Merge branch 'caa-flag-fix' of github.com:letsencrypt/boulder into caa-flag-fix
This commit is contained in:
commit
82c7d0b7a5
|
|
@ -11,6 +11,7 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
|
|
@ -410,7 +411,12 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
|
|||
if err != nil {
|
||||
err = core.InternalServerError(err.Error())
|
||||
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
|
||||
ca.log.Audit(fmt.Sprintf("Failed RPC to store at SA, orphaning certificate: pem=[%s] err=[%v]", certPEM, err))
|
||||
ca.log.Audit(fmt.Sprintf(
|
||||
"Failed RPC to store at SA, orphaning certificate: b64der=[%s] err=[%v], regID=[%d]",
|
||||
base64.StdEncoding.EncodeToString(certDER),
|
||||
err,
|
||||
regID,
|
||||
))
|
||||
return emptyCert, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,11 +113,11 @@ type Config struct {
|
|||
Mailer struct {
|
||||
ServiceConfig
|
||||
DBConfig
|
||||
PasswordConfig
|
||||
|
||||
Server string
|
||||
Port string
|
||||
Username string
|
||||
Password string
|
||||
From string
|
||||
Subject string
|
||||
|
||||
|
|
@ -217,6 +217,26 @@ func (config *Config) KeyPolicy() core.KeyPolicy {
|
|||
}
|
||||
}
|
||||
|
||||
// PasswordConfig either contains a password or the path to a file
|
||||
// containing a password
|
||||
type PasswordConfig struct {
|
||||
Password string
|
||||
PasswordFile string
|
||||
}
|
||||
|
||||
// Pass returns a password, either directly from the configuration
|
||||
// struct or by reading from a specified file
|
||||
func (pc *PasswordConfig) Pass() (string, error) {
|
||||
if pc.PasswordFile != "" {
|
||||
contents, err := ioutil.ReadFile(pc.PasswordFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimRight(string(contents), "\n"), nil
|
||||
}
|
||||
return pc.Password, nil
|
||||
}
|
||||
|
||||
// ServiceConfig contains config items that are common to all our services, to
|
||||
// be embedded in other config structs.
|
||||
type ServiceConfig struct {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
func TestPasswordConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
pc PasswordConfig
|
||||
expected string
|
||||
}{
|
||||
{pc: PasswordConfig{}, expected: ""},
|
||||
{pc: PasswordConfig{Password: "config"}, expected: "config"},
|
||||
{pc: PasswordConfig{Password: "config", PasswordFile: "testdata/test_secret"}, expected: "secret"},
|
||||
{pc: PasswordConfig{PasswordFile: "testdata/test_secret"}, expected: "secret"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
password, err := tc.pc.Pass()
|
||||
test.AssertNotError(t, err, "Failed to retrieve password")
|
||||
test.AssertEquals(t, password, tc.expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -252,7 +252,9 @@ func main() {
|
|||
_, err = netmail.ParseAddress(c.Mailer.From)
|
||||
cmd.FailOnError(err, fmt.Sprintf("Could not parse from address: %s", c.Mailer.From))
|
||||
|
||||
mailClient := mail.New(c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, c.Mailer.Password, c.Mailer.From)
|
||||
smtpPassword, err := c.Mailer.PasswordConfig.Pass()
|
||||
cmd.FailOnError(err, "Failed to load SMTP password")
|
||||
mailClient := mail.New(c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, smtpPassword, c.Mailer.From)
|
||||
err = mailClient.Connect()
|
||||
cmd.FailOnError(err, "Couldn't connect to mail server.")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"syslog": {
|
||||
"network": "",
|
||||
"server": "",
|
||||
"stdoutlevel": 7
|
||||
},
|
||||
|
||||
"statsd": {
|
||||
"server": "localhost:8125",
|
||||
"prefix": "Boulder"
|
||||
},
|
||||
|
||||
"amqp": {
|
||||
"serverURLFile": "test/secrets/amqp_url",
|
||||
"insecure": true,
|
||||
"SA": {
|
||||
"server": "SA.server",
|
||||
"rpcTimeout": "15s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/rpc"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
AMQP cmd.AMQPConfig
|
||||
Statsd cmd.StatsdConfig
|
||||
Syslog cmd.SyslogConfig
|
||||
}
|
||||
|
||||
var (
|
||||
b64derOrphan = regexp.MustCompile(`b64der=\[([a-zA-Z0-9+/]+)\]`)
|
||||
regOrphan = regexp.MustCompile(`regID=\[(\d+)\]`)
|
||||
)
|
||||
|
||||
func checkDER(sai core.StorageAuthority, der []byte) error {
|
||||
cert, err := x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse DER: %s", err)
|
||||
}
|
||||
_, err = sai.GetCertificate(core.SerialToString(cert.SerialNumber))
|
||||
if err == nil {
|
||||
return fmt.Errorf("Existing certificate found with serial %s", core.SerialToString(cert.SerialNumber))
|
||||
}
|
||||
if _, ok := err.(core.NotFoundError); ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Existing certificate lookup failed: %s", err)
|
||||
}
|
||||
|
||||
func parseLogLine(sa core.StorageAuthority, logger *blog.AuditLogger, line string) (found bool, added bool) {
|
||||
if !strings.Contains(line, "b64der=") {
|
||||
return false, false
|
||||
}
|
||||
derStr := b64derOrphan.FindStringSubmatch(line)
|
||||
if len(derStr) <= 1 {
|
||||
logger.Err(fmt.Sprintf("b64der variable is empty, [%s]", line))
|
||||
return true, false
|
||||
}
|
||||
der, err := base64.StdEncoding.DecodeString(derStr[1])
|
||||
if err != nil {
|
||||
logger.Err(fmt.Sprintf("Couldn't decode b64: %s, [%s]", err, line))
|
||||
return true, false
|
||||
}
|
||||
err = checkDER(sa, der)
|
||||
if err != nil {
|
||||
logger.Err(fmt.Sprintf("%s, [%s]", err, line))
|
||||
return true, false
|
||||
}
|
||||
// extract the regID
|
||||
regStr := regOrphan.FindStringSubmatch(line)
|
||||
if len(regStr) <= 1 {
|
||||
logger.Err(fmt.Sprintf("regID variable is empty, [%s]", line))
|
||||
return true, false
|
||||
}
|
||||
regID, err := strconv.Atoi(regStr[1])
|
||||
if err != nil {
|
||||
logger.Err(fmt.Sprintf("Couldn't parse regID: %s, [%s]", err, line))
|
||||
return true, false
|
||||
}
|
||||
_, err = sa.AddCertificate(der, int64(regID))
|
||||
if err != nil {
|
||||
logger.Err(fmt.Sprintf("Failed to store certificate: %s, [%s]", err, line))
|
||||
return true, false
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
|
||||
func setup(c *cli.Context) (statsd.Statter, *blog.AuditLogger, *rpc.StorageAuthorityClient) {
|
||||
configJSON, err := ioutil.ReadFile(c.GlobalString("config"))
|
||||
cmd.FailOnError(err, "Failed to read config file")
|
||||
var conf config
|
||||
err = json.Unmarshal(configJSON, &conf)
|
||||
cmd.FailOnError(err, "Failed to parse config file")
|
||||
stats, logger := cmd.StatsAndLogging(conf.Statsd, conf.Syslog)
|
||||
sa, err := rpc.NewStorageAuthorityClient("orphan-finder", &conf.AMQP, stats)
|
||||
cmd.FailOnError(err, "Failed to create SA client")
|
||||
return stats, logger, sa
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "orphan-finder"
|
||||
app.Usage = "Reads orphaned certificates from a boulder-ca log or a der file and add them to the database"
|
||||
app.Version = cmd.Version()
|
||||
app.Author = "Boulder contributors"
|
||||
app.Email = "ca-dev@letsencrypt.org"
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "config.json",
|
||||
EnvVar: "BOULDER_CONFIG",
|
||||
Usage: "Path to Boulder JSON configuration file",
|
||||
},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "parse-ca-log",
|
||||
Usage: "Parses boulder-ca logs to add multiple orphaned certificates",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: "Path to boulder-ca log file to parse",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
stats, logger, sa := setup(c)
|
||||
logPath := c.String("log-file")
|
||||
if logPath == "" {
|
||||
fmt.Println("log file path must be provided")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logData, err := ioutil.ReadFile(logPath)
|
||||
cmd.FailOnError(err, "Failed to read log file")
|
||||
|
||||
orphansFound := int64(0)
|
||||
orphansAdded := int64(0)
|
||||
for _, line := range strings.Split(string(logData), "\n") {
|
||||
found, added := parseLogLine(sa, logger, line)
|
||||
if found {
|
||||
orphansFound++
|
||||
if added {
|
||||
orphansAdded++
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Info(fmt.Sprintf("Found %d orphans and added %d to the database\n", orphansFound, orphansAdded))
|
||||
stats.Inc("orphaned-certificates.found", orphansFound, 1.0)
|
||||
stats.Inc("orphaned-certificates.added", orphansAdded, 1.0)
|
||||
stats.Inc("orphaned-certificates.adding-failed", orphansFound-orphansAdded, 1.0)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "parse-der",
|
||||
Usage: "Parses a single orphaned DER certificate file and adds it to the database",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "der-file",
|
||||
Usage: "Path to DER certificate file",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "regID",
|
||||
Usage: "Registration ID of user who requested the certificate",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
_, _, sa := setup(c)
|
||||
derPath := c.String("der-file")
|
||||
if derPath == "" {
|
||||
fmt.Println("der file path must be provided")
|
||||
os.Exit(1)
|
||||
}
|
||||
regID := c.Int("regID")
|
||||
if regID == 0 {
|
||||
fmt.Println("--regID must be non-zero")
|
||||
os.Exit(1)
|
||||
}
|
||||
der, err := ioutil.ReadFile(derPath)
|
||||
cmd.FailOnError(err, "Failed to read DER file")
|
||||
err = checkDER(sa, der)
|
||||
cmd.FailOnError(err, "Pre-AddCertificate checks failed")
|
||||
_, err = sa.AddCertificate(der, int64(regID))
|
||||
cmd.FailOnError(err, "Failed to add certificate to database")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/mocks"
|
||||
"github.com/letsencrypt/boulder/sa"
|
||||
"github.com/letsencrypt/boulder/sa/satest"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/letsencrypt/boulder/test/vars"
|
||||
)
|
||||
|
||||
var log = mocks.UseMockLog()
|
||||
|
||||
func TestParseLine(t *testing.T) {
|
||||
dbMap, err := sa.NewDbMap(vars.DBConnSA)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create dbMap: %s", err)
|
||||
}
|
||||
fc := clock.NewFake()
|
||||
fc.Set(time.Date(2015, 3, 4, 5, 0, 0, 0, time.UTC))
|
||||
sa, err := sa.NewSQLStorageAuthority(dbMap, fc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create SA: %s", err)
|
||||
}
|
||||
defer test.ResetSATestDatabase(t)()
|
||||
logger := blog.GetAuditLogger()
|
||||
|
||||
found, added := parseLogLine(sa, logger, "")
|
||||
test.AssertEquals(t, found, false)
|
||||
test.AssertEquals(t, added, false)
|
||||
|
||||
found, added = parseLogLine(sa, logger, "0000-00-00T00:00:00+00:00 hostname boulder-ca[pid]: [AUDIT] Failed RPC to store at SA, orphaning certificate: b64der=[] err=[AMQP-RPC timeout], regID=[1337]")
|
||||
test.AssertEquals(t, found, true)
|
||||
test.AssertEquals(t, added, false)
|
||||
|
||||
found, added = parseLogLine(sa, logger, "0000-00-00T00:00:00+00:00 hostname boulder-ca[pid]: [AUDIT] Failed RPC to store at SA, orphaning certificate: b64der=[deadbeef] err=[AMQP-RPC timeout], regID=[]")
|
||||
test.AssertEquals(t, found, true)
|
||||
test.AssertEquals(t, added, false)
|
||||
|
||||
reg := satest.CreateWorkingRegistration(t, sa)
|
||||
|
||||
found, added = parseLogLine(sa, logger, fmt.Sprintf("0000-00-00T00:00:00+00:00 hostname boulder-ca[pid]: [AUDIT] Failed RPC to store at SA, orphaning certificate: b64der=[MIIEWzCCA0OgAwIBAgITAP+gFgYw1hiy61wFEIJLFCdIVjANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNTEwMDMwNTIxMDBaFw0xNjAxMDEwNTIxMDBaMBgxFjAUBgNVBAMTDWV4YW1wbGUuY28uYm4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCeo/HSH63lWW42pqdwlalHWOS3JGa3REraT3xM9v3psdRwuTtlwf3YlpF/JIzK5JtXyA3CHGSwEGmUMhMNBZ0tg5I0booXnHyUeDVUnGSnpWgMUY+vCly+pI5oT8pjBHdcj6kjnDTx1cstBjsJi9HBcYPHUh78iEZBsvC0FAKsh8cHaEjUNHzvWd1anBdK0lRn25M8le9IxXi6di9SeyFmahmPteH+LYKZtNzrF5HpatB14+ywV8d212T62PCCnUPDLd+YWjo2+t5pZs7IlGhyGh7EerOOrI2kUUBg3tUdKDp4e3xplxvaAfSfdrqkGx+bQ0iqQnng+lVkXWYWRB8NAgMBAAGjggGVMIIBkTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDadDBAEUrnrP/566FLp6DmjrlrbMB8GA1UdIwQYMBaAFPt4TxL5YBWDLJ8XfzQZsy426kGJMGoGCCsGAQUFBwEBBF4wXDAmBggrBgEFBQcwAYYaaHR0cDovL2xvY2FsaG9zdDo0MDAyL29jc3AwMgYIKwYBBQUHMAKGJmh0dHA6Ly9sb2NhbGhvc3Q6NDAwMC9hY21lL2lzc3Vlci1jZXJ0MBgGA1UdEQQRMA+CDWV4YW1wbGUuY28uYm4wJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL2V4YW1wbGUuY29tL2NybDBjBgNVHSAEXDBaMAoGBmeBDAECATAAMEwGAyoDBDBFMCIGCCsGAQUFBwIBFhZodHRwOi8vZXhhbXBsZS5jb20vY3BzMB8GCCsGAQUFBwICMBMMEURvIFdoYXQgVGhvdSBXaWx0MA0GCSqGSIb3DQEBCwUAA4IBAQC7tLmUlxyvouVuIljbRtiL+zYdi/zXVSHAMXTkceqp8/8ucZBZu1fMBkB5SW2FUFd8EnuqhKGOeS3dNr9Pe4dLbUDR0UKIwV045Na+Jet4BbHDdWs3NXAutFhdGIa8ivLBQIbTzlBuVRhJE8g6qqjf5hYL0DXkLNptl2l+0+4xJMm/liCp/mYCGRwbdGUzwdSjACO76QLLSqZhkBF37ZJOuDbJTMBi3QzkOcTs6e4d/gSZpCy7yy6nJDxZ9N9P3XBYIpus+aZAYy29d2shYzE3st8cQfB2Wmb0SHd67sftTAzeudiiNW/4E4IKKH4R1S794apUO07y7pkqep1cz32k] err=[AMQP-RPC timeout], regID=[%d]", reg.ID))
|
||||
test.AssertEquals(t, found, true)
|
||||
test.AssertEquals(t, added, true)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
secret
|
||||
|
|
@ -43,7 +43,7 @@ func NewNonceService() (*NonceService, error) {
|
|||
if err != nil {
|
||||
panic("Failure in NewCipher: " + err.Error())
|
||||
}
|
||||
gcm, _ := cipher.NewGCM(c)
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
panic("Failure in NewGCM: " + err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ func (ssa *SQLStorageAuthority) GetCertificate(serial string) (core.Certificate,
|
|||
}
|
||||
if certObj == nil {
|
||||
ssa.log.Debug(fmt.Sprintf("Nil cert for %s", serial))
|
||||
return core.Certificate{}, fmt.Errorf("Certificate does not exist for %s", serial)
|
||||
return core.Certificate{}, core.NotFoundError(fmt.Sprintf("No certificate found for %s", serial))
|
||||
}
|
||||
|
||||
certPtr, ok := certObj.(*core.Certificate)
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@
|
|||
"port": "25",
|
||||
"username": "cert-master@example.com",
|
||||
"from": "Expiry bot <test@example.com>",
|
||||
"password": "password",
|
||||
"passwordFile": "test/secrets/smtp_password",
|
||||
"dbConnectFile": "test/secrets/mailer_dburl",
|
||||
"messageLimit": 0,
|
||||
"nagTimes": ["24h", "72h", "168h", "336h"],
|
||||
|
|
|
|||
|
|
@ -70,7 +70,11 @@ func deleteEverythingInAllTables(db CleanUpDB) error {
|
|||
// rejecting the DELETE for not having a WHERE clause.
|
||||
_, err := db.Exec("delete from `" + tn + "` where 1 = 1")
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
_, err = db.Exec("alter table `" + tn + "` AUTO_INCREMENT = 1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to reset autoincrement on table %#v: %s", tn, err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
password
|
||||
Loading…
Reference in New Issue