Merge branch 'master' into cert-limit

This commit is contained in:
Jeff Hodges 2015-10-01 11:11:55 -07:00
commit c24ced260e
28 changed files with 101 additions and 316 deletions

View File

@ -1,9 +0,0 @@
development:
driver: mysql
open: boulder@tcp(localhost:3306)/boulder_ca_development
test:
driver: mysql
open: boulder@tcp(localhost:3306)/boulder_ca_test
integration:
driver: mysql
open: boulder@tcp(localhost:3306)/boulder_ca_integration

View File

@ -1,23 +0,0 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
CREATE TABLE `serialNumber` (
`id` int(11) DEFAULT NULL,
`number` int(11) DEFAULT NULL,
`lastUpdated` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `serialNumber`
(`id`,
`number`,
`lastUpdated`)
VALUES (1,
1,
now()
);
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE `serialNumber`

View File

@ -1,30 +0,0 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
DROP TABLE `serialNumber`;
CREATE TABLE `serialNumber` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE `serialNumber`;
CREATE TABLE `serialNumber` (
`id` int(11) DEFAULT NULL,
`number` int(11) DEFAULT NULL,
`lastUpdated` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `serialNumber`
(`id`,
`number`,
`lastUpdated`)
VALUES (1,
1,
now()
);

View File

@ -1,60 +0,0 @@
// Copyright 2015 ISRG. All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package ca
import (
"time"
blog "github.com/letsencrypt/boulder/log"
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
)
// CertificateAuthorityDatabaseImpl represents a database used by the CA; it
// enforces transaction semantics, and is effectively single-threaded.
type CertificateAuthorityDatabaseImpl struct {
log *blog.AuditLogger
dbMap *gorp.DbMap
}
// SerialNumber defines the database table used to hold the serial number.
type SerialNumber struct {
ID int `db:"id"`
Number int64 `db:"number"`
LastUpdated time.Time `db:"lastUpdated"`
}
// NewCertificateAuthorityDatabaseImpl constructs a Database for the
// Certificate Authority.
func NewCertificateAuthorityDatabaseImpl(dbMap *gorp.DbMap) (cadb *CertificateAuthorityDatabaseImpl, err error) {
logger := blog.GetAuditLogger()
dbMap.AddTableWithName(SerialNumber{}, "serialNumber").SetKeys(true, "ID")
cadb = &CertificateAuthorityDatabaseImpl{
dbMap: dbMap,
log: logger,
}
return cadb, nil
}
// Begin starts a transaction at the GORP wrapper.
func (cadb *CertificateAuthorityDatabaseImpl) Begin() (*gorp.Transaction, error) {
return cadb.dbMap.Begin()
}
// IncrementAndGetSerial returns the next-available serial number, incrementing
// it in the database before returning. There must be an active transaction to
// call this method. Callers should Begin the transaction, call this method,
// perform any other work, and Commit at the end once the certificate is issued.
func (cadb *CertificateAuthorityDatabaseImpl) IncrementAndGetSerial(tx *gorp.Transaction) (int64, error) {
r, err := tx.Exec("REPLACE INTO serialNumber (stub) VALUES ('a');")
if err != nil {
return -1, err
}
return r.LastInsertId()
}

View File

@ -1,61 +0,0 @@
// Copyright 2015 ISRG. All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package ca
import (
"testing"
"github.com/letsencrypt/boulder/sa"
"github.com/letsencrypt/boulder/test"
)
func TestGetSetSequenceOutsideTx(t *testing.T) {
cadb, cleanUp := caDBImpl(t)
defer cleanUp()
tx, err := cadb.Begin()
test.AssertNotError(t, err, "Could not begin")
tx.Commit()
_, err = cadb.IncrementAndGetSerial(tx)
test.AssertError(t, err, "Not permitted")
tx2, err := cadb.Begin()
test.AssertNotError(t, err, "Could not begin")
tx2.Rollback()
_, err = cadb.IncrementAndGetSerial(tx2)
test.AssertError(t, err, "Not permitted")
}
func TestGetSetSequenceNumber(t *testing.T) {
cadb, cleanUp := caDBImpl(t)
defer cleanUp()
tx, err := cadb.Begin()
test.AssertNotError(t, err, "Could not begin")
num, err := cadb.IncrementAndGetSerial(tx)
test.AssertNotError(t, err, "Could not get number")
num2, err := cadb.IncrementAndGetSerial(tx)
test.AssertNotError(t, err, "Could not get number")
test.Assert(t, num+1 == num2, "Numbers should be incrementing")
err = tx.Commit()
test.AssertNotError(t, err, "Could not commit")
}
func caDBImpl(t *testing.T) (*CertificateAuthorityDatabaseImpl, func()) {
dbMap, err := sa.NewDbMap(caDBConnStr)
if err != nil {
t.Fatalf("Could not construct dbMap: %s", err)
}
cadb, err := NewCertificateAuthorityDatabaseImpl(dbMap)
if err != nil {
t.Fatalf("Could not construct CA DB: %s", err)
}
cleanUp := test.ResetTestDatabase(t, dbMap.Db)
return cadb, cleanUp
}

View File

@ -54,7 +54,6 @@ type CertificateAuthorityImpl struct {
OCSPSigner ocsp.Signer
SA core.StorageAuthority
PA core.PolicyAuthority
DB core.CertificateAuthorityDatabase
Publisher core.Publisher
Clk clock.Clock // TODO(jmhodges): should be private, like log
log *blog.AuditLogger
@ -70,7 +69,7 @@ type CertificateAuthorityImpl struct {
// using CFSSL's authenticated signature scheme. A CA created in this way
// issues for a single profile on the remote signer, which is indicated
// by name in this constructor.
func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config cmd.CAConfig, clk clock.Clock, issuerCert string) (*CertificateAuthorityImpl, error) {
func NewCertificateAuthorityImpl(config cmd.CAConfig, clk clock.Clock, issuerCert string) (*CertificateAuthorityImpl, error) {
var ca *CertificateAuthorityImpl
var err error
logger := blog.GetAuditLogger()
@ -127,7 +126,6 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
Signer: signer,
OCSPSigner: ocspSigner,
profile: config.Profile,
DB: cadb,
Prefix: config.SerialPrefix,
Clk: clk,
log: logger,
@ -306,15 +304,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
Bytes: csr.Raw,
}))
// Get the next serial number
tx, err := ca.DB.Begin()
if err != nil {
err = core.InternalServerError(err.Error())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.AuditErr(err)
return emptyCert, err
}
// Hack: CFSSL always sticks a 64-bit random number at the end of the
// serialSeq we provide, but we want 136 bits of random number, plus an 8-bit
// instance id prefix. For now, we generate the extra 72 bits of randomness
@ -328,7 +317,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
err = core.InternalServerError(err.Error())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("Serial randomness failed, err=[%v]", err))
tx.Rollback()
return emptyCert, err
}
serialHex := hex.EncodeToString([]byte{byte(ca.Prefix)}) + hex.EncodeToString(randSlice)
@ -349,7 +337,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
err = core.InternalServerError(err.Error())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("Signer failed, rolling back: serial=[%s] err=[%v]", serialHex, err))
tx.Rollback()
return emptyCert, err
}
@ -357,7 +344,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
err = core.InternalServerError("No certificate returned by server")
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("PEM empty from Signer, rolling back: serial=[%s] err=[%v]", serialHex, err))
tx.Rollback()
return emptyCert, err
}
@ -366,7 +352,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
err = core.InternalServerError("Invalid certificate value returned")
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("PEM decode error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err))
tx.Rollback()
return emptyCert, err
}
certDER := block.Bytes
@ -380,7 +365,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
err = core.InternalServerError(err.Error())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("Uncaught error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err))
tx.Rollback()
return emptyCert, err
}
@ -390,14 +374,6 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
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))
tx.Rollback()
return emptyCert, err
}
if err = tx.Commit(); err != nil {
err = core.InternalServerError(err.Error())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
ca.log.Audit(fmt.Sprintf("Failed to commit, orphaning certificate: pem=[%s] err=[%v]", certPEM, err))
return emptyCert, err
}

View File

@ -91,7 +91,6 @@ const caCertFile = "../test/test-ca.pem"
const (
paDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_policy_test"
caDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_ca_test"
saDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_sa_test"
)
@ -104,7 +103,6 @@ func mustRead(path string) []byte {
}
type testCtx struct {
caDB core.CertificateAuthorityDatabase
sa core.StorageAuthority
caConfig cmd.CAConfig
reg core.Registration
@ -126,7 +124,6 @@ func setup(t *testing.T) *testCtx {
t.Fatalf("Failed to create SA: %s", err)
}
saDBCleanUp := test.ResetTestDatabase(t, dbMap.Db)
cadb, caDBCleanUp := caDBImpl(t)
paDbMap, err := sa.NewDbMap(paDBConnStr)
test.AssertNotError(t, err, "Could not construct dbMap")
@ -136,7 +133,6 @@ func setup(t *testing.T) *testCtx {
cleanUp := func() {
saDBCleanUp()
caDBCleanUp()
paDBCleanUp()
}
@ -188,7 +184,7 @@ func setup(t *testing.T) *testCtx {
},
},
}
return &testCtx{cadb, ssa, caConfig, reg, pa, fc, cleanUp}
return &testCtx{ssa, caConfig, reg, pa, fc, cleanUp}
}
func TestFailNoSerial(t *testing.T) {
@ -196,14 +192,14 @@ func TestFailNoSerial(t *testing.T) {
defer ctx.cleanUp()
ctx.caConfig.SerialPrefix = 0
_, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
_, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertError(t, err, "CA should have failed with no SerialPrefix")
}
func TestRevoke(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -244,7 +240,7 @@ func TestRevoke(t *testing.T) {
func TestIssueCertificate(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
@ -321,7 +317,7 @@ func TestIssueCertificate(t *testing.T) {
func TestRejectNoName(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
@ -338,7 +334,7 @@ func TestRejectNoName(t *testing.T) {
func TestRejectTooManyNames(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
@ -355,7 +351,7 @@ func TestRejectTooManyNames(t *testing.T) {
func TestDeduplication(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
@ -379,7 +375,7 @@ func TestDeduplication(t *testing.T) {
func TestRejectValidityTooLong(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
@ -397,7 +393,7 @@ func TestRejectValidityTooLong(t *testing.T) {
func TestShortKey(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -413,7 +409,7 @@ func TestShortKey(t *testing.T) {
func TestRejectBadAlgorithm(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, caCertFile)
ca.Publisher = &mocks.MockPublisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa

View File

@ -118,8 +118,8 @@ func main() {
cmd.FailOnError(err, "Could not connect to statsd")
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
blog.SetAuditLogger(auditlogger)
@ -131,8 +131,6 @@ func main() {
go cmd.ProfileCmd("AM", stats)
auditlogger.Info(app.VersionString())
startMonitor(ch, auditlogger, stats)
}

View File

@ -25,6 +25,7 @@ func main() {
// Set up logging
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -33,18 +34,12 @@ func main() {
go cmd.DebugServer(c.CA.DebugAddr)
dbMap, err := sa.NewDbMap(c.CA.DBConnect)
cmd.FailOnError(err, "Couldn't connect to CA database")
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(dbMap)
cmd.FailOnError(err, "Failed to create CA database")
paDbMap, err := sa.NewDbMap(c.PA.DBConnect)
cmd.FailOnError(err, "Couldn't connect to policy database")
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, c.PA.EnforcePolicyWhitelist)
cmd.FailOnError(err, "Couldn't create PA")
cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, clock.Default(), c.Common.IssuerCert)
cai, err := ca.NewCertificateAuthorityImpl(c.CA, clock.Default(), c.Common.IssuerCert)
cmd.FailOnError(err, "Failed to create CA impl")
cai.PA = pa
@ -71,8 +66,6 @@ func main() {
cmd.FailOnError(err, "Unable to create CA RPC server")
rpc.NewCertificateAuthorityServer(cas, cai)
auditlogger.Info(app.VersionString())
err = cas.Start(c)
cmd.FailOnError(err, "Unable to run CA RPC server")
}

View File

@ -23,6 +23,7 @@ func main() {
// Set up logging
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -49,8 +50,6 @@ func main() {
cmd.FailOnError(err, "Unable to create Publisher RPC server")
rpc.NewPublisherServer(pubs, &pubi)
auditlogger.Info(app.VersionString())
err = pubs.Start(c)
cmd.FailOnError(err, "Unable to run Publisher RPC server")
}

View File

@ -29,6 +29,7 @@ func main() {
// Set up logging
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -85,8 +86,6 @@ func main() {
cmd.FailOnError(err, "Unable to create RA RPC server")
rpc.NewRegistrationAuthorityServer(ras, &rai)
auditlogger.Info(app.VersionString())
err = ras.Start(c)
cmd.FailOnError(err, "Unable to run RA RPC server")
}

View File

@ -23,6 +23,7 @@ func main() {
// Set up logging
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -46,8 +47,6 @@ func main() {
cmd.FailOnError(err, "Unable to create SA RPC server")
rpc.NewStorageAuthorityServer(sas, sai)
auditlogger.Info(app.VersionString())
err = sas.Start(c)
cmd.FailOnError(err, "Unable to run SA RPC server")
}

View File

@ -27,6 +27,7 @@ func main() {
// Set up logging
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -75,8 +76,6 @@ func main() {
cmd.FailOnError(err, "Unable to create VA RPC server")
rpc.NewValidationAuthorityServer(vas, vai)
auditlogger.Info(app.VersionString())
err = vas.Start(c)
cmd.FailOnError(err, "Unable to run VA RPC server")
}

View File

@ -66,6 +66,7 @@ func main() {
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -120,8 +121,6 @@ func main() {
h, err := wfe.Handler()
cmd.FailOnError(err, "Problem setting up HTTP handlers")
auditlogger.Info(app.VersionString())
httpMonitor := metrics.NewHTTPMonitor(stats, h, "WFE")
auditlogger.Info(fmt.Sprintf("Server running, listening on %s...\n", c.WFE.ListenAddress))

View File

@ -239,9 +239,9 @@ func main() {
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
blog.SetAuditLogger(auditlogger)
auditlogger.Info(app.VersionString())
saDbMap, err := sa.NewDbMap(c.CertChecker.DBConnect)
cmd.FailOnError(err, "Could not connect to database")

View File

@ -230,14 +230,13 @@ func main() {
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
blog.SetAuditLogger(auditlogger)
auditlogger.Info(app.VersionString())
go cmd.DebugServer(c.Mailer.DebugAddr)
// Configure DB

View File

@ -131,6 +131,7 @@ func main() {
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -141,8 +142,6 @@ func main() {
go cmd.ProfileCmd("OCSP", stats)
auditlogger.Info(app.VersionString())
config := c.OCSPResponder
var source cfocsp.Source
url, err := url.Parse(config.Source)

View File

@ -205,6 +205,7 @@ func main() {
auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
cmd.FailOnError(err, "Could not connect to Syslog")
auditlogger.Info(app.VersionString())
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
defer auditlogger.AuditPanic()
@ -229,8 +230,6 @@ func main() {
}
}()
auditlogger.Info(app.VersionString())
updater := &OCSPUpdater{
cac: cac,
dbMap: dbMap,

View File

@ -44,11 +44,11 @@ func (logDesc *LogDescription) UnmarshalJSON(data []byte) error {
// Load Key
pkBytes, err := base64.StdEncoding.DecodeString(rawLogDesc.PublicKey)
if err != nil {
return fmt.Errorf("")
return fmt.Errorf("Failed to decode base64 log public key")
}
pk, err := x509.ParsePKIXPublicKey(pkBytes)
if err != nil {
return fmt.Errorf("")
return fmt.Errorf("Failed to parse log public key")
}
ecdsaKey, ok := pk.(*ecdsa.PublicKey)
if !ok {

View File

@ -129,7 +129,6 @@ var (
const (
paDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_policy_test"
caDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_ca_test"
saDBConnStr = "mysql+tcp://boulder@localhost:3306/boulder_sa_test"
)
@ -188,13 +187,11 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
policyDBCleanUp := test.ResetTestDatabase(t, paDbMap.Db)
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, false)
test.AssertNotError(t, err, "Couldn't create PA")
cadb, caDBCleanUp := caDBImpl(t)
ca := ca.CertificateAuthorityImpl{
Signer: signer,
OCSPSigner: ocspSigner,
SA: ssa,
PA: pa,
DB: cadb,
Publisher: &mocks.MockPublisher{},
ValidityPeriod: time.Hour * 2190,
NotAfter: time.Now().Add(time.Hour * 8761),
@ -202,7 +199,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
}
cleanUp := func() {
saDBCleanUp()
caDBCleanUp()
policyDBCleanUp()
}
@ -235,27 +231,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
return va, ssa, &ra, fc, cleanUp
}
// This is an unfortunate bit of tech debt that is being taken on in
// order to get the more important change of using MySQL/MariaDB in
// all of our tests working without SQLite. We already had issues with
// the RA here getting a real CertificateAuthority instead of a
// CertificateAuthorityClient, so this is only marginally worse.
// TODO(Issue #628): use a CAClient fake instead of a CAImpl instance
func caDBImpl(t *testing.T) (core.CertificateAuthorityDatabase, func()) {
dbMap, err := sa.NewDbMap(caDBConnStr)
if err != nil {
t.Fatalf("Could not construct dbMap: %s", err)
}
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(dbMap)
if err != nil {
t.Fatalf("Could not construct CA DB: %s", err)
}
cleanUp := test.ResetTestDatabase(t, dbMap.Db)
return cadb, cleanUp
}
func assertAuthzEqual(t *testing.T, a1, a2 core.Authorization) {
test.Assert(t, a1.ID == a2.ID, "ret != DB: ID")
test.Assert(t, a1.Identifier == a2.Identifier, "ret != DB: Identifier")

View File

@ -119,7 +119,7 @@ func SetSQLDebug(dbMap *gorp.DbMap, state bool) {
// SQLLogger adapts the AuditLogger to a format GORP can use.
type SQLLogger struct {
log *blog.AuditLogger
log blog.SyslogWriter
}
// Printf adapts the AuditLogger to GORP's interface

View File

@ -38,6 +38,7 @@ func initSA(t *testing.T) (*SQLStorageAuthority, clock.FakeClock, func()) {
if err != nil {
t.Fatalf("Failed to create dbMap: %s", err)
}
dbMap.TraceOn("SQL: ", &SQLLogger{log})
fc := clock.NewFake()
fc.Add(1 * time.Hour)

View File

@ -7,12 +7,13 @@ import socket
import subprocess
import sys
import tempfile
import urllib2
import startservers
class ExitStatus:
OK, PythonFailure, NodeFailure, Error, OCSPFailure = range(5)
OK, PythonFailure, NodeFailure, Error, OCSPFailure, CTFailure = range(6)
class ProcInfo:
@ -62,6 +63,13 @@ def verify_ocsp_revoked(certFile, url):
die(ExitStatus.OCSPFailure)
pass
def verify_ct_submission(expectedSubmissions, url):
resp = urllib2.urlopen(url)
submissionStr = resp.read()
if int(submissionStr) != expectedSubmissions:
print "Expected %d submissions, found %d" % (expectedSubmissions, int(submissionStr))
die(ExitStatus.CTFailure)
def run_node_test():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
@ -91,6 +99,7 @@ def run_node_test():
# Also verify that the static OCSP responder, which answers with a
# pre-signed, long-lived response for the CA cert, also works.
verify_ocsp_good("../test-ca.der", issuer_ocsp_url)
verify_ct_submission(1, "http://localhost:4500/submissions")
if subprocess.Popen('''
node revoke.js %s %s http://localhost:4000/acme/revoke-cert

View File

@ -54,7 +54,6 @@
"ca": {
"serialPrefix": 255,
"profile": "ee",
"dbConnect": "mysql+tcp://boulder@localhost:3306/boulder_ca_integration",
"debugAddr": "localhost:8001",
"Key": {
"File": "test/test-ca.key"

View File

@ -10,47 +10,70 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"sync/atomic"
)
type ctSubmissionRequest struct {
Chain []string `json:"chain"`
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" || r.URL.Path != "/ct/v1/add-chain" {
type integrationSrv struct {
submissions int64
}
func (is *integrationSrv) handler(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/ct/v1/add-chain":
if r.Method != "POST" {
http.NotFound(w, r)
return
}
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
var addChainReq ctSubmissionRequest
err = json.Unmarshal(bodyBytes, &addChainReq)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
w.WriteHeader(http.StatusOK)
// id is a sha256 of a random EC key. Generate your own with:
// openssl ecparam -name prime256v1 -genkey -outform der | openssl sha256 -binary | base64
w.Write([]byte(`{
"sct_version": 0,
"id": "8fjM8cvLPOhzCFwI62IYJhjkOcvWFLx1dMJbs0uhxJU=",
"timestamp": 1442400000,
"extensions": "",
"signature": "BAMARzBFAiBB5wKED8KqKhADT37n0y28fZIPiGbCfZRVKq0wNo0hrwIhAOIa2tPBF/rB1y30Y/ROh4LBmJ0mItAbTWy8XZKh7Wcp"
}`))
atomic.AddInt64(&is.submissions, 1)
case "/submissions":
if r.Method != "GET" {
http.NotFound(w, r)
return
}
submissions := atomic.LoadInt64(&is.submissions)
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("%d", submissions)))
default:
http.NotFound(w, r)
return
}
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
var addChainReq ctSubmissionRequest
err = json.Unmarshal(bodyBytes, &addChainReq)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
w.WriteHeader(http.StatusOK)
// id is a sha256 of a random EC key. Generate your own with:
// openssl ecparam -name prime256v1 -genkey -outform der | openssl sha256 -binary | base64
w.Write([]byte(`{
"sct_version": 0,
"id": "8fjM8cvLPOhzCFwI62IYJhjkOcvWFLx1dMJbs0uhxJU=",
"timestamp": 1442400000,
"extensions": "",
"signature": "BAMARzBFAiBB5wKED8KqKhADT37n0y28fZIPiGbCfZRVKq0wNo0hrwIhAOIa2tPBF/rB1y30Y/ROh4LBmJ0mItAbTWy8XZKh7Wcp"
}`))
}
func main() {
is := integrationSrv{}
s := &http.Server{
Addr: ":4500",
Handler: http.HandlerFunc(handler),
Addr: "localhost:4500",
Handler: http.HandlerFunc(is.handler),
}
log.Fatal(s.ListenAndServe())
}

View File

@ -6,8 +6,7 @@ function die() {
exit 1
}
SERVICES="ca
sa
SERVICES="sa
policy"
DBENVS="development
test

View File

@ -157,8 +157,9 @@ func (mrw BodylessResponseWriter) Write(buf []byte) (int, error) {
// * Respond http.StatusMethodNotAllowed for HTTP methods other than
// those listed.
//
// * Never send a body in response to a HEAD request. (Anything
// written by the handler will be discarded if the method is HEAD.)
// * Never send a body in response to a HEAD request. Anything
// written by the handler will be discarded if the method is HEAD. Also, all
// handlers that accept GET automatically accept HEAD.
func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h func(http.ResponseWriter, *http.Request), methods ...string) {
methodsOK := make(map[string]bool)
for _, m := range methods {
@ -173,14 +174,14 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h fun
}
response.Header().Set("Access-Control-Allow-Origin", "*")
switch request.Method {
case "HEAD":
// Return a bodyless response to HEAD for any resource that allows GET.
if _, ok := methodsOK["GET"]; ok && request.Method == "HEAD" {
// We'll be sending an error anyway, but we
// should still comply with HTTP spec by not
// sending a body.
response = BodylessResponseWriter{response}
case "OPTIONS":
// TODO, #469
h(response, request)
return
}
if _, ok := methodsOK[request.Method]; !ok {

View File

@ -309,9 +309,15 @@ func TestHandleFunc(t *testing.T) {
// Disallowed method special case: response to HEAD has got no body
runWrappedHandler(&http.Request{Method: "HEAD"}, "GET", "POST")
test.AssertEquals(t, stubCalled, false)
test.AssertEquals(t, stubCalled, true)
test.AssertEquals(t, rw.Body.String(), "")
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, POST")
// HEAD doesn't work with POST-only endpoints
runWrappedHandler(&http.Request{Method: "HEAD"}, "POST")
test.AssertEquals(t, stubCalled, false)
test.AssertEquals(t, rw.Header().Get("Content-Type"), "application/problem+json")
test.AssertEquals(t, rw.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
test.AssertEquals(t, rw.Header().Get("Allow"), "POST")
}
func TestStandardHeaders(t *testing.T) {