Merge branch 'master' into google-ct
This commit is contained in:
commit
7e093c3ed4
|
|
@ -66,6 +66,9 @@ 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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -116,6 +118,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)
|
||||
|
|
@ -146,11 +163,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,
|
||||
|
|
@ -193,7 +207,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) {
|
||||
|
|
@ -201,14 +223,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
|
||||
|
|
@ -285,7 +307,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
|
||||
|
|
@ -302,7 +324,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
|
||||
|
|
@ -319,7 +341,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
|
||||
|
|
@ -343,7 +365,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
|
||||
|
|
@ -351,7 +373,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)
|
||||
|
|
@ -361,7 +383,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
|
||||
|
|
@ -377,7 +399,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
|
||||
|
|
@ -394,7 +416,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
|
||||
|
|
@ -415,7 +437,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
|
||||
|
|
@ -429,13 +451,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)
|
||||
|
|
@ -450,7 +472,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)
|
||||
|
||||
|
|
@ -460,7 +482,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)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@ package main
|
|||
// broker to look for anomalies.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
|
||||
|
|
@ -22,106 +19,24 @@ import (
|
|||
"github.com/letsencrypt/boulder/rpc"
|
||||
)
|
||||
|
||||
// Constants for AMQP
|
||||
const (
|
||||
QueueName = "Monitor"
|
||||
AmqpExchange = "boulder"
|
||||
AmqpExchangeType = "topic"
|
||||
AmqpInternal = false
|
||||
AmqpDurable = false
|
||||
AmqpDeleteUnused = false
|
||||
AmqpExclusive = false
|
||||
AmqpNoWait = false
|
||||
AmqpNoLocal = false
|
||||
AmqpAutoAck = false
|
||||
AmqpMandatory = false
|
||||
AmqpImmediate = false
|
||||
)
|
||||
|
||||
func startMonitor(rpcCh *amqp.Channel, logger *blog.AuditLogger, stats statsd.Statter) {
|
||||
ae := analysisengine.NewLoggingAnalysisEngine()
|
||||
|
||||
// For convenience at the broker, identifiy ourselves by hostname
|
||||
consumerTag, err := os.Hostname()
|
||||
if err != nil {
|
||||
cmd.FailOnError(err, "Could not determine hostname")
|
||||
}
|
||||
|
||||
_, err = rpcCh.QueueDeclarePassive(
|
||||
QueueName,
|
||||
AmqpDurable,
|
||||
AmqpDeleteUnused,
|
||||
AmqpExclusive,
|
||||
AmqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
logger.Info(fmt.Sprintf("Queue %s does not exist on AMQP server, attempting to create.", QueueName))
|
||||
|
||||
// Attempt to create the Queue if not exists
|
||||
_, err = rpcCh.QueueDeclare(
|
||||
QueueName,
|
||||
AmqpDurable,
|
||||
AmqpDeleteUnused,
|
||||
AmqpExclusive,
|
||||
AmqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
cmd.FailOnError(err, "Could not declare queue")
|
||||
}
|
||||
|
||||
routingKey := "#" //wildcard
|
||||
|
||||
err = rpcCh.QueueBind(
|
||||
QueueName,
|
||||
routingKey,
|
||||
AmqpExchange,
|
||||
false,
|
||||
nil)
|
||||
if err != nil {
|
||||
txt := fmt.Sprintf("Could not bind to queue [%s]. NOTE: You may need to delete %s to re-trigger the bind attempt after fixing permissions, or manually bind the queue to %s.", QueueName, QueueName, routingKey)
|
||||
cmd.FailOnError(err, txt)
|
||||
}
|
||||
}
|
||||
|
||||
deliveries, err := rpcCh.Consume(
|
||||
QueueName,
|
||||
consumerTag,
|
||||
AmqpAutoAck,
|
||||
AmqpExclusive,
|
||||
AmqpNoLocal,
|
||||
AmqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
cmd.FailOnError(err, "Could not subscribe to queue")
|
||||
}
|
||||
|
||||
// Run forever.
|
||||
for d := range deliveries {
|
||||
// Pass each message to the Analysis Engine
|
||||
err = ae.ProcessMessage(d)
|
||||
if err != nil {
|
||||
logger.Alert(fmt.Sprintf("Could not process message: %s", err))
|
||||
} else {
|
||||
// Only ack the delivery we actually handled (ackMultiple=false)
|
||||
const ackMultiple = false
|
||||
d.Ack(ackMultiple)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cmd.NewAppShell("activity-monitor", "RPC activity monitor")
|
||||
|
||||
app.Action = func(c cmd.Config, stats statsd.Statter, auditlogger *blog.AuditLogger) {
|
||||
go cmd.DebugServer(c.ActivityMonitor.DebugAddr)
|
||||
|
||||
ch, err := rpc.AmqpChannel(c.ActivityMonitor.AMQP)
|
||||
|
||||
amqpConf := c.ActivityMonitor.AMQP
|
||||
server, err := rpc.NewAmqpRPCServer(amqpConf, 0, stats)
|
||||
cmd.FailOnError(err, "Could not connect to AMQP")
|
||||
|
||||
ae := analysisengine.NewLoggingAnalysisEngine()
|
||||
server.HandleDeliveries(rpc.DeliveryHandler(func(d amqp.Delivery) {
|
||||
ae.ProcessMessage(d)
|
||||
}))
|
||||
|
||||
go cmd.ProfileCmd("AM", stats)
|
||||
|
||||
startMonitor(ch, auditlogger, stats)
|
||||
server.Start(amqpConf)
|
||||
}
|
||||
|
||||
app.Run()
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -37,7 +79,18 @@ func main() {
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,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/crypto/pkcs11key"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/va"
|
||||
)
|
||||
|
|
@ -265,16 +266,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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2014 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 main
|
||||
|
||||
// This command does a one-time setup of the RabbitMQ exchange and the Activity
|
||||
// Monitor queue, suitable for setting up a dev environment or Travis.
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
)
|
||||
|
||||
var server = flag.String("server", "", "RabbitMQ Server URL")
|
||||
|
||||
func init() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// Constants for AMQP
|
||||
const (
|
||||
monitorQueueName = "Monitor"
|
||||
amqpExchange = "boulder"
|
||||
amqpExchangeType = "topic"
|
||||
amqpInternal = false
|
||||
amqpDurable = false
|
||||
amqpDeleteUnused = false
|
||||
amqpExclusive = false
|
||||
amqpNoWait = false
|
||||
)
|
||||
|
||||
func main() {
|
||||
server := *server
|
||||
conn, err := amqp.Dial(server)
|
||||
cmd.FailOnError(err, "Could not connect to AMQP")
|
||||
ch, err := conn.Channel()
|
||||
cmd.FailOnError(err, "Could not connect to AMQP")
|
||||
|
||||
err = ch.ExchangeDeclare(
|
||||
amqpExchange,
|
||||
amqpExchangeType,
|
||||
amqpDurable,
|
||||
amqpDeleteUnused,
|
||||
amqpInternal,
|
||||
amqpNoWait,
|
||||
nil)
|
||||
cmd.FailOnError(err, "Declaring exchange")
|
||||
|
||||
_, err = ch.QueueDeclare(
|
||||
monitorQueueName,
|
||||
amqpDurable,
|
||||
amqpDeleteUnused,
|
||||
amqpExclusive,
|
||||
amqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
cmd.FailOnError(err, "Could not declare queue")
|
||||
}
|
||||
|
||||
routingKey := "#" //wildcard
|
||||
|
||||
err = ch.QueueBind(
|
||||
monitorQueueName,
|
||||
routingKey,
|
||||
amqpExchange,
|
||||
false,
|
||||
nil)
|
||||
if err != nil {
|
||||
txt := fmt.Sprintf("Could not bind to queue [%s]. NOTE: You may need to delete %s to re-trigger the bind attempt after fixing permissions, or manually bind the queue to %s.", monitorQueueName, monitorQueueName, routingKey)
|
||||
cmd.FailOnError(err, txt)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
)
|
||||
|
||||
// AcmeStatus defines the state of a given authorization
|
||||
|
|
@ -33,16 +34,6 @@ type IdentifierType string
|
|||
// OCSPStatus defines the state of OCSP for a domain
|
||||
type OCSPStatus string
|
||||
|
||||
// ProblemType defines the error types in the ACME protocol
|
||||
type ProblemType string
|
||||
|
||||
// ProblemDetails objects represent problem documents
|
||||
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
||||
type ProblemDetails struct {
|
||||
Type ProblemType `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
}
|
||||
|
||||
// These statuses are the states of authorizations
|
||||
const (
|
||||
StatusUnknown = AcmeStatus("unknown") // Unknown status; the default
|
||||
|
|
@ -74,18 +65,6 @@ const (
|
|||
OCSPStatusRevoked = OCSPStatus("revoked")
|
||||
)
|
||||
|
||||
// Error types that can be used in ACME payloads
|
||||
const (
|
||||
ConnectionProblem = ProblemType("urn:acme:error:connection")
|
||||
MalformedProblem = ProblemType("urn:acme:error:malformed")
|
||||
ServerInternalProblem = ProblemType("urn:acme:error:serverInternal")
|
||||
TLSProblem = ProblemType("urn:acme:error:tls")
|
||||
UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized")
|
||||
UnknownHostProblem = ProblemType("urn:acme:error:unknownHost")
|
||||
RateLimitedProblem = ProblemType("urn:acme:error:rateLimited")
|
||||
BadNonceProblem = ProblemType("urn:acme:error:badNonce")
|
||||
)
|
||||
|
||||
// These types are the available challenges
|
||||
const (
|
||||
ChallengeTypeSimpleHTTP = "simpleHttp"
|
||||
|
|
@ -122,10 +101,6 @@ const TLSSNISuffix = "acme.invalid"
|
|||
// DNSPrefix is attached to DNS names in DNS challenges
|
||||
const DNSPrefix = "_acme-challenge"
|
||||
|
||||
func (pd *ProblemDetails) Error() string {
|
||||
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
|
||||
}
|
||||
|
||||
// An AcmeIdentifier encodes an identifier that can
|
||||
// be validated by ACME. The protocol allows for different
|
||||
// types of identifier to be supported (DNS names, IP
|
||||
|
|
@ -326,7 +301,7 @@ type Challenge struct {
|
|||
Status AcmeStatus `json:"status,omitempty"`
|
||||
|
||||
// Contains the error that occured during challenge validation, if any
|
||||
Error *ProblemDetails `json:"error,omitempty"`
|
||||
Error *probs.ProblemDetails `json:"error,omitempty"`
|
||||
|
||||
// If successful, the time at which this challenge
|
||||
// was completed by the server.
|
||||
|
|
|
|||
|
|
@ -17,13 +17,6 @@ import (
|
|||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
func TestProblemDetails(t *testing.T) {
|
||||
pd := &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: "Wat? o.O"}
|
||||
test.AssertEquals(t, pd.Error(), "urn:acme:error:malformed :: Wat? o.O")
|
||||
}
|
||||
|
||||
func TestRegistrationUpdate(t *testing.T) {
|
||||
oldURL, _ := ParseAcmeURL("http://old.invalid")
|
||||
newURL, _ := ParseAcmeURL("http://new.invalid")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package dns
|
|||
import (
|
||||
"net"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
)
|
||||
|
||||
const detailDNSTimeout = "DNS query timed out"
|
||||
|
|
@ -14,8 +14,8 @@ const detailServerFailure = "Server failure at resolver"
|
|||
// methods and tests if the error was an underlying net.OpError or an error
|
||||
// caused by resolver returning SERVFAIL or other invalid Rcodes and returns
|
||||
// the relevant core.ProblemDetails.
|
||||
func ProblemDetailsFromDNSError(err error) *core.ProblemDetails {
|
||||
problem := &core.ProblemDetails{Type: core.ConnectionProblem}
|
||||
func ProblemDetailsFromDNSError(err error) *probs.ProblemDetails {
|
||||
problem := &probs.ProblemDetails{Type: probs.ConnectionProblem}
|
||||
if netErr, ok := err.(*net.OpError); ok {
|
||||
if netErr.Timeout() {
|
||||
problem.Detail = detailDNSTimeout
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/mocks"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
)
|
||||
|
||||
func TestProblemDetailsFromDNSError(t *testing.T) {
|
||||
|
|
@ -27,8 +27,8 @@ func TestProblemDetailsFromDNSError(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
err := ProblemDetailsFromDNSError(tc.err)
|
||||
if err.Type != core.ConnectionProblem {
|
||||
t.Errorf("ProblemDetailsFromDNSError(%q).Type = %q, expected %q", tc.err, err.Type, core.ConnectionProblem)
|
||||
if err.Type != probs.ConnectionProblem {
|
||||
t.Errorf("ProblemDetailsFromDNSError(%q).Type = %q, expected %q", tc.err, err.Type, probs.ConnectionProblem)
|
||||
}
|
||||
if err.Detail != tc.expected {
|
||||
t.Errorf("ProblemDetailsFromDNSError(%q).Detail = %q, expected %q", tc.err, err.Detail, tc.expected)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package probs
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error types that can be used in ACME payloads
|
||||
const (
|
||||
ConnectionProblem = ProblemType("urn:acme:error:connection")
|
||||
MalformedProblem = ProblemType("urn:acme:error:malformed")
|
||||
ServerInternalProblem = ProblemType("urn:acme:error:serverInternal")
|
||||
TLSProblem = ProblemType("urn:acme:error:tls")
|
||||
UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized")
|
||||
UnknownHostProblem = ProblemType("urn:acme:error:unknownHost")
|
||||
RateLimitedProblem = ProblemType("urn:acme:error:rateLimited")
|
||||
BadNonceProblem = ProblemType("urn:acme:error:badNonce")
|
||||
)
|
||||
|
||||
// ProblemType defines the error types in the ACME protocol
|
||||
type ProblemType string
|
||||
|
||||
// ProblemDetails objects represent problem documents
|
||||
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
||||
type ProblemDetails struct {
|
||||
Type ProblemType `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
HTTPStatus int `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (pd *ProblemDetails) Error() string {
|
||||
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package probs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
func TestProblemDetails(t *testing.T) {
|
||||
pd := &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: "Wat? o.O"}
|
||||
test.AssertEquals(t, pd.Error(), "urn:acme:error:malformed :: Wat? o.O")
|
||||
}
|
||||
|
|
@ -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{}
|
||||
|
||||
|
|
|
|||
121
rpc/amqp-rpc.go
121
rpc/amqp-rpc.go
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
|
@ -57,59 +58,6 @@ const (
|
|||
consumerName = "boulder"
|
||||
)
|
||||
|
||||
// AMQPDeclareExchange attempts to declare the configured AMQP exchange,
|
||||
// returning silently if already declared, erroring if nonexistant and
|
||||
// unable to create.
|
||||
func amqpDeclareExchange(conn *amqp.Connection) error {
|
||||
var err error
|
||||
var ch *amqp.Channel
|
||||
log := blog.GetAuditLogger()
|
||||
|
||||
ch, err = conn.Channel()
|
||||
if err != nil {
|
||||
log.Crit(fmt.Sprintf("Could not connect Channel: %s", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = ch.ExchangeDeclarePassive(
|
||||
AmqpExchange,
|
||||
AmqpExchangeType,
|
||||
AmqpDurable,
|
||||
AmqpDeleteUnused,
|
||||
AmqpInternal,
|
||||
AmqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
log.Info(fmt.Sprintf("Exchange %s does not exist on AMQP server, creating.", AmqpExchange))
|
||||
|
||||
// Channel is invalid at this point, so recreate
|
||||
ch.Close()
|
||||
ch, err = conn.Channel()
|
||||
if err != nil {
|
||||
log.Crit(fmt.Sprintf("Could not connect Channel: %s", err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = ch.ExchangeDeclare(
|
||||
AmqpExchange,
|
||||
AmqpExchangeType,
|
||||
AmqpDurable,
|
||||
AmqpDeleteUnused,
|
||||
AmqpInternal,
|
||||
AmqpNoWait,
|
||||
nil)
|
||||
if err != nil {
|
||||
log.Crit(fmt.Sprintf("Could not declare exchange: %s", err))
|
||||
ch.Close()
|
||||
return err
|
||||
}
|
||||
log.Info(fmt.Sprintf("Created exchange %s.", AmqpExchange))
|
||||
}
|
||||
|
||||
ch.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// A simplified way to declare and subscribe to an AMQP queue
|
||||
func amqpSubscribe(ch amqpChannel, name string) (<-chan amqp.Delivery, error) {
|
||||
var err error
|
||||
|
|
@ -135,8 +83,8 @@ func amqpSubscribe(ch amqpChannel, name string) (<-chan amqp.Delivery, error) {
|
|||
nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(
|
||||
"Could not bind to queue [%s]. NOTE: You may need to delete %s to re-trigger the bind attempt after fixing permissions, or manually bind the queue to %s.",
|
||||
name, name, routingKey)
|
||||
"Could not bind to queue %s: %s. NOTE: You may need to delete it to re-trigger the bind attempt after fixing permissions, or manually bind the queue to %s.",
|
||||
err, name, routingKey)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +103,10 @@ func amqpSubscribe(ch amqpChannel, name string) (<-chan amqp.Delivery, error) {
|
|||
return msgs, nil
|
||||
}
|
||||
|
||||
// DeliveryHandler is a function that will process an amqp.DeliveryHandler
|
||||
type DeliveryHandler func(amqp.Delivery)
|
||||
type messageHandler func([]byte) ([]byte, error)
|
||||
|
||||
// AmqpRPCServer listens on a specified queue within an AMQP channel.
|
||||
// When messages arrive on that queue, it dispatches them based on type,
|
||||
// and returns the response to the ReplyTo queue.
|
||||
|
|
@ -162,10 +114,13 @@ func amqpSubscribe(ch amqpChannel, name string) (<-chan amqp.Delivery, error) {
|
|||
// To implement specific functionality, using code should use the Handle
|
||||
// method to add specific actions.
|
||||
type AmqpRPCServer struct {
|
||||
serverQueue string
|
||||
connection *amqpConnector
|
||||
log *blog.AuditLogger
|
||||
dispatchTable map[string]func([]byte) ([]byte, error)
|
||||
serverQueue string
|
||||
connection *amqpConnector
|
||||
log *blog.AuditLogger
|
||||
handleDelivery DeliveryHandler
|
||||
// Servers that just care about messages (method + body) add entries to
|
||||
// dispatchTable
|
||||
dispatchTable map[string]messageHandler
|
||||
connected bool
|
||||
done bool
|
||||
mu sync.RWMutex
|
||||
|
|
@ -194,7 +149,7 @@ func NewAmqpRPCServer(amqpConf *cmd.AMQPConfig, maxConcurrentRPCServerRequests i
|
|||
serverQueue: amqpConf.ServiceQueue,
|
||||
connection: newAMQPConnector(amqpConf.ServiceQueue, reconnectBase, reconnectMax),
|
||||
log: log,
|
||||
dispatchTable: make(map[string]func([]byte) ([]byte, error)),
|
||||
dispatchTable: make(map[string]messageHandler),
|
||||
maxConcurrentRPCServerRequests: maxConcurrentRPCServerRequests,
|
||||
clk: clock.Default(),
|
||||
stats: stats,
|
||||
|
|
@ -202,15 +157,27 @@ func NewAmqpRPCServer(amqpConf *cmd.AMQPConfig, maxConcurrentRPCServerRequests i
|
|||
}
|
||||
|
||||
// Handle registers a function to handle a particular method.
|
||||
func (rpc *AmqpRPCServer) Handle(method string, handler func([]byte) ([]byte, error)) {
|
||||
func (rpc *AmqpRPCServer) Handle(method string, handler messageHandler) {
|
||||
rpc.mu.Lock()
|
||||
rpc.dispatchTable[method] = handler
|
||||
rpc.mu.Unlock()
|
||||
}
|
||||
|
||||
// HandleDeliveries allows a server to receive amqp.Delivery directly (e.g.
|
||||
// ActivityMonitor), it can provide one of these. Otherwise processMessage is
|
||||
// used by default.
|
||||
func (rpc *AmqpRPCServer) HandleDeliveries(handler DeliveryHandler) {
|
||||
rpc.mu.Lock()
|
||||
rpc.handleDelivery = handler
|
||||
rpc.mu.Unlock()
|
||||
}
|
||||
|
||||
// rpcError is a JSON wrapper for error as it cannot be un/marshalled
|
||||
// due to type interface{}.
|
||||
type rpcError struct {
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type,omitempty"`
|
||||
HTTPStatus int `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// Wraps a error in a rpcError so it can be marshalled to
|
||||
|
|
@ -245,10 +212,10 @@ func wrapError(err error) *rpcError {
|
|||
wrapped.Type = "RateLimitedError"
|
||||
case core.ServiceUnavailableError:
|
||||
wrapped.Type = "ServiceUnavailableError"
|
||||
case *core.ProblemDetails:
|
||||
case *probs.ProblemDetails:
|
||||
wrapped.Type = string(terr.Type)
|
||||
wrapped.Value = terr.Detail
|
||||
|
||||
wrapped.HTTPStatus = terr.HTTPStatus
|
||||
}
|
||||
return wrapped
|
||||
}
|
||||
|
|
@ -285,9 +252,10 @@ func unwrapError(rpcError *rpcError) error {
|
|||
return core.ServiceUnavailableError(rpcError.Value)
|
||||
default:
|
||||
if strings.HasPrefix(rpcError.Type, "urn:") {
|
||||
return &core.ProblemDetails{
|
||||
Type: core.ProblemType(rpcError.Type),
|
||||
Detail: rpcError.Value,
|
||||
return &probs.ProblemDetails{
|
||||
Type: probs.ProblemType(rpcError.Type),
|
||||
Detail: rpcError.Value,
|
||||
HTTPStatus: rpcError.HTTPStatus,
|
||||
}
|
||||
}
|
||||
return errors.New(rpcError.Value)
|
||||
|
|
@ -319,11 +287,11 @@ func (r rpcResponse) debugString() string {
|
|||
if r.Error == nil {
|
||||
return ret
|
||||
}
|
||||
return fmt.Sprintf("%s, RPCERR: %s", ret, r.Error)
|
||||
return fmt.Sprintf("%s, RPCERR: %v", ret, r.Error)
|
||||
}
|
||||
|
||||
// AmqpChannel sets a AMQP connection up using SSL if configuration is provided
|
||||
func AmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
|
||||
// makeAmqpChannel sets a AMQP connection up using SSL if configuration is provided
|
||||
func makeAmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
|
||||
var conn *amqp.Connection
|
||||
var err error
|
||||
|
||||
|
|
@ -387,11 +355,6 @@ func AmqpChannel(conf *cmd.AMQPConfig) (*amqp.Channel, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = amqpDeclareExchange(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.Channel()
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +435,11 @@ func (rpc *AmqpRPCServer) Start(c *cmd.AMQPConfig) error {
|
|||
atomic.AddInt64(&rpc.currentGoroutines, 1)
|
||||
defer atomic.AddInt64(&rpc.currentGoroutines, -1)
|
||||
startedProcessing := rpc.clk.Now()
|
||||
rpc.processMessage(msg)
|
||||
if rpc.handleDelivery != nil {
|
||||
rpc.handleDelivery(msg)
|
||||
} else {
|
||||
rpc.processMessage(msg)
|
||||
}
|
||||
rpc.stats.TimingDuration(fmt.Sprintf("RPC.ServerProcessingLatency.%s", msg.Type), time.Since(startedProcessing), 1.0)
|
||||
}()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
|
|
@ -43,17 +44,19 @@ func TestWrapError(t *testing.T) {
|
|||
expected error
|
||||
}{
|
||||
{
|
||||
&core.ProblemDetails{
|
||||
Type: core.ConnectionProblem,
|
||||
Detail: "whoops",
|
||||
&probs.ProblemDetails{
|
||||
Type: probs.ConnectionProblem,
|
||||
Detail: "whoops",
|
||||
HTTPStatus: 417,
|
||||
},
|
||||
&core.ProblemDetails{
|
||||
Type: core.ConnectionProblem,
|
||||
Detail: "whoops",
|
||||
&probs.ProblemDetails{
|
||||
Type: probs.ConnectionProblem,
|
||||
Detail: "whoops",
|
||||
HTTPStatus: 417,
|
||||
},
|
||||
},
|
||||
{
|
||||
&core.ProblemDetails{Type: "invalid", Detail: "hm"},
|
||||
&probs.ProblemDetails{Type: "invalid", Detail: "hm"},
|
||||
errors.New("hm"),
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type channelMaker interface {
|
|||
type defaultChannelMaker struct{}
|
||||
|
||||
func (d defaultChannelMaker) makeChannel(conf *cmd.AMQPConfig) (amqpChannel, error) {
|
||||
return AmqpChannel(conf)
|
||||
return makeAmqpChannel(conf)
|
||||
}
|
||||
|
||||
// amqpConnector encapsulates an AMQP channel and a subscription to a specific
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ type Client interface {
|
|||
|
||||
// Server describes the functions an RPC Server performs
|
||||
type Server interface {
|
||||
Handle(string, func([]byte) ([]byte, error))
|
||||
Handle(string, messageHandler)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
)
|
||||
|
||||
var mediumBlobSize = int(math.Pow(2, 24))
|
||||
|
|
@ -170,7 +171,7 @@ func modelToChallenge(cm *challModel) (core.Challenge, error) {
|
|||
c.KeyAuthorization = &ka
|
||||
}
|
||||
if len(cm.Error) > 0 {
|
||||
var problem core.ProblemDetails
|
||||
var problem probs.ProblemDetails
|
||||
err := json.Unmarshal(cm.Error, &problem)
|
||||
if err != nil {
|
||||
return core.Challenge{}, err
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"issuer_urls": [
|
||||
"http://127.0.0.1:4000/acme/issuer-cert"
|
||||
],
|
||||
"ocsp_url": "http://127.0.0.1:4002/ocsp",
|
||||
"ocsp_url": "http://127.0.0.1:4002/",
|
||||
"crl_url": "http://example.com/crl",
|
||||
"policies": [
|
||||
{
|
||||
|
|
@ -243,6 +243,7 @@
|
|||
"activityMonitor": {
|
||||
"debugAddr": "localhost:8007",
|
||||
"amqp": {
|
||||
"serviceQueue": "Monitor",
|
||||
"server": "amqp://guest:guest@localhost:5673",
|
||||
"insecure": true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ def start(race_detection):
|
|||
t.daemon = True
|
||||
t.start()
|
||||
progs = [
|
||||
'activity-monitor',
|
||||
'boulder-wfe',
|
||||
'boulder-ra',
|
||||
'boulder-sa',
|
||||
|
|
|
|||
|
|
@ -1,16 +1,30 @@
|
|||
#!/bin/bash
|
||||
set -o xtrace
|
||||
|
||||
# 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
|
||||
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=""
|
||||
fi
|
||||
|
||||
travis_retry go get \
|
||||
|
|
@ -27,17 +41,10 @@ travis_retry go get \
|
|||
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
|
||||
|
||||
# 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
|
||||
|
||||
set +o xtrace
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/net/publicsuffix"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
bdns "github.com/letsencrypt/boulder/dns"
|
||||
|
|
@ -125,7 +126,7 @@ func verifyValidationJWS(validation *jose.JsonWebSignature, accountKey *jose.Jso
|
|||
// This is the same choice made by the Go internal resolution library used by
|
||||
// net/http, except we only send A queries and accept IPv4 addresses.
|
||||
// TODO(#593): Add IPv6 support
|
||||
func (va ValidationAuthorityImpl) getAddr(hostname string) (addr net.IP, addrs []net.IP, problem *core.ProblemDetails) {
|
||||
func (va ValidationAuthorityImpl) getAddr(hostname string) (addr net.IP, addrs []net.IP, problem *probs.ProblemDetails) {
|
||||
addrs, rtt, err := va.DNSResolver.LookupHost(hostname)
|
||||
if err != nil {
|
||||
problem = bdns.ProblemDetailsFromDNSError(err)
|
||||
|
|
@ -136,8 +137,8 @@ func (va ValidationAuthorityImpl) getAddr(hostname string) (addr net.IP, addrs [
|
|||
va.stats.Inc("VA.DNS.Rate", 1, 1.0)
|
||||
|
||||
if len(addrs) == 0 {
|
||||
problem = &core.ProblemDetails{
|
||||
Type: core.UnknownHostProblem,
|
||||
problem = &probs.ProblemDetails{
|
||||
Type: probs.UnknownHostProblem,
|
||||
Detail: fmt.Sprintf("No IPv4 addresses found for %s", hostname),
|
||||
}
|
||||
return
|
||||
|
|
@ -158,7 +159,7 @@ func (d *dialer) Dial(_, _ string) (net.Conn, error) {
|
|||
|
||||
// resolveAndConstructDialer gets the prefered address using va.getAddr and returns
|
||||
// the chosen address and dialer for that address and correct port.
|
||||
func (va *ValidationAuthorityImpl) resolveAndConstructDialer(name string, port int) (dialer, *core.ProblemDetails) {
|
||||
func (va *ValidationAuthorityImpl) resolveAndConstructDialer(name string, port int) (dialer, *probs.ProblemDetails) {
|
||||
d := dialer{
|
||||
record: core.ValidationRecord{
|
||||
Hostname: name,
|
||||
|
|
@ -205,8 +206,8 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
va.log.Audit(fmt.Sprintf("Attempting to validate %s for %s", challenge.Type, url))
|
||||
httpRequest, err := http.NewRequest("GET", url.String(), nil)
|
||||
if err != nil {
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "URL provided for HTTP was invalid",
|
||||
}
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] HTTP failure: %s", challenge.Type, identifier, err))
|
||||
|
|
@ -297,7 +298,7 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
httpResponse, err := client.Do(httpRequest)
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: parseHTTPConnError(err),
|
||||
Detail: fmt.Sprintf("Could not connect to %s", url),
|
||||
}
|
||||
|
|
@ -307,8 +308,8 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
|
||||
if httpResponse.StatusCode != 200 {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: fmt.Sprintf("Invalid response from %s [%s]: %d",
|
||||
url.String(), dialer.record.AddressUsed, httpResponse.StatusCode),
|
||||
}
|
||||
|
|
@ -320,8 +321,8 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: fmt.Sprintf("Error reading HTTP response body: %v", err),
|
||||
}
|
||||
return emptyBody, challenge, err
|
||||
|
|
@ -359,7 +360,7 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
|
|||
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: parseHTTPConnError(err),
|
||||
Detail: "Failed to connect to host for DVSNI challenge",
|
||||
}
|
||||
|
|
@ -371,8 +372,8 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
|
|||
// Check that zName is a dNSName SAN in the server's certificate
|
||||
certs := conn.ConnectionState().PeerCertificates
|
||||
if len(certs) == 0 {
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: "No certs presented for TLS SNI challenge",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
|
@ -385,9 +386,10 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
|
|||
}
|
||||
}
|
||||
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
Detail: "Correct zName not found for TLS SNI challenge",
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
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
|
||||
|
|
@ -399,8 +401,8 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
|
|||
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for SimpleHTTP was not DNS",
|
||||
}
|
||||
|
||||
|
|
@ -424,8 +426,8 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
|
|||
err = fmt.Errorf("Validation response failed to parse as JWS: %s", err.Error())
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
}
|
||||
return challenge, err
|
||||
|
|
@ -444,8 +446,8 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
|
|||
if err != nil {
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
}
|
||||
return challenge, err
|
||||
|
|
@ -460,8 +462,8 @@ func (va *ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
|
|||
challenge := input
|
||||
|
||||
if identifier.Type != "dns" {
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for DVSNI was not DNS",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
|
@ -480,8 +482,8 @@ func (va *ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
|
|||
if err != nil {
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
}
|
||||
return challenge, err
|
||||
|
|
@ -502,8 +504,8 @@ func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier
|
|||
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for HTTP validation was not DNS",
|
||||
}
|
||||
|
||||
|
|
@ -526,8 +528,8 @@ func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier
|
|||
err = fmt.Errorf("Error parsing key authorization file: %s", err.Error())
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
}
|
||||
return challenge, err
|
||||
|
|
@ -539,8 +541,8 @@ func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier
|
|||
challenge.KeyAuthorization.String(), string(body))
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
}
|
||||
return challenge, err
|
||||
|
|
@ -554,8 +556,8 @@ func (va *ValidationAuthorityImpl) validateTLSSNI01(identifier core.AcmeIdentifi
|
|||
challenge := input
|
||||
|
||||
if identifier.Type != "dns" {
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for TLS-SNI was not DNS",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
|
@ -574,7 +576,7 @@ func (va *ValidationAuthorityImpl) validateTLSSNI01(identifier core.AcmeIdentifi
|
|||
|
||||
// parseHTTPConnError returns the ACME ProblemType corresponding to an error
|
||||
// that occurred during domain validation.
|
||||
func parseHTTPConnError(err error) core.ProblemType {
|
||||
func parseHTTPConnError(err error) probs.ProblemType {
|
||||
if urlErr, ok := err.(*url.Error); ok {
|
||||
err = urlErr.Err
|
||||
}
|
||||
|
|
@ -585,21 +587,21 @@ func parseHTTPConnError(err error) core.ProblemType {
|
|||
if netErr, ok := err.(*net.OpError); ok {
|
||||
dnsErr, ok := netErr.Err.(*net.DNSError)
|
||||
if ok && !dnsErr.Timeout() && !dnsErr.Temporary() {
|
||||
return core.UnknownHostProblem
|
||||
return probs.UnknownHostProblem
|
||||
} else if fmt.Sprintf("%T", netErr.Err) == "tls.alert" {
|
||||
return core.TLSProblem
|
||||
return probs.TLSProblem
|
||||
}
|
||||
}
|
||||
|
||||
return core.ConnectionProblem
|
||||
return probs.ConnectionProblem
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateDNS01(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.MalformedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for DNS was not itself DNS",
|
||||
}
|
||||
va.log.Debug(fmt.Sprintf("DNS [%s] Identifier failure", identifier))
|
||||
|
|
@ -632,15 +634,15 @@ func (va *ValidationAuthorityImpl) validateDNS01(identifier core.AcmeIdentifier,
|
|||
}
|
||||
}
|
||||
|
||||
challenge.Error = &core.ProblemDetails{
|
||||
Type: core.UnauthorizedProblem,
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: "Correct value not found for DNS challenge",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) checkCAA(identifier core.AcmeIdentifier, regID int64) *core.ProblemDetails {
|
||||
func (va *ValidationAuthorityImpl) checkCAA(identifier core.AcmeIdentifier, regID int64) *probs.ProblemDetails {
|
||||
// Check CAA records for the requested identifier
|
||||
present, valid, err := va.CheckCAARecords(identifier)
|
||||
if err != nil {
|
||||
|
|
@ -650,8 +652,8 @@ func (va *ValidationAuthorityImpl) checkCAA(identifier core.AcmeIdentifier, regI
|
|||
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
|
||||
va.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid))
|
||||
if !valid {
|
||||
return &core.ProblemDetails{
|
||||
Type: core.ConnectionProblem,
|
||||
return &probs.ProblemDetails{
|
||||
Type: probs.ConnectionProblem,
|
||||
Detail: "CAA check for identifier failed",
|
||||
}
|
||||
}
|
||||
|
|
@ -669,7 +671,7 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
if !authz.Challenges[challengeIndex].IsSane(true) {
|
||||
chall := &authz.Challenges[challengeIndex]
|
||||
chall.Status = core.StatusInvalid
|
||||
chall.Error = &core.ProblemDetails{Type: core.MalformedProblem,
|
||||
chall.Error = &probs.ProblemDetails{Type: probs.MalformedProblem,
|
||||
Detail: fmt.Sprintf("Challenge failed sanity check.")}
|
||||
logEvent.Challenge = *chall
|
||||
logEvent.Error = chall.Error.Detail
|
||||
|
|
@ -685,7 +687,7 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
} else if !authz.Challenges[challengeIndex].RecordsSane() {
|
||||
chall := &authz.Challenges[challengeIndex]
|
||||
chall.Status = core.StatusInvalid
|
||||
chall.Error = &core.ProblemDetails{Type: core.ServerInternalProblem,
|
||||
chall.Error = &probs.ProblemDetails{Type: probs.ServerInternalProblem,
|
||||
Detail: "Records for validation failed sanity check"}
|
||||
logEvent.Error = chall.Error.Detail
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/mocks"
|
||||
|
|
@ -303,7 +304,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
invalidChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
|
||||
va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default())
|
||||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
|
@ -319,7 +320,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found a 404 for the challenge.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1)
|
||||
|
||||
log.Clear()
|
||||
|
|
@ -329,7 +330,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found the wrong token value.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1)
|
||||
|
||||
log.Clear()
|
||||
|
|
@ -351,12 +352,12 @@ func TestSimpleHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateSimpleHTTP(ipIdentifier, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
|
||||
invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name is invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
|
||||
chall.Token = "wait-long"
|
||||
started := time.Now()
|
||||
|
|
@ -367,7 +368,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
// TODO(https://github.com/letsencrypt/boulder/issues/894): Remove this method
|
||||
|
|
@ -483,13 +484,13 @@ func TestDvsni(t *testing.T) {
|
|||
}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
|
||||
log.Clear()
|
||||
invalidChall, err = va.validateDvsni(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name was supposed to be invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
|
||||
// Need to re-sign to get an unknown SNI (from the signature value)
|
||||
chall.Token = core.NewToken()
|
||||
|
|
@ -509,7 +510,7 @@ func TestDvsni(t *testing.T) {
|
|||
test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1)
|
||||
|
||||
// Take down DVSNI validation server and check that validation fails.
|
||||
|
|
@ -517,7 +518,7 @@ func TestDvsni(t *testing.T) {
|
|||
invalidChall, err = va.validateDvsni(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func TestDVSNIWithTLSError(t *testing.T) {
|
||||
|
|
@ -533,7 +534,7 @@ func TestDVSNIWithTLSError(t *testing.T) {
|
|||
invalidChall, err := va.validateDvsni(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "What cert was used?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.TLSProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.TLSProblem)
|
||||
}
|
||||
|
||||
func httpSrv(t *testing.T, token string) *httptest.Server {
|
||||
|
|
@ -681,7 +682,7 @@ func TestHttp(t *testing.T) {
|
|||
invalidChall, err := va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
|
||||
va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default())
|
||||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
|
@ -698,7 +699,7 @@ func TestHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found a 404 for the challenge.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1)
|
||||
|
||||
log.Clear()
|
||||
|
|
@ -708,7 +709,7 @@ func TestHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found the wrong token value.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1)
|
||||
|
||||
log.Clear()
|
||||
|
|
@ -730,12 +731,12 @@ func TestHttp(t *testing.T) {
|
|||
invalidChall, err = va.validateHTTP01(ipIdentifier, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
|
||||
invalidChall, err = va.validateHTTP01(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name is invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
|
||||
setChallengeToken(&chall, pathWaitLong)
|
||||
started := time.Now()
|
||||
|
|
@ -746,7 +747,7 @@ func TestHttp(t *testing.T) {
|
|||
test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func TestHTTPRedirectLookup(t *testing.T) {
|
||||
|
|
@ -891,13 +892,13 @@ func TestTLSSNI(t *testing.T) {
|
|||
}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
|
||||
log.Clear()
|
||||
invalidChall, err = va.validateTLSSNI01(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name was supposed to be invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
|
||||
// Need to create a new authorized keys object to get an unknown SNI (from the signature value)
|
||||
chall.Token = core.NewToken()
|
||||
|
|
@ -913,7 +914,7 @@ func TestTLSSNI(t *testing.T) {
|
|||
test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1)
|
||||
|
||||
// Take down validation server and check that validation fails.
|
||||
|
|
@ -921,7 +922,7 @@ func TestTLSSNI(t *testing.T) {
|
|||
invalidChall, err = va.validateTLSSNI01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func brokenTLSSrv() *httptest.Server {
|
||||
|
|
@ -948,7 +949,7 @@ func TestTLSError(t *testing.T) {
|
|||
invalidChall, err := va.validateTLSSNI01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "What cert was used?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.TLSProblem)
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.TLSProblem)
|
||||
}
|
||||
|
||||
func TestValidateHTTP(t *testing.T) {
|
||||
|
|
@ -1096,8 +1097,8 @@ func TestCAATimeout(t *testing.T) {
|
|||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
va.IssuerDomain = "letsencrypt.org"
|
||||
err := va.checkCAA(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "caa-timeout.com"}, 101)
|
||||
if err.Type != core.ConnectionProblem {
|
||||
t.Errorf("Expected timeout error type %s, got %s", core.ConnectionProblem, err.Type)
|
||||
if err.Type != probs.ConnectionProblem {
|
||||
t.Errorf("Expected timeout error type %s, got %s", probs.ConnectionProblem, err.Type)
|
||||
}
|
||||
expected := "DNS query timed out"
|
||||
if err.Detail != expected {
|
||||
|
|
@ -1173,7 +1174,7 @@ func TestDNSValidationFailure(t *testing.T) {
|
|||
t.Logf("Resulting Authz: %+v", authz)
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, core.UnauthorizedProblem)
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.UnauthorizedProblem)
|
||||
}
|
||||
|
||||
func TestDNSValidationInvalid(t *testing.T) {
|
||||
|
|
@ -1201,7 +1202,7 @@ func TestDNSValidationInvalid(t *testing.T) {
|
|||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.MalformedProblem)
|
||||
}
|
||||
|
||||
func TestDNSValidationNotSane(t *testing.T) {
|
||||
|
|
@ -1231,7 +1232,7 @@ func TestDNSValidationNotSane(t *testing.T) {
|
|||
for i := 0; i < len(authz.Challenges); i++ {
|
||||
va.validate(authz, i)
|
||||
test.AssertEquals(t, authz.Challenges[i].Status, core.StatusInvalid)
|
||||
test.AssertEquals(t, authz.Challenges[i].Error.Type, core.MalformedProblem)
|
||||
test.AssertEquals(t, authz.Challenges[i].Error.Type, probs.MalformedProblem)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1258,7 +1259,7 @@ func TestDNSValidationServFail(t *testing.T) {
|
|||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func TestDNSValidationNoServer(t *testing.T) {
|
||||
|
|
@ -1280,7 +1281,7 @@ func TestDNSValidationNoServer(t *testing.T) {
|
|||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, core.ConnectionProblem)
|
||||
test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
// TestDNSValidationLive is an integration test, depending on
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/letsencrypt/go-jose"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
)
|
||||
|
||||
// Paths are the ACME-spec identified URL path-segments for various methods
|
||||
|
|
@ -486,12 +487,12 @@ func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Req
|
|||
|
||||
// Notify the client of an error condition and log it for audit purposes.
|
||||
func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, logEvent *requestEvent, msg string, detail error, code int) {
|
||||
problem := core.ProblemDetails{Detail: msg}
|
||||
problem := probs.ProblemDetails{Detail: msg, HTTPStatus: code}
|
||||
switch code {
|
||||
case http.StatusPreconditionFailed:
|
||||
fallthrough
|
||||
case http.StatusForbidden:
|
||||
problem.Type = core.UnauthorizedProblem
|
||||
problem.Type = probs.UnauthorizedProblem
|
||||
case http.StatusConflict:
|
||||
fallthrough
|
||||
case http.StatusMethodNotAllowed:
|
||||
|
|
@ -501,14 +502,15 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, logEvent *re
|
|||
case http.StatusBadRequest:
|
||||
fallthrough
|
||||
case http.StatusLengthRequired:
|
||||
problem.Type = core.MalformedProblem
|
||||
problem.Type = probs.MalformedProblem
|
||||
case StatusRateLimited:
|
||||
problem.Type = core.RateLimitedProblem
|
||||
problem.Type = probs.RateLimitedProblem
|
||||
case statusBadNonce:
|
||||
problem.Type = core.BadNonceProblem
|
||||
problem.Type = probs.BadNonceProblem
|
||||
problem.HTTPStatus = http.StatusBadRequest
|
||||
code = http.StatusBadRequest
|
||||
default: // Either http.StatusInternalServerError or an unexpected code
|
||||
problem.Type = core.ServerInternalProblem
|
||||
problem.Type = probs.ServerInternalProblem
|
||||
}
|
||||
|
||||
// Record details to the log event
|
||||
|
|
@ -516,7 +518,7 @@ func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, logEvent *re
|
|||
|
||||
// Only audit log internal errors so users cannot purposefully cause
|
||||
// auditable events.
|
||||
if problem.Type == core.ServerInternalProblem {
|
||||
if problem.Type == probs.ServerInternalProblem {
|
||||
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
|
||||
wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", msg, detail))
|
||||
} else if statusCodeFromError(detail) != http.StatusInternalServerError {
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ func TestHandleFunc(t *testing.T) {
|
|||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), sortHeader(strings.Join(addHeadIfGet(c.allowed), ", ")))
|
||||
test.AssertEquals(t,
|
||||
rw.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
}
|
||||
nonce := rw.Header().Get("Replay-Nonce")
|
||||
test.AssertNotEquals(t, nonce, lastNonce)
|
||||
|
|
@ -313,7 +313,7 @@ func TestHandleFunc(t *testing.T) {
|
|||
// Disallowed method returns error JSON in body
|
||||
runWrappedHandler(&http.Request{Method: "PUT"}, "GET", "POST")
|
||||
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.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, HEAD, POST")
|
||||
|
||||
// Disallowed method special case: response to HEAD has got no body
|
||||
|
|
@ -559,7 +559,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
|
||||
// POST, but no body.
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -571,14 +571,14 @@ func TestIssueCertificate(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST","status":400}`)
|
||||
|
||||
// POST, but body that isn't valid JWS
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewCertificate(newRequestEvent(), responseWriter, makePostRequest("hi"))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS","status":400}`)
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -586,7 +586,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
makePostRequest(signRequest(t, "foo", wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON","status":400}`)
|
||||
|
||||
// Valid, signed JWS body, payload is '{}'
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -595,7 +595,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
signRequest(t, "{}", wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload does not specify a resource"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload does not specify a resource","status":400}`)
|
||||
|
||||
// Valid, signed JWS body, payload is '{"resource":"new-cert"}'
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -603,7 +603,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
makePostRequest(signRequest(t, `{"resource":"new-cert"}`, wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Error unmarshaling certificate request"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Error unmarshaling certificate request","status":400}`)
|
||||
|
||||
// Valid, signed JWS body, payload has a invalid signature on CSR and no authorizations:
|
||||
// alias b64url="base64 -w0 | sed -e 's,+,-,g' -e 's,/,_,g'"
|
||||
|
|
@ -618,7 +618,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
}`, wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Invalid signature on CSR"}`)
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Invalid signature on CSR","status":403}`)
|
||||
|
||||
// Valid, signed JWS body, payload has a valid CSR but no authorizations:
|
||||
// openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=meep.com | b64url
|
||||
|
|
@ -631,7 +631,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
}`, wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Authorizations for these names not found or expired: meep.com"}`)
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Authorizations for these names not found or expired: meep.com","status":403}`)
|
||||
assertCsrLogged(t, mockLog)
|
||||
|
||||
mockLog.Clear()
|
||||
|
|
@ -737,7 +737,7 @@ func TestChallenge(t *testing.T) {
|
|||
signRequest(t, `{"resource":"challenge"}`, wfe.nonceService)))
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusNotFound)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Expired authorization"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Expired authorization","status":404}`)
|
||||
}
|
||||
|
||||
func TestBadNonce(t *testing.T) {
|
||||
|
|
@ -755,7 +755,7 @@ func TestBadNonce(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to sign body")
|
||||
wfe.NewRegistration(newRequestEvent(), responseWriter,
|
||||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:badNonce","detail":"Unable to read/verify body :: JWS has no anti-replay nonce"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:badNonce","detail":"Unable to read/verify body :: JWS has no anti-replay nonce","status":400}`)
|
||||
}
|
||||
|
||||
func TestNewRegistration(t *testing.T) {
|
||||
|
|
@ -787,7 +787,7 @@ func TestNewRegistration(t *testing.T) {
|
|||
Method: "GET",
|
||||
URL: mustParseURL(NewRegPath),
|
||||
},
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`,
|
||||
},
|
||||
|
||||
// POST, but no body.
|
||||
|
|
@ -799,19 +799,19 @@ func TestNewRegistration(t *testing.T) {
|
|||
"Content-Length": []string{"0"},
|
||||
},
|
||||
},
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST","status":400}`,
|
||||
},
|
||||
|
||||
// POST, but body that isn't valid JWS
|
||||
{
|
||||
makePostRequestWithPath(NewRegPath, "hi"),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS","status":400}`,
|
||||
},
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
{
|
||||
makePostRequestWithPath(NewRegPath, fooBody.FullSerialize()),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON","status":400}`,
|
||||
},
|
||||
|
||||
// Same signed body, but payload modified by one byte, breaking signature.
|
||||
|
|
@ -831,11 +831,11 @@ func TestNewRegistration(t *testing.T) {
|
|||
"signature": "RjUQ679fxJgeAJlxqgvDP_sfGZnJ-1RgWF2qmcbnBWljs6h1qp63pLnJOl13u81bP_bCSjaWkelGG8Ymx_X-aQ"
|
||||
}
|
||||
`),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: JWS verification error"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: JWS verification error","status":400}`,
|
||||
},
|
||||
{
|
||||
makePostRequestWithPath(NewRegPath, wrongAgreementBody.FullSerialize()),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Provided agreement URL [https://letsencrypt.org/im-bad] does not match current agreement URL [` + agreementURL + `]"}`,
|
||||
`{"type":"urn:acme:error:malformed","detail":"Provided agreement URL [https://letsencrypt.org/im-bad] does not match current agreement URL [` + agreementURL + `]","status":400}`,
|
||||
},
|
||||
}
|
||||
for _, rt := range regErrTests {
|
||||
|
|
@ -885,7 +885,7 @@ func TestNewRegistration(t *testing.T) {
|
|||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Registration key is already in use"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Registration key is already in use","status":409}`)
|
||||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Location"),
|
||||
"/acme/reg/1")
|
||||
|
|
@ -996,7 +996,7 @@ func TestRevokeCertificateWrongKey(t *testing.T) {
|
|||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 403)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it."}`)
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it.","status":403}`)
|
||||
}
|
||||
|
||||
// Valid revocation request for already-revoked cert
|
||||
|
|
@ -1037,7 +1037,7 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
|
|||
makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Code, 409)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Certificate already revoked"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Certificate already revoked","status":409}`)
|
||||
}
|
||||
|
||||
func TestAuthorization(t *testing.T) {
|
||||
|
|
@ -1052,7 +1052,7 @@ func TestAuthorization(t *testing.T) {
|
|||
Method: "GET",
|
||||
URL: mustParseURL(NewAuthzPath),
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
|
||||
// POST, but no body.
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -1062,12 +1062,12 @@ func TestAuthorization(t *testing.T) {
|
|||
"Content-Length": []string{"0"},
|
||||
},
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: No body on POST","status":400}`)
|
||||
|
||||
// POST, but body that isn't valid JWS
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter, makePostRequest("hi"))
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS","status":400}`)
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
responseWriter.Body.Reset()
|
||||
|
|
@ -1075,7 +1075,7 @@ func TestAuthorization(t *testing.T) {
|
|||
makePostRequest(signRequest(t, "foo", wfe.nonceService)))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Request payload did not parse as JSON","status":400}`)
|
||||
|
||||
// Same signed body, but payload modified by one byte, breaking signature.
|
||||
// should fail JWS verification.
|
||||
|
|
@ -1096,7 +1096,7 @@ func TestAuthorization(t *testing.T) {
|
|||
`))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: JWS verification error"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: JWS verification error","status":400}`)
|
||||
|
||||
responseWriter.Body.Reset()
|
||||
wfe.NewAuthorization(newRequestEvent(), responseWriter,
|
||||
|
|
@ -1124,7 +1124,7 @@ func TestAuthorization(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusNotFound)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Expired authorization"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Expired authorization","status":404}`)
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
|
|
@ -1150,7 +1150,7 @@ func TestRegistration(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
responseWriter.Body.Reset()
|
||||
|
||||
// Test GET proper entry returns 405
|
||||
|
|
@ -1160,14 +1160,14 @@ func TestRegistration(t *testing.T) {
|
|||
})
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Method not allowed","status":405}`)
|
||||
responseWriter.Body.Reset()
|
||||
|
||||
// Test POST invalid JSON
|
||||
wfe.Registration(newRequestEvent(), responseWriter, makePostRequestWithPath("/2", "invalid"))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Unable to read/verify body :: Parse error reading JWS","status":400}`)
|
||||
responseWriter.Body.Reset()
|
||||
|
||||
key, err := jose.LoadPrivateKey([]byte(test2KeyPrivatePEM))
|
||||
|
|
@ -1185,7 +1185,7 @@ func TestRegistration(t *testing.T) {
|
|||
makePostRequestWithPath("/2", result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"No registration exists matching provided key"}`)
|
||||
`{"type":"urn:acme:error:unauthorized","detail":"No registration exists matching provided key","status":403}`)
|
||||
responseWriter.Body.Reset()
|
||||
|
||||
key, err = jose.LoadPrivateKey([]byte(test1KeyPrivatePEM))
|
||||
|
|
@ -1204,7 +1204,7 @@ func TestRegistration(t *testing.T) {
|
|||
makePostRequestWithPath("/1", result.FullSerialize()))
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Provided agreement URL [https://letsencrypt.org/im-bad] does not match current agreement URL [`+agreementURL+`]"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Provided agreement URL [https://letsencrypt.org/im-bad] does not match current agreement URL [`+agreementURL+`]","status":400}`)
|
||||
responseWriter.Body.Reset()
|
||||
|
||||
// Test POST valid JSON with registration up in the mock (with correct agreement URL)
|
||||
|
|
@ -1290,7 +1290,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found","status":404}`)
|
||||
|
||||
reqlogs = mockLog.GetAllMatching(`Terminated request`)
|
||||
test.AssertEquals(t, len(reqlogs), 1)
|
||||
|
|
@ -1303,7 +1303,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found","status":404}`)
|
||||
|
||||
// Invalid serial, no cache
|
||||
responseWriter = httptest.NewRecorder()
|
||||
|
|
@ -1311,7 +1311,7 @@ func TestGetCertificate(t *testing.T) {
|
|||
mux.ServeHTTP(responseWriter, req)
|
||||
test.AssertEquals(t, responseWriter.Code, 404)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found","status":404}`)
|
||||
}
|
||||
|
||||
func assertCsrLogged(t *testing.T, mockLog *mocks.SyslogWriter) {
|
||||
|
|
@ -1397,7 +1397,7 @@ func TestBadKeyCSR(t *testing.T) {
|
|||
|
||||
test.AssertEquals(t,
|
||||
responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Invalid key in certificate request :: Key too small: 512"}`)
|
||||
`{"type":"urn:acme:error:malformed","detail":"Invalid key in certificate request :: Key too small: 512","status":400}`)
|
||||
}
|
||||
|
||||
func TestStatusCodeFromError(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue