Merge branch 'master' into test_wrong_signature

This commit is contained in:
Damian Düsentrieb 2015-12-02 17:59:13 +01:00
commit 1ce57b6f60
31 changed files with 365 additions and 257 deletions

View File

@ -39,7 +39,7 @@ branches:
- /^test-.*$/
before_install:
- source test/travis-before-install.sh
- travis_retry test/travis-before-install.sh
# Override default Travis install command to prevent it from adding
# Godeps/_workspace to GOPATH. When that happens, it hides failures that should

View File

@ -3,7 +3,6 @@ Boulder - An ACME CA
This is an initial implementation of an ACME-based CA. The [ACME protocol](https://github.com/letsencrypt/acme-spec/) allows the CA to automatically verify that an applicant for a certificate actually controls an identifier, and allows domain holders to issue and revoke certificates for their domains.
[![Build Status](https://travis-ci.org/letsencrypt/boulder.svg)](https://travis-ci.org/letsencrypt/boulder)
[![Coverage Status](https://coveralls.io/repos/letsencrypt/boulder/badge.svg)](https://coveralls.io/r/letsencrypt/boulder)
@ -54,29 +53,18 @@ or
(On OS X, using port, you will have to add `CGO_CFLAGS="-I/opt/local/include" CGO_LDFLAGS="-L/opt/local/lib"` to your environment or `go` invocations.)
Resolve Go-dependencies:
Resolve Go-dependencies, set up a database and RabbitMQ:
> go get bitbucket.org/liamstask/goose/cmd/goose
> go get github.com/jsha/listenbuddy
> go get github.com/letsencrypt/boulder/ # Ignore errors about no buildable files
> go get -u github.com/golang/lint/golint
> ./test/setup.sh
Set up a database:
> cd $GOPATH/src/github.com/letsencrypt/boulder
> ./test/create_db.sh
Set up RabbitMQ:
> go run cmd/rabbitmq-setup/main.go -server amqp://localhost
**Note**: `create_db.sh` uses the root MariaDB user with the default
password, so if you have disabled that account or changed the password
you may have to adjust the file or recreate the commands.
**Note**: `setup.sh` calls `create_db.sh`, which uses the root MariaDB
user with the default password, so if you have disabled that account
or changed the password you may have to adjust the file or recreate the commands.
Start each boulder component with test configs (Ctrl-C kills all):
> ./start.py
Run tests:
> ./test.sh
@ -108,27 +96,21 @@ client <-checks-> VA ---+
```
In Boulder, these components are represented by Go interfaces. This allows us to have two operational modes: Consolidated and distributed. In consolidated mode, the objects representing the different components interact directly, through function calls. In distributed mode, each component runs in a separate process (possibly on a separate machine), and sees the other components' methods by way of a messaging layer.
Internally, the logic of the system is based around two types of objects, authorizations and certificates, mapping directly to the resources of the same name in ACME.
Internally, the logic of the system is based around four types of objects: registrations, authorizations, challenges, and certificates, mapping directly to the resources of the same name in ACME.
Requests from ACME clients result in new objects and changes to objects. The Storage Authority maintains persistent copies of the current set of objects.
Objects are also passed from one component to another on change events. For example, when a client provides a successful response to a validation challenge, it results in a change to the corresponding validation object. The Validation Authority forward the new validation object to the Storage Authority for storage, and to the Registration Authority for any updates to a related Authorization object.
Objects are also passed from one component to another on change events. For example, when a client provides a successful response to a validation challenge, it results in a change to the corresponding validation object. The Validation Authority forwards the new validation object to the Storage Authority for storage, and to the Registration Authority for any updates to a related Authorization object.
Boulder supports distributed operation using AMQP as a message bus (e.g., via RabbitMQ). For components that you want to be remote, it is necessary to instantiate a "client" and "server" for that component. The client implements the component's Go interface, while the server has the actual logic for the component. More details in `amqp-rpc.go`.
Boulder uses AMQP as a message bus. For components that you want to be remote, it is necessary to instantiate a "client" and "server" for that component. The client implements the component's Go interface, while the server has the actual logic for the component. More details in `amqp-rpc.go`.
The full details of how the various ACME operations happen in Boulder are laid out in [DESIGN.md](https://github.com/letsencrypt/boulder/blob/master/DESIGN.md)
Dependencies
------------
All Go dependencies are vendorized under the Godeps directory,
both to [make dependency management
easier](https://groups.google.com/forum/m/#!topic/golang-dev/nMWoEAG55v8)
and to [avoid insecure fallback in go
get](https://github.com/golang/go/issues/9637).
to [make dependency management easier](https://groups.google.com/forum/m/#!topic/golang-dev/nMWoEAG55v8).
Local development also requires a RabbitMQ installation and MariaDB
10 installation (see above). MariaDB should be run on port 3306 for the
@ -137,10 +119,10 @@ default integration tests.
To update the Go dependencies:
```
# Disable insecure fallback by blocking port 80.
sudo /sbin/iptables -A OUTPUT -p tcp --dport 80 -j DROP
# Fetch godep
go get -u https://github.com/tools/godep
# Check out the currently vendorized version of each dependency.
godep restore
# Update to the latest version of a dependency. Alternately you can cd to the
# directory under GOPATH and check out a specific revision. Here's an example
# using cfssl:
@ -152,11 +134,8 @@ godep update github.com/cloudflare/cfssl/...
godep save -r ./...
git add Godeps
git commit
# Assuming you had no other iptables rules, re-enable port 80.
sudo iptables -D OUTPUT 1
```
TODO
----

View File

@ -14,7 +14,6 @@ import (
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"math/big"
"strings"
"sync"
@ -27,8 +26,6 @@ import (
blog "github.com/letsencrypt/boulder/log"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
@ -63,18 +60,18 @@ const (
// OCSP responses.
type CertificateAuthorityImpl struct {
profile string
Signer signer.Signer
OCSPSigner ocsp.Signer
signer signer.Signer
ocspSigner ocsp.Signer
SA core.StorageAuthority
PA core.PolicyAuthority
Publisher core.Publisher
Clk clock.Clock // TODO(jmhodges): should be private, like log
clk clock.Clock // TODO(jmhodges): should be private, like log
log *blog.AuditLogger
stats statsd.Statter
Prefix int // Prepended to the serial number
ValidityPeriod time.Duration
NotAfter time.Time
MaxNames int
prefix int // Prepended to the serial number
validityPeriod time.Duration
notAfter time.Time
maxNames int
hsmFaultLock sync.Mutex
hsmFaultLastObserved time.Time
@ -87,7 +84,13 @@ 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(config cmd.CAConfig, clk clock.Clock, stats statsd.Statter, issuerCert string) (*CertificateAuthorityImpl, error) {
func NewCertificateAuthorityImpl(
config cmd.CAConfig,
clk clock.Clock,
stats statsd.Statter,
issuer *x509.Certificate,
privateKey crypto.Signer,
) (*CertificateAuthorityImpl, error) {
var ca *CertificateAuthorityImpl
var err error
logger := blog.GetAuditLogger()
@ -109,18 +112,7 @@ func NewCertificateAuthorityImpl(config cmd.CAConfig, clk clock.Clock, stats sta
return nil, err
}
// Load the private key, which can be a file or a PKCS#11 key.
priv, err := loadKey(config.Key)
if err != nil {
return nil, err
}
issuer, err := core.LoadCert(issuerCert)
if err != nil {
return nil, err
}
signer, err := local.NewSigner(priv, issuer, x509.SHA256WithRSA, cfsslConfigObj.Signing)
signer, err := local.NewSigner(privateKey, issuer, x509.SHA256WithRSA, cfsslConfigObj.Signing)
if err != nil {
return nil, err
}
@ -135,61 +127,36 @@ func NewCertificateAuthorityImpl(config cmd.CAConfig, clk clock.Clock, stats sta
// Set up our OCSP signer. Note this calls for both the issuer cert and the
// OCSP signing cert, which are the same in our case.
ocspSigner, err := ocsp.NewSigner(issuer, issuer, priv, lifespanOCSP)
ocspSigner, err := ocsp.NewSigner(issuer, issuer, privateKey, lifespanOCSP)
if err != nil {
return nil, err
}
ca = &CertificateAuthorityImpl{
Signer: signer,
OCSPSigner: ocspSigner,
signer: signer,
ocspSigner: ocspSigner,
profile: config.Profile,
Prefix: config.SerialPrefix,
Clk: clk,
prefix: config.SerialPrefix,
clk: clk,
log: logger,
stats: stats,
NotAfter: issuer.NotAfter,
notAfter: issuer.NotAfter,
hsmFaultTimeout: config.HSMFaultTimeout.Duration,
}
if config.Expiry == "" {
return nil, errors.New("Config must specify an expiry period.")
}
ca.ValidityPeriod, err = time.ParseDuration(config.Expiry)
ca.validityPeriod, err = time.ParseDuration(config.Expiry)
if err != nil {
return nil, err
}
ca.MaxNames = config.MaxNames
ca.maxNames = config.MaxNames
return ca, nil
}
func loadKey(keyConfig cmd.KeyConfig) (priv crypto.Signer, err error) {
if keyConfig.File != "" {
var keyBytes []byte
keyBytes, err = ioutil.ReadFile(keyConfig.File)
if err != nil {
return nil, fmt.Errorf("Could not read key file %s", keyConfig.File)
}
priv, err = helpers.ParsePrivateKeyPEM(keyBytes)
return
}
pkcs11Config := keyConfig.PKCS11
if pkcs11Config.Module == "" ||
pkcs11Config.TokenLabel == "" ||
pkcs11Config.PIN == "" ||
pkcs11Config.PrivateKeyLabel == "" {
err = fmt.Errorf("Missing a field in pkcs11Config %#v", pkcs11Config)
return
}
priv, err = pkcs11key.New(pkcs11Config.Module,
pkcs11Config.TokenLabel, pkcs11Config.PIN, pkcs11Config.PrivateKeyLabel)
return
}
// checkHSMFault checks whether there has been an HSM fault observed within the
// timeout window. CA methods that use the HSM should call this method right
// away, to minimize the performance impact of HSM outages.
@ -202,7 +169,7 @@ func (ca *CertificateAuthorityImpl) checkHSMFault() error {
return nil
}
now := ca.Clk.Now()
now := ca.clk.Now()
timeout := ca.hsmFaultLastObserved.Add(ca.hsmFaultTimeout)
if now.Before(timeout) {
err := core.ServiceUnavailableError("HSM is unavailable")
@ -221,7 +188,7 @@ func (ca *CertificateAuthorityImpl) noteHSMFault(err error) {
if err != nil {
ca.stats.Inc(metricHSMFaultObserved, 1, 1.0)
ca.hsmFaultLastObserved = ca.Clk.Now()
ca.hsmFaultLastObserved = ca.clk.Now()
}
return
}
@ -246,7 +213,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(xferObj core.OCSPSigningRequest
RevokedAt: xferObj.RevokedAt,
}
ocspResponse, err := ca.OCSPSigner.Sign(signRequest)
ocspResponse, err := ca.ocspSigner.Sign(signRequest)
ca.noteHSMFault(err)
return ocspResponse, err
}
@ -307,8 +274,8 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
// Collapse any duplicate names. Note that this operation may re-order the names
hostNames = core.UniqueLowerNames(hostNames)
if ca.MaxNames > 0 && len(hostNames) > ca.MaxNames {
err = core.MalformedRequestError(fmt.Sprintf("Certificate request has %d > %d names", len(hostNames), ca.MaxNames))
if ca.maxNames > 0 && len(hostNames) > ca.maxNames {
err = core.MalformedRequestError(fmt.Sprintf("Certificate request has %d names, maximum is %d.", len(hostNames), ca.maxNames))
ca.log.WarningErr(err)
return emptyCert, err
}
@ -331,9 +298,9 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
}
}
notAfter := ca.Clk.Now().Add(ca.ValidityPeriod)
notAfter := ca.clk.Now().Add(ca.validityPeriod)
if ca.NotAfter.Before(notAfter) {
if ca.notAfter.Before(notAfter) {
err = core.InternalServerError("Cannot issue a certificate that expires after the intermediate certificate.")
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
ca.log.AuditErr(err)
@ -349,7 +316,7 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
// We want 136 bits of random number, plus an 8-bit instance id prefix.
const randBits = 136
serialBytes := make([]byte, randBits/8+1)
serialBytes[0] = byte(ca.Prefix)
serialBytes[0] = byte(ca.prefix)
_, err = rand.Read(serialBytes[1:])
if err != nil {
err = core.InternalServerError(err.Error())
@ -372,7 +339,7 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
Serial: serialBigInt,
}
certPEM, err := ca.Signer.Sign(req)
certPEM, err := ca.signer.Sign(req)
ca.noteHSMFault(err)
if err != nil {
err = core.InternalServerError(err.Error())

View File

@ -7,6 +7,7 @@ package ca
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/asn1"
"fmt"
@ -16,6 +17,7 @@ import (
"time"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
ocspConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/cmd"
@ -120,6 +122,21 @@ type testCtx struct {
cleanUp func()
}
var caKey crypto.Signer
var caCert *x509.Certificate
func init() {
var err error
caKey, err = helpers.ParsePrivateKeyPEM(mustRead(caKeyFile))
if err != nil {
panic(fmt.Sprintf("Unable to parse %s: %s", caKeyFile, err))
}
caCert, err = core.LoadCert(caCertFile)
if err != nil {
panic(fmt.Sprintf("Unable to parse %s: %s", caCertFile, err))
}
}
func setup(t *testing.T) *testCtx {
// Create an SA
dbMap, err := sa.NewDbMap(vars.DBConnSA)
@ -150,11 +167,8 @@ func setup(t *testing.T) *testCtx {
// Create a CA
caConfig := cmd.CAConfig{
Profile: profileName,
SerialPrefix: 17,
Key: cmd.KeyConfig{
File: caKeyFile,
},
Profile: profileName,
SerialPrefix: 17,
Expiry: "8760h",
LifespanOCSP: "45m",
MaxNames: 2,
@ -197,7 +211,15 @@ func setup(t *testing.T) *testCtx {
stats := mocks.NewStatter()
return &testCtx{ssa, caConfig, reg, pa, fc, &stats, cleanUp}
return &testCtx{
ssa,
caConfig,
reg,
pa,
fc,
&stats,
cleanUp,
}
}
func TestFailNoSerial(t *testing.T) {
@ -205,14 +227,14 @@ func TestFailNoSerial(t *testing.T) {
defer ctx.cleanUp()
ctx.caConfig.SerialPrefix = 0
_, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
_, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertError(t, err, "CA should have failed with no SerialPrefix")
}
func TestIssueCertificate(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
@ -289,7 +311,7 @@ func TestIssueCertificate(t *testing.T) {
func TestRejectNoName(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
@ -306,7 +328,7 @@ func TestRejectNoName(t *testing.T) {
func TestRejectTooManyNames(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
@ -323,7 +345,7 @@ func TestRejectTooManyNames(t *testing.T) {
func TestDeduplication(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
@ -347,7 +369,7 @@ func TestDeduplication(t *testing.T) {
func TestRejectValidityTooLong(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
test.AssertNotError(t, err, "Failed to create CA")
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
@ -355,7 +377,7 @@ func TestRejectValidityTooLong(t *testing.T) {
// Test that the CA rejects CSRs that would expire after the intermediate cert
csr, _ := x509.ParseCertificateRequest(NoCNCSR)
ca.NotAfter = ctx.fc.Now()
ca.notAfter = ctx.fc.Now()
_, err = ca.IssueCertificate(*csr, 1)
test.AssertEquals(t, err.Error(), "Cannot issue a certificate that expires after the intermediate certificate.")
_, ok := err.(core.InternalServerError)
@ -365,7 +387,7 @@ func TestRejectValidityTooLong(t *testing.T) {
func TestShortKey(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -381,7 +403,7 @@ func TestShortKey(t *testing.T) {
func TestRejectBadAlgorithm(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -398,7 +420,7 @@ func TestCapitalizedLetters(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ctx.caConfig.MaxNames = 3
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -437,7 +459,7 @@ func TestHSMFaultTimeout(t *testing.T) {
ctx := setup(t)
defer ctx.cleanUp()
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCertFile)
ca, err := NewCertificateAuthorityImpl(ctx.caConfig, ctx.fc, ctx.stats, caCert, caKey)
ca.Publisher = &mocks.Publisher{}
ca.PA = ctx.pa
ca.SA = ctx.sa
@ -451,13 +473,13 @@ func TestHSMFaultTimeout(t *testing.T) {
}
// Swap in a bad signer
goodSigner := ca.Signer
goodSigner := ca.signer
badHSMErrorMessage := "This is really serious. You should wait"
badSigner := mocks.BadHSMSigner(badHSMErrorMessage)
badOCSPSigner := mocks.BadHSMOCSPSigner(badHSMErrorMessage)
// Cause the CA to enter the HSM fault condition
ca.Signer = badSigner
ca.signer = badSigner
_, err = ca.IssueCertificate(*csr, ctx.reg.ID)
test.AssertError(t, err, "CA failed to return HSM error")
test.AssertEquals(t, err.Error(), badHSMErrorMessage)
@ -472,7 +494,7 @@ func TestHSMFaultTimeout(t *testing.T) {
test.AssertEquals(t, err.Error(), "HSM is unavailable")
// Swap in a good signer and move the clock forward to clear the fault
ca.Signer = goodSigner
ca.signer = goodSigner
ctx.fc.Add(ca.hsmFaultTimeout)
ctx.fc.Add(10 * time.Second)
@ -482,7 +504,7 @@ func TestHSMFaultTimeout(t *testing.T) {
_, err = ca.GenerateOCSP(ocspRequest)
// Check that GenerateOCSP can also trigger an HSM failure, in the same way
ca.OCSPSigner = badOCSPSigner
ca.ocspSigner = badOCSPSigner
_, err = ca.GenerateOCSP(ocspRequest)
test.AssertError(t, err, "CA failed to return HSM error")
test.AssertEquals(t, err.Error(), badHSMErrorMessage)

View File

@ -36,7 +36,8 @@ func main() {
go cmd.ProfileCmd("AM", stats)
server.Start(amqpConf)
err = server.Start(amqpConf)
cmd.FailOnError(err, "Unable to run Activity Monitor")
}
app.Run()

View File

@ -48,7 +48,9 @@ func setupContext(context *cli.Context) (rpc.RegistrationAuthorityClient, *blog.
rac, err := rpc.NewRegistrationAuthorityClient(clientName, amqpConf, stats)
cmd.FailOnError(err, "Unable to create CA client")
dbMap, err := sa.NewDbMap(c.Revoker.DBConnect)
dbURL, err := c.Revoker.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Couldn't setup database connection")
sac, err := rpc.NewStorageAuthorityClient(clientName, amqpConf, stats)

View File

@ -6,10 +6,18 @@
package main
import (
"crypto"
"encoding/json"
"fmt"
"io/ioutil"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/ca"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/policy"
"github.com/letsencrypt/boulder/rpc"
@ -18,6 +26,40 @@ import (
const clientName = "CA"
func loadPrivateKey(keyConfig cmd.KeyConfig) (crypto.Signer, error) {
if keyConfig.File != "" {
keyBytes, err := ioutil.ReadFile(keyConfig.File)
if err != nil {
return nil, fmt.Errorf("Could not read key file %s", keyConfig.File)
}
return helpers.ParsePrivateKeyPEM(keyBytes)
}
var pkcs11Config *pkcs11key.Config
if keyConfig.ConfigFile != "" {
contents, err := ioutil.ReadFile(keyConfig.ConfigFile)
if err != nil {
return nil, err
}
pkcs11Config = new(pkcs11key.Config)
err = json.Unmarshal(contents, pkcs11Config)
if err != nil {
return nil, err
}
} else {
pkcs11Config = keyConfig.PKCS11
}
if pkcs11Config.Module == "" ||
pkcs11Config.TokenLabel == "" ||
pkcs11Config.PIN == "" ||
pkcs11Config.PrivateKeyLabel == "" {
return nil, fmt.Errorf("Missing a field in pkcs11Config %#v", pkcs11Config)
}
return pkcs11key.New(pkcs11Config.Module,
pkcs11Config.TokenLabel, pkcs11Config.PIN, pkcs11Config.PrivateKeyLabel)
}
func main() {
app := cmd.NewAppShell("boulder-ca", "Handles issuance operations")
app.Action = func(c cmd.Config, stats statsd.Statter, auditlogger *blog.AuditLogger) {
@ -32,12 +74,25 @@ func main() {
go cmd.DebugServer(c.CA.DebugAddr)
paDbMap, err := sa.NewDbMap(c.PA.DBConnect)
dbURL, err := c.PA.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
paDbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Couldn't connect to policy database")
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, c.PA.EnforcePolicyWhitelist, c.PA.Challenges)
cmd.FailOnError(err, "Couldn't create PA")
cai, err := ca.NewCertificateAuthorityImpl(c.CA, clock.Default(), stats, c.Common.IssuerCert)
priv, err := loadPrivateKey(c.CA.Key)
cmd.FailOnError(err, "Couldn't load private key")
issuer, err := core.LoadCert(c.Common.IssuerCert)
cmd.FailOnError(err, "Couldn't load issuer cert")
cai, err := ca.NewCertificateAuthorityImpl(
c.CA,
clock.Default(),
stats,
issuer,
priv)
cmd.FailOnError(err, "Failed to create CA impl")
cai.PA = pa

View File

@ -31,7 +31,9 @@ func main() {
go cmd.DebugServer(c.RA.DebugAddr)
paDbMap, err := sa.NewDbMap(c.PA.DBConnect)
dbURL, err := c.PA.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
paDbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Couldn't connect to policy database")
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, c.PA.EnforcePolicyWhitelist, c.PA.Challenges)
cmd.FailOnError(err, "Couldn't create PA")

View File

@ -20,7 +20,9 @@ func main() {
saConf := c.SA
go cmd.DebugServer(saConf.DebugAddr)
dbMap, err := sa.NewDbMap(saConf.DBConnect)
dbURL, err := saConf.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Couldn't connect to SA database")
sai, err := sa.NewSQLStorageAuthority(dbMap, clock.Default())

View File

@ -239,10 +239,14 @@ func main() {
cmd.FailOnError(c.PA.CheckChallenges(), "Invalid PA configuration")
c.PA.SetDefaultChallengesIfEmpty()
saDbMap, err := sa.NewDbMap(c.CertChecker.DBConnect)
saDbURL, err := c.CertChecker.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
saDbMap, err := sa.NewDbMap(saDbURL)
cmd.FailOnError(err, "Could not connect to database")
paDbMap, err := sa.NewDbMap(c.PA.DBConnect)
paDbURL, err := c.PA.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
paDbMap, err := sa.NewDbMap(paDbURL)
cmd.FailOnError(err, "Could not connect to policy database")
checker := newChecker(saDbMap, paDbMap, clock.Default(), c.PA.EnforcePolicyWhitelist, c.PA.Challenges)

View File

@ -9,9 +9,12 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"strings"
"time"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/publisher"
"github.com/letsencrypt/boulder/va"
@ -64,8 +67,7 @@ type Config struct {
SA struct {
ServiceConfig
DBConnect string
DBConfig
MaxConcurrentRPCServerRequests int64
}
@ -91,7 +93,7 @@ type Config struct {
Syslog SyslogConfig
Revoker struct {
DBConnect string
DBConfig
// The revoker isn't a long running service, so doesn't get a full
// ServiceConfig, just an AMQPConfig.
AMQP *AMQPConfig
@ -99,14 +101,13 @@ type Config struct {
Mailer struct {
ServiceConfig
DBConfig
Server string
Port string
Username string
Password string
DBConnect string
CertLimit int
NagTimes []string
// How much earlier (than configured nag intervals) to
@ -119,10 +120,12 @@ type Config struct {
OCSPResponder struct {
ServiceConfig
DBConfig
// Source indicates the source of pre-signed OCSP responses to be used. It
// can be a DBConnect string or a file URL. The file URL style is used
// when responding from a static file for intermediates and roots.
// If DBConfig has non-empty fields, it takes precedence over this.
Source string
Path string
@ -164,9 +167,10 @@ type Config struct {
}
CertChecker struct {
DBConfig
Workers int
ReportDirectoryPath string
DBConnect string
}
SubscriberAgreementURL string
@ -180,9 +184,32 @@ type ServiceConfig struct {
AMQP *AMQPConfig
}
// DBConfig defines how to connect to a database. The connect string may be
// stored in a file separate from the config, because it can contain a password,
// which we want to keep out of configs.
type DBConfig struct {
DBConnect string
// A file containing a connect URL for the DB.
DBConnectFile string
}
// URL returns the DBConnect URL represented by this DBConfig object, either
// loading it from disk or returning a default value.
func (d *DBConfig) URL() (string, error) {
if d.DBConnectFile != "" {
url, err := ioutil.ReadFile(d.DBConnectFile)
return string(url), err
}
return d.DBConnect, nil
}
// AMQPConfig describes how to connect to AMQP, and how to speak to each of the
// RPC services we offer via AMQP.
type AMQPConfig struct {
// A file from which the AMQP Server URL will be read. This allows secret
// values (like the password) to be stored separately from the main config.
ServerURLFile string
// AMQP server URL, including username and password.
Server string
Insecure bool
RA *RPCServerConfig
@ -200,15 +227,28 @@ type AMQPConfig struct {
}
}
// ServerURL returns the appropriate server URL for this object, which may
// involve reading from a file.
func (a *AMQPConfig) ServerURL() (string, error) {
if a.ServerURLFile != "" {
url, err := ioutil.ReadFile(a.ServerURLFile)
return strings.TrimRight(string(url), "\n"), err
}
if a.Server == "" {
return "", fmt.Errorf("Missing AMQP server URL")
}
return a.Server, nil
}
// CAConfig structs have configuration information for the certificate
// authority, including database parameters as well as controls for
// issued certificates.
type CAConfig struct {
ServiceConfig
DBConfig
Profile string
TestMode bool
DBConnect string
SerialPrefix int
Key KeyConfig
// LifespanOCSP is how long OCSP responses are valid for; It should be longer
@ -230,7 +270,7 @@ type CAConfig struct {
// database, what policies it should enforce, and what challenges
// it should offer.
type PAConfig struct {
DBConnect string
DBConfig
EnforcePolicyWhitelist bool
Challenges map[string]bool
}
@ -263,16 +303,10 @@ func (pc *PAConfig) SetDefaultChallengesIfEmpty() {
// KeyConfig should contain either a File path to a PEM-format private key,
// or a PKCS11Config defining how to load a module for an HSM.
type KeyConfig struct {
File string
PKCS11 PKCS11Config
}
// PKCS11Config defines how to load a module for an HSM.
type PKCS11Config struct {
Module string
TokenLabel string
PIN string
PrivateKeyLabel string
// A file from which a pkcs11key.Config will be read and parsed, if present
ConfigFile string
File string
PKCS11 *pkcs11key.Config
}
// TLSConfig reprents certificates and a key for authenticated TLS.
@ -293,7 +327,7 @@ type RPCServerConfig struct {
// for the OCSP (and SCT) updater
type OCSPUpdaterConfig struct {
ServiceConfig
DBConnect string
DBConfig
NewCertificateWindow ConfigDuration
OldOCSPWindow ConfigDuration

View File

@ -231,10 +231,12 @@ func main() {
go cmd.DebugServer(c.Mailer.DebugAddr)
// Configure DB
dbMap, err := sa.NewDbMap(c.Mailer.DBConnect)
dbURL, err := c.Mailer.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Could not connect to database")
amqpConf := c.SA.AMQP
amqpConf := c.Mailer.AMQP
sac, err := rpc.NewStorageAuthorityClient(clientName, amqpConf, stats)
cmd.FailOnError(err, "Failed to create SA client")

View File

@ -150,7 +150,9 @@ func main() {
app.Action = func(c cmd.Config, stats statsd.Statter, auditlogger *blog.AuditLogger) {
// Configure DB
dbMap, err := sa.NewDbMap(c.PA.DBConnect)
dbURL, err := c.PA.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Could not connect to database")
dbMap.AddTableWithName(core.ExternalCert{}, "externalCerts").SetKeys(false, "SHA1")

View File

@ -141,7 +141,14 @@ func main() {
config := c.OCSPResponder
var source cfocsp.Source
url, err := url.Parse(config.Source)
// DBConfig takes precedence over Source, if present.
dbConnect, err := config.DBConfig.URL()
cmd.FailOnError(err, "Reading DB config")
if dbConnect == "" {
dbConnect = config.Source
}
url, err := url.Parse(dbConnect)
cmd.FailOnError(err, fmt.Sprintf("Source was not a URL: %s", config.Source))
if url.Scheme == "mysql+tcp" {

View File

@ -560,7 +560,9 @@ func main() {
go cmd.ProfileCmd("OCSP-Updater", stats)
// Configure DB
dbMap, err := sa.NewDbMap(conf.DBConnect)
dbURL, err := conf.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Could not connect to database")
cac, pubc, sac := setupClients(conf, stats)

View File

@ -110,7 +110,9 @@ func setupFromContext(context *cli.Context) (*policy.PolicyAuthorityDatabaseImpl
err = json.Unmarshal(configJSON, &c)
cmd.FailOnError(err, "Couldn't unmarshal configuration object")
dbMap, err := sa.NewDbMap(c.PA.DBConnect)
dbURL, err := c.PA.DBConfig.URL()
cmd.FailOnError(err, "Couldn't load DB URL")
dbMap, err := sa.NewDbMap(dbURL)
cmd.FailOnError(err, "Failed to create DB map")
padb, err := policy.NewPolicyAuthorityDatabaseImpl(dbMap)

View File

@ -25,14 +25,15 @@ func init() {
// Constants for AMQP
const (
monitorQueueName = "Monitor"
amqpExchange = "boulder"
amqpExchangeType = "topic"
amqpInternal = false
amqpDurable = false
amqpDeleteUnused = false
amqpExclusive = false
amqpNoWait = false
monitorQueueName = "Monitor"
amqpExchange = "boulder"
amqpExchangeType = "topic"
amqpInternal = false
amqpExchangeDurable = true
amqpQueueDurable = false
amqpDeleteUnused = false
amqpExclusive = false
amqpNoWait = false
)
func main() {
@ -45,7 +46,7 @@ func main() {
err = ch.ExchangeDeclare(
amqpExchange,
amqpExchangeType,
amqpDurable,
amqpExchangeDurable,
amqpDeleteUnused,
amqpInternal,
amqpNoWait,
@ -54,7 +55,7 @@ func main() {
_, err = ch.QueueDeclare(
monitorQueueName,
amqpDurable,
amqpQueueDurable,
amqpDeleteUnused,
amqpExclusive,
amqpNoWait,

View File

@ -20,8 +20,6 @@ import (
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
"github.com/letsencrypt/boulder/ca"
@ -180,20 +178,26 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
caKey, _ := x509.ParsePKCS1PrivateKey(caKeyPEM.Bytes)
caCertPEM, _ := pem.Decode([]byte(CAcertPEM))
caCert, _ := x509.ParseCertificate(caCertPEM.Bytes)
basicPolicy := &cfsslConfig.Signing{
Default: &cfsslConfig.SigningProfile{
Usage: []string{"server auth", "client auth"},
Expiry: 1 * time.Hour,
CSRWhitelist: &cfsslConfig.CSRWhitelist{
PublicKey: true,
PublicKeyAlgorithm: true,
SignatureAlgorithm: true,
DNSNames: true,
cfsslC := cfsslConfig.Config{
Signing: &cfsslConfig.Signing{
Default: &cfsslConfig.SigningProfile{
Usage: []string{"server auth", "client auth"},
ExpiryString: "1h",
CSRWhitelist: &cfsslConfig.CSRWhitelist{
PublicKey: true,
PublicKeyAlgorithm: true,
SignatureAlgorithm: true,
DNSNames: true,
},
},
},
}
signer, _ := local.NewSigner(caKey, caCert, x509.SHA256WithRSA, basicPolicy)
ocspSigner, _ := ocsp.NewSigner(caCert, caCert, caKey, time.Hour)
caConf := cmd.CAConfig{
SerialPrefix: 10,
LifespanOCSP: "1h",
Expiry: "1h",
CFSSL: cfsslC,
}
paDbMap, err := sa.NewDbMap(vars.DBConnPolicy)
if err != nil {
t.Fatalf("Failed to create dbMap: %s", err)
@ -201,16 +205,19 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
policyDBCleanUp := test.ResetPolicyTestDatabase(t)
pa, err := policy.NewPolicyAuthorityImpl(paDbMap, false, SupportedChallenges)
test.AssertNotError(t, err, "Couldn't create PA")
ca := ca.CertificateAuthorityImpl{
Signer: signer,
OCSPSigner: ocspSigner,
SA: ssa,
PA: pa,
ValidityPeriod: time.Hour * 2190,
NotAfter: time.Now().Add(time.Hour * 8761),
Clk: fc,
Publisher: &mocks.Publisher{},
}
stats, _ := statsd.NewNoopClient()
ca, err := ca.NewCertificateAuthorityImpl(
caConf,
fc,
stats,
caCert,
caKey)
test.AssertNotError(t, err, "Couldn't create CA")
ca.SA = ssa
ca.PA = pa
ca.Publisher = &mocks.Publisher{}
cleanUp := func() {
saDBCleanUp()
policyDBCleanUp()
@ -224,7 +231,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
InitialIP: net.ParseIP("3.2.3.3"),
})
stats, _ := statsd.NewNoopClient()
ra := NewRegistrationAuthorityImpl(fc,
blog.GetAuditLogger(),
stats,
@ -237,7 +243,7 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
}, 1)
ra.SA = ssa
ra.VA = va
ra.CA = &ca
ra.CA = ca
ra.PA = pa
ra.DNSResolver = &mocks.DNSResolver{}

View File

@ -297,14 +297,19 @@ func makeAmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
log := blog.GetAuditLogger()
serverURL, err := conf.ServerURL()
if err != nil {
return nil, err
}
if conf.Insecure == true {
// If the Insecure flag is true, then just go ahead and connect
conn, err = amqp.Dial(conf.Server)
conn, err = amqp.Dial(serverURL)
} else {
// The insecure flag is false or not set, so we need to load up the options
log.Info("AMQPS: Loading TLS Options.")
if strings.HasPrefix(conf.Server, "amqps") == false {
if strings.HasPrefix(serverURL, "amqps") == false {
err = fmt.Errorf("AMQPS: Not using an AMQPS URL. To use AMQP instead of AMQPS, set insecure=true")
return nil, err
}
@ -348,7 +353,7 @@ func makeAmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
log.Info("AMQPS: Configured CA certificate for AMQPS.")
}
conn, err = amqp.DialTLS(conf.Server, cfg)
conn, err = amqp.DialTLS(serverURL, cfg)
}
if err != nil {

View File

@ -217,13 +217,6 @@ if [[ "$RUN" =~ "migrations" ]] ; then
end_context #"migrations"
fi
#
# Prepare the database for unittests and integration tests
#
if [[ "${TRAVIS}" == "true" ]] ; then
./test/create_db.sh || die "unable to create the boulder database with test/create_db.sh"
fi
#
# Unit Tests.
#

View File

@ -96,7 +96,7 @@
"maxConcurrentRPCServerRequests": 16,
"hsmFaultTimeout": "300s",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"serviceQueue": "CA.server",
"SA": {
@ -127,7 +127,7 @@
"maxContactsPerRegistration": 100,
"debugAddr": "localhost:8002",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"serviceQueue": "RA.server",
"VA": {
@ -147,11 +147,11 @@
},
"sa": {
"dbConnect": "mysql+tcp://sa@localhost:3306/boulder_sa_integration",
"dbConnectFile": "test/secrets/sa_dburl",
"maxConcurrentRPCServerRequests": 16,
"debugAddr": "localhost:8003",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"serviceQueue": "SA.server"
}
@ -167,7 +167,7 @@
},
"maxConcurrentRPCServerRequests": 16,
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"serviceQueue": "VA.server",
"RA": {
@ -182,9 +182,9 @@
},
"revoker": {
"dbConnect": "mysql+tcp://revoker@localhost:3306/boulder_sa_integration",
"dbConnectFile": "test/secrets/revoker_dburl",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"RA": {
"server": "RA.server",
@ -208,7 +208,7 @@
},
"ocspUpdater": {
"dbConnect": "mysql+tcp://ocsp_update@localhost:3306/boulder_sa_integration",
"dbConnectFile": "test/secrets/ocsp_updater_dburl",
"newCertificateWindow": "1s",
"oldOCSPWindow": "2s",
"missingSCTWindow": "1m",
@ -223,7 +223,7 @@
"signFailureBackoffMax": "30m",
"debugAddr": "localhost:8006",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"SA": {
"server": "SA.server",
@ -244,7 +244,7 @@
"debugAddr": "localhost:8007",
"amqp": {
"serviceQueue": "Monitor",
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true
}
},
@ -254,19 +254,27 @@
"port": "25",
"username": "cert-master@example.com",
"password": "password",
"dbConnect": "mysql+tcp://mailer@localhost:3306/boulder_sa_integration",
"dbConnectFile": "test/secrets/mailer_dburl",
"messageLimit": 0,
"nagTimes": ["24h", "72h", "168h", "336h"],
"nagCheckInterval": "24h",
"emailTemplate": "test/example-expiration-template",
"debugAddr": "localhost:8008"
"debugAddr": "localhost:8008",
"amqp": {
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"SA": {
"server": "SA.server",
"rpcTimeout": "15s"
}
}
},
"publisher": {
"maxConcurrentRPCServerRequests": 16,
"debugAddr": "localhost:8009",
"amqp": {
"server": "amqp://guest:guest@localhost:5673",
"serverURLFile": "test/secrets/amqp_url",
"insecure": true,
"serviceQueue": "Publisher.server",
"SA": {
@ -296,7 +304,7 @@
},
"certChecker": {
"dbConnect": "mysql+tcp://cert_checker@localhost:3306/boulder_sa_integration"
"dbConnectFile": "test/secrets/cert_checker_dburl"
},
"subscriberAgreementURL": "http://127.0.0.1:4001/terms/v1"

1
test/secrets/amqp_url Normal file
View File

@ -0,0 +1 @@
amqp://guest:guest@localhost:5673

View File

@ -0,0 +1 @@
mysql+tcp://cert_checker@localhost:3306/boulder_sa_integration

View File

@ -0,0 +1 @@
mysql+tcp://mailer@localhost:3306/boulder_sa_integration

View File

@ -0,0 +1 @@
mysql+tcp://ocsp_update@localhost:3306/boulder_sa_integration

1
test/secrets/pa_dburl Normal file
View File

@ -0,0 +1 @@
mysql+tcp://policy@localhost:3306/boulder_policy_integration

View File

@ -0,0 +1 @@
mysql+tcp://revoker@localhost:3306/boulder_sa_integration

1
test/secrets/sa_dburl Normal file
View File

@ -0,0 +1 @@
mysql+tcp://sa@localhost:3306/boulder_sa_integration

26
test/setup.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
#
# Fetch dependencies of Boulderthat are necessary for development or testing,
# and configure database and RabbitMQ.
#
go get \
golang.org/x/tools/cmd/vet \
golang.org/x/tools/cmd/cover \
github.com/golang/lint/golint \
github.com/mattn/goveralls \
github.com/modocache/gover \
github.com/jcjones/github-pr-status \
github.com/jsha/listenbuddy &
(wget https://github.com/jsha/boulder-tools/raw/master/goose.gz &&
mkdir -p $GOPATH/bin &&
zcat goose.gz > $GOPATH/bin/goose &&
chmod +x $GOPATH/bin/goose &&
./test/create_db.sh) &
# Set up rabbitmq exchange and activity monitor queue
go run cmd/rabbitmq-setup/main.go -server amqp://localhost &
# Wait for all the background commands to finish.
wait

View File

@ -1,50 +1,28 @@
#!/bin/bash
set -o xtrace
if [ "${TRAVIS}" == "true" ]; then
# Boulder consists of multiple Go packages, which
# refer to each other by their absolute GitHub path,
# e.g. github.com/letsencrypt/boulder/analysis. That means, by default, if
# someone forks the repo, Travis won't pass on their own repo. To fix that,
# we add a symlink.
mkdir -p $TRAVIS_BUILD_DIR $GOPATH/src/github.com/letsencrypt
if [ ! -d $GOPATH/src/github.com/letsencrypt/boulder ] ; then
ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/letsencrypt/boulder
fi
# Travis does shallow clones, so there is no master branch present.
# But test-no-outdated-migrations.sh needs to check diffs against master.
# Fetch just the master branch from origin.
( git fetch origin master
git branch master FETCH_HEAD ) &
# Github-PR-Status secret
if [ -n "$encrypted_53b2630f0fb4_key" ]; then
openssl aes-256-cbc \
-K $encrypted_53b2630f0fb4_key -iv $encrypted_53b2630f0fb4_iv \
-in test/github-secret.json.enc -out /tmp/github-secret.json -d
fi
else
alias travis_retry=""
# Boulder consists of multiple Go packages, which
# refer to each other by their absolute GitHub path,
# e.g. github.com/letsencrypt/boulder/analysis. That means, by default, if
# someone forks the repo, Travis won't pass on their own repo. To fix that,
# we add a symlink.
mkdir -p $TRAVIS_BUILD_DIR $GOPATH/src/github.com/letsencrypt
if [ ! -d $GOPATH/src/github.com/letsencrypt/boulder ] ; then
ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/letsencrypt/boulder
fi
travis_retry go get \
golang.org/x/tools/cmd/vet \
golang.org/x/tools/cmd/cover \
github.com/golang/lint/golint \
github.com/mattn/goveralls \
github.com/modocache/gover \
github.com/jcjones/github-pr-status \
github.com/jsha/listenbuddy &
# Travis does shallow clones, so there is no master branch present.
# But test-no-outdated-migrations.sh needs to check diffs against master.
# Fetch just the master branch from origin.
( git fetch origin master
git branch master FETCH_HEAD ) &
# Github-PR-Status secret
if [ -n "$encrypted_53b2630f0fb4_key" ]; then
openssl aes-256-cbc \
-K $encrypted_53b2630f0fb4_key -iv $encrypted_53b2630f0fb4_iv \
-in test/github-secret.json.enc -out /tmp/github-secret.json -d
fi
(wget https://github.com/jsha/boulder-tools/raw/master/goose.gz &&
mkdir -p $GOPATH/bin &&
zcat goose.gz > $GOPATH/bin/goose &&
chmod +x $GOPATH/bin/goose) &
# Set up rabbitmq exchange and activity monitor queue
go run cmd/rabbitmq-setup/main.go -server amqp://localhost &
# Wait for all the background commands to finish.
wait
./test/setup.sh
set +o xtrace

View File

@ -387,8 +387,9 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
}
challenge.Error = &probs.ProblemDetails{
Type: probs.UnauthorizedProblem,
Detail: "Correct zName not found for TLS SNI challenge",
Type: probs.UnauthorizedProblem,
Detail: fmt.Sprintf("Correct zName not found for TLS SNI challenge. Found %s",
strings.Join(certs[0].DNSNames, ", ")),
}
challenge.Status = core.StatusInvalid
return challenge, challenge.Error