Merge branch 'master' to wfe_context
This commit is contained in:
commit
fdc0e2cce9
|
@ -126,7 +126,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/net/publicsuffix",
|
||||
"Rev": "b1959d2ce5b815bfc7aa5ca2d2af62293eb4abd5"
|
||||
"Rev": "edf0080f92131f2a12f1c7763d74ae60e3ebf550"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/gorp.v1",
|
||||
|
|
|
@ -73,7 +73,7 @@ func getSuffix(domain string, icannOnly bool) (publicSuffix string, icann bool)
|
|||
s, suffix, wildcard := domain, len(domain), false
|
||||
var dot int
|
||||
loop:
|
||||
for ;; s = s[:dot] {
|
||||
for {
|
||||
dot = strings.LastIndex(s, ".")
|
||||
if wildcard {
|
||||
suffix = 1 + dot
|
||||
|
@ -91,6 +91,10 @@ loop:
|
|||
// If we're only interested in ICANN suffixes, ignore any matches that are
|
||||
// not ICANN.
|
||||
if icannOnly && !icann {
|
||||
if dot == -1 {
|
||||
break
|
||||
}
|
||||
s = s[:dot]
|
||||
continue
|
||||
}
|
||||
u >>= nodesBitsICANN
|
||||
|
@ -112,6 +116,7 @@ loop:
|
|||
if dot == -1 {
|
||||
break
|
||||
}
|
||||
s = s[:dot]
|
||||
}
|
||||
if icannOnly && suffix < len(domain) {
|
||||
icann = true
|
||||
|
|
|
@ -76,7 +76,7 @@ func main() {
|
|||
|
||||
go cmd.DebugServer(c.WFE.DebugAddr)
|
||||
|
||||
wfe, err := wfe.NewWebFrontEndImpl(stats)
|
||||
wfe, err := wfe.NewWebFrontEndImpl(stats, clock.Default())
|
||||
cmd.FailOnError(err, "Unable to create WFE")
|
||||
rac, sac, closeChan := setupWFE(c, auditlogger, stats)
|
||||
wfe.RA = &rac
|
||||
|
|
|
@ -8,6 +8,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -20,7 +21,6 @@ import (
|
|||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/facebookgo/httpdown"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/golang.org/x/crypto/ocsp"
|
||||
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
|
||||
"github.com/letsencrypt/boulder/metrics"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
|
@ -55,34 +55,41 @@ serialNumber field, since we will always query on it.
|
|||
|
||||
*/
|
||||
type DBSource struct {
|
||||
dbMap *gorp.DbMap
|
||||
dbMap dbSelector
|
||||
caKeyHash []byte
|
||||
log *blog.AuditLogger
|
||||
}
|
||||
|
||||
// Since the only thing we use from gorp is the SelectOne method on the
|
||||
// gorp.DbMap object, we just define the interface an interface with that method
|
||||
// instead of importing all of gorp. This also allows us to simulate MySQL failures
|
||||
// by mocking the interface.
|
||||
type dbSelector interface {
|
||||
SelectOne(holder interface{}, query string, args ...interface{}) error
|
||||
}
|
||||
|
||||
// NewSourceFromDatabase produces a DBSource representing the binding of a
|
||||
// given DB schema to a CA key.
|
||||
func NewSourceFromDatabase(dbMap *gorp.DbMap, caKeyHash []byte) (src *DBSource, err error) {
|
||||
src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash}
|
||||
func NewSourceFromDatabase(dbMap dbSelector, caKeyHash []byte, log *blog.AuditLogger) (src *DBSource, err error) {
|
||||
src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash, log: log}
|
||||
return
|
||||
}
|
||||
|
||||
// Response is called by the HTTP server to handle a new OCSP request.
|
||||
func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
||||
log := blog.GetAuditLogger()
|
||||
|
||||
// Check that this request is for the proper CA
|
||||
if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 {
|
||||
log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash)))
|
||||
src.log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash)))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
serialString := core.SerialToString(req.SerialNumber)
|
||||
log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString))
|
||||
src.log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString))
|
||||
|
||||
var response []byte
|
||||
defer func() {
|
||||
if len(response) != 0 {
|
||||
log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString))
|
||||
src.log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString))
|
||||
}
|
||||
}()
|
||||
// Note: we first check for an OCSP response in the certificateStatus table (
|
||||
|
@ -94,6 +101,9 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
|||
"SELECT ocspResponse FROM certificateStatus WHERE serial = :serial",
|
||||
map[string]interface{}{"serial": serialString},
|
||||
)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
src.log.Err(fmt.Sprintf("Failed to retrieve response from certificateStatus table: %s", err))
|
||||
}
|
||||
// TODO(#970): Delete this ocspResponses check once the table has been removed
|
||||
if len(response) == 0 {
|
||||
// Ignoring possible error, if response hasn't been filled, attempt to find
|
||||
|
@ -103,6 +113,9 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
|||
"SELECT response from ocspResponses WHERE serial = :serial ORDER BY id DESC LIMIT 1;",
|
||||
map[string]interface{}{"serial": serialString},
|
||||
)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
src.log.Err(fmt.Sprintf("Failed to retrieve response from ocspResponses table: %s", err))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false
|
||||
|
@ -111,14 +124,7 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
|||
return response, true
|
||||
}
|
||||
|
||||
func makeDBSource(dbConnect, issuerCert string, sqlDebug bool) (*DBSource, error) {
|
||||
// Configure DB
|
||||
dbMap, err := sa.NewDbMap(dbConnect)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not connect to database: %s", err)
|
||||
}
|
||||
sa.SetSQLDebug(dbMap, sqlDebug)
|
||||
|
||||
func makeDBSource(dbMap dbSelector, issuerCert string, log *blog.AuditLogger) (*DBSource, error) {
|
||||
// Load the CA's key so we can store its SubjectKey in the DB
|
||||
caCertDER, err := cmd.LoadCert(issuerCert)
|
||||
if err != nil {
|
||||
|
@ -133,7 +139,7 @@ func makeDBSource(dbConnect, issuerCert string, sqlDebug bool) (*DBSource, error
|
|||
}
|
||||
|
||||
// Construct source from DB
|
||||
return NewSourceFromDatabase(dbMap, caCert.SubjectKeyId)
|
||||
return NewSourceFromDatabase(dbMap, caCert.SubjectKeyId, log)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -163,7 +169,12 @@ func main() {
|
|||
|
||||
if url.Scheme == "mysql+tcp" {
|
||||
auditlogger.Info(fmt.Sprintf("Loading OCSP Database for CA Cert: %s", c.Common.IssuerCert))
|
||||
source, err = makeDBSource(config.Source, c.Common.IssuerCert, c.SQL.SQLDebug)
|
||||
dbMap, err := sa.NewDbMap(config.Source)
|
||||
cmd.FailOnError(err, "Could not connect to database")
|
||||
if c.SQL.SQLDebug {
|
||||
sa.SetSQLDebug(dbMap, true)
|
||||
}
|
||||
source, err = makeDBSource(dbMap, c.Common.IssuerCert, auditlogger)
|
||||
cmd.FailOnError(err, "Couldn't load OCSP DB")
|
||||
} else if url.Scheme == "file" {
|
||||
filename := url.Path
|
||||
|
|
|
@ -12,7 +12,10 @@ import (
|
|||
|
||||
cfocsp "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/mocks"
|
||||
"github.com/letsencrypt/boulder/sa"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
@ -62,11 +65,14 @@ func TestHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDBHandler(t *testing.T) {
|
||||
src, err := makeDBSource("mysql+tcp://ocsp_resp@localhost:3306/boulder_sa_test", "./testdata/test-ca.der.pem", false)
|
||||
dbMap, err := sa.NewDbMap("mysql+tcp://ocsp_resp@localhost:3306/boulder_sa_test")
|
||||
test.AssertNotError(t, err, "Could not connect to database")
|
||||
src, err := makeDBSource(dbMap, "./testdata/test-ca.der.pem", blog.GetAuditLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("makeDBSource: %s", err)
|
||||
}
|
||||
defer test.ResetSATestDatabase(t)
|
||||
|
||||
ocspResp, err := ocsp.ParseResponse(resp, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ocsp.ParseResponse: %s", err)
|
||||
|
@ -99,7 +105,31 @@ func TestDBHandler(t *testing.T) {
|
|||
if !bytes.Equal(w.Body.Bytes(), resp) {
|
||||
t.Errorf("Mismatched body: want %#v, got %#v", resp, w.Body.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// brokenSelector allows us to test what happens when gorp SelectOne statements
|
||||
// throw errors and satisfies the dbSelector interface
|
||||
type brokenSelector struct{}
|
||||
|
||||
func (bs brokenSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error {
|
||||
return fmt.Errorf("Failure!")
|
||||
}
|
||||
|
||||
func TestErrorLog(t *testing.T) {
|
||||
src, err := makeDBSource(brokenSelector{}, "./testdata/test-ca.der.pem", blog.GetAuditLogger())
|
||||
test.AssertNotError(t, err, "Failed to create broken dbMap")
|
||||
|
||||
src.log.SyslogWriter = mocks.NewSyslogWriter()
|
||||
mockLog := src.log.SyslogWriter.(*mocks.SyslogWriter)
|
||||
|
||||
ocspReq, err := ocsp.ParseRequest(req)
|
||||
test.AssertNotError(t, err, "Failed to parse OCSP request")
|
||||
|
||||
_, found := src.Response(ocspReq)
|
||||
test.Assert(t, !found, "Somehow found OCSP response")
|
||||
|
||||
test.AssertEquals(t, len(mockLog.GetAllMatching("Failed to retrieve response from certificateStatus table")), 1)
|
||||
test.AssertEquals(t, len(mockLog.GetAllMatching("Failed to retrieve response from ocspResponses table")), 1)
|
||||
}
|
||||
|
||||
func mustRead(path string) []byte {
|
||||
|
|
|
@ -227,7 +227,7 @@ func (updater *OCSPUpdater) generateRevokedResponse(status core.CertificateStatu
|
|||
return &status, nil
|
||||
}
|
||||
|
||||
func (updater *OCSPUpdater) storeResponse(status *core.CertificateStatus, statusGuard core.OCSPStatus) error {
|
||||
func (updater *OCSPUpdater) storeResponse(status *core.CertificateStatus) error {
|
||||
// Update the certificateStatus table with the new OCSP response, the status
|
||||
// WHERE is used make sure we don't overwrite a revoked response with a one
|
||||
// containing a 'good' status and that we don't do the inverse when the OCSP
|
||||
|
@ -240,7 +240,7 @@ func (updater *OCSPUpdater) storeResponse(status *core.CertificateStatus, status
|
|||
status.OCSPResponse,
|
||||
status.OCSPLastUpdated,
|
||||
status.Serial,
|
||||
string(statusGuard),
|
||||
string(status.Status),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ func (updater *OCSPUpdater) revokedCertificatesTick(batchSize int) {
|
|||
updater.stats.Inc("OCSP.Errors.RevokedResponseGeneration", 1, 1.0)
|
||||
continue
|
||||
}
|
||||
err = updater.storeResponse(meta, core.OCSPStatusRevoked)
|
||||
err = updater.storeResponse(meta)
|
||||
if err != nil {
|
||||
updater.stats.Inc("OCSP.Errors.StoreRevokedResponse", 1, 1.0)
|
||||
updater.log.AuditErr(fmt.Errorf("Failed to store OCSP response: %s", err))
|
||||
|
@ -309,7 +309,7 @@ func (updater *OCSPUpdater) generateOCSPResponses(statuses []core.CertificateSta
|
|||
continue
|
||||
}
|
||||
updater.stats.Inc("OCSP.GeneratedResponses", 1, 1.0)
|
||||
err = updater.storeResponse(meta, core.OCSPStatusGood)
|
||||
err = updater.storeResponse(meta)
|
||||
if err != nil {
|
||||
updater.log.AuditErr(fmt.Errorf("Failed to store OCSP response: %s", err))
|
||||
updater.stats.Inc("OCSP.Errors.StoreResponse", 1, 1.0)
|
||||
|
|
|
@ -93,12 +93,12 @@ func TestGenerateAndStoreOCSPResponse(t *testing.T) {
|
|||
|
||||
meta, err := updater.generateResponse(status)
|
||||
test.AssertNotError(t, err, "Couldn't generate OCSP response")
|
||||
err = updater.storeResponse(meta, core.OCSPStatusGood)
|
||||
err = updater.storeResponse(meta)
|
||||
test.AssertNotError(t, err, "Couldn't store certificate status")
|
||||
|
||||
secondMeta, err := updater.generateRevokedResponse(status)
|
||||
test.AssertNotError(t, err, "Couldn't generate revoked OCSP response")
|
||||
err = updater.storeResponse(secondMeta, core.OCSPStatusGood)
|
||||
err = updater.storeResponse(secondMeta)
|
||||
test.AssertNotError(t, err, "Couldn't store certificate status")
|
||||
|
||||
newStatus, err := sa.GetCertificateStatus(status.Serial)
|
||||
|
@ -152,7 +152,7 @@ func TestFindStaleOCSPResponses(t *testing.T) {
|
|||
|
||||
meta, err := updater.generateResponse(status)
|
||||
test.AssertNotError(t, err, "Couldn't generate OCSP response")
|
||||
err = updater.storeResponse(meta, core.OCSPStatusGood)
|
||||
err = updater.storeResponse(meta)
|
||||
test.AssertNotError(t, err, "Couldn't store OCSP response")
|
||||
|
||||
certs, err = updater.findStaleOCSPResponses(earliest, 10)
|
||||
|
@ -290,18 +290,27 @@ func TestStoreResponseGuard(t *testing.T) {
|
|||
status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
|
||||
test.AssertNotError(t, err, "Failed to get certificate status")
|
||||
|
||||
status.OCSPResponse = []byte{0}
|
||||
err = updater.storeResponse(&status, core.OCSPStatusRevoked)
|
||||
err = sa.MarkCertificateRevoked(core.SerialToString(parsedCert.SerialNumber), 0)
|
||||
test.AssertNotError(t, err, "Failed to revoked certificate")
|
||||
|
||||
// Attempt to update OCSP response where status.Status is good but stored status
|
||||
// is revoked, this should fail silently
|
||||
status.OCSPResponse = []byte{0, 1, 1}
|
||||
err = updater.storeResponse(&status)
|
||||
test.AssertNotError(t, err, "Failed to update certificate status")
|
||||
|
||||
// Make sure the OCSP response hasn't actually changed
|
||||
unchangedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
|
||||
test.AssertNotError(t, err, "Failed to get certificate status")
|
||||
test.AssertEquals(t, len(unchangedStatus.OCSPResponse), 0)
|
||||
|
||||
err = updater.storeResponse(&status, core.OCSPStatusGood)
|
||||
// Changing the status to the stored status should allow the update to occur
|
||||
status.Status = core.OCSPStatusRevoked
|
||||
err = updater.storeResponse(&status)
|
||||
test.AssertNotError(t, err, "Failed to updated certificate status")
|
||||
|
||||
// Make sure the OCSP response has been updated
|
||||
changedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber))
|
||||
test.AssertNotError(t, err, "Failed to get certificate status")
|
||||
test.AssertEquals(t, len(changedStatus.OCSPResponse), 1)
|
||||
test.AssertEquals(t, len(changedStatus.OCSPResponse), 3)
|
||||
}
|
||||
|
|
32
cmd/shell.go
32
cmd/shell.go
|
@ -375,25 +375,45 @@ func FailOnError(err error, msg string) {
|
|||
|
||||
// ProfileCmd runs forever, sending Go runtime statistics to StatsD.
|
||||
func ProfileCmd(profileName string, stats statsd.Statter) {
|
||||
var memoryStats runtime.MemStats
|
||||
prevNumGC := int64(0)
|
||||
c := time.Tick(1 * time.Second)
|
||||
for range c {
|
||||
var memoryStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memoryStats)
|
||||
|
||||
// Gather goroutine count
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Goroutines", profileName), int64(runtime.NumGoroutine()), 1.0)
|
||||
|
||||
// Gather various heap metrics
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Heap.Alloc", profileName), int64(memoryStats.HeapAlloc), 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Heap.Objects", profileName), int64(memoryStats.HeapObjects), 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Heap.Idle", profileName), int64(memoryStats.HeapIdle), 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Heap.InUse", profileName), int64(memoryStats.HeapInuse), 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Heap.Released", profileName), int64(memoryStats.HeapReleased), 1.0)
|
||||
|
||||
// Calculate average and last and convert from nanoseconds to milliseconds
|
||||
gcPauseAvg := (int64(memoryStats.PauseTotalNs) / int64(len(memoryStats.PauseNs))) / 1000000
|
||||
lastGC := int64(memoryStats.PauseNs[(memoryStats.NumGC+255)%256]) / 1000000
|
||||
stats.Timing(fmt.Sprintf("%s.Gostats.Gc.PauseAvg", profileName), gcPauseAvg, 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Gc.LastPauseLatency", profileName), lastGC, 1.0)
|
||||
// Gather various GC related metrics
|
||||
if memoryStats.NumGC > 0 {
|
||||
totalRecentGC := uint64(0)
|
||||
realBufSize := uint32(256)
|
||||
if memoryStats.NumGC < 256 {
|
||||
realBufSize = memoryStats.NumGC
|
||||
}
|
||||
for _, pause := range memoryStats.PauseNs {
|
||||
totalRecentGC += pause
|
||||
}
|
||||
gcPauseAvg := totalRecentGC / uint64(realBufSize)
|
||||
lastGC := memoryStats.PauseNs[(memoryStats.NumGC+255)%256]
|
||||
stats.Timing(fmt.Sprintf("%s.Gostats.Gc.PauseAvg", profileName), int64(gcPauseAvg), 1.0)
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Gc.LastPause", profileName), int64(lastGC), 1.0)
|
||||
}
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Gc.NextAt", profileName), int64(memoryStats.NextGC), 1.0)
|
||||
// Send both a counter and a gauge here we can much more easily observe
|
||||
// the GC rate (versus the raw number of GCs) in graphing tools that don't
|
||||
// like deltas
|
||||
stats.Gauge(fmt.Sprintf("%s.Gostats.Gc.Count", profileName), int64(memoryStats.NumGC), 1.0)
|
||||
gcInc := int64(memoryStats.NumGC) - prevNumGC
|
||||
stats.Inc(fmt.Sprintf("%s.Gostats.Gc.Rate", profileName), gcInc, 1.0)
|
||||
prevNumGC += gcInc
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,16 +97,19 @@ This list is split up into metric topics with the names of the clients that subm
|
|||
* Client performance profiling (`cmd/boulder-*`)
|
||||
|
||||
```
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Goroutines
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Alloc
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Objects
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Idle
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.InUse
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Released
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Gc.NextAt
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Gc.LastPauseLatency
|
||||
[counter] Boulder.{cmd-name}.Gostats.Gc.Rate
|
||||
|
||||
[timing] Boulder.{cmd-name}.Gostats.Gc.PauseAvg
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Goroutines
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Alloc
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Objects
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Idle
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.InUse
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Heap.Released
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Gc.NextAt
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Gc.Count
|
||||
[gauge] Boulder.{cmd-name}.Gostats.Gc.LastPause
|
||||
|
||||
[timing] Boulder.{cmd-name}.Gostats.Gc.PauseAvg
|
||||
```
|
||||
|
||||
* External certificate store loading (`cmd/external-cert-importer`)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"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/Godeps/_workspace/src/github.com/miekg/dns"
|
||||
|
||||
|
@ -134,9 +135,16 @@ func (mock *DNSResolver) LookupMX(domain string) ([]string, time.Duration, error
|
|||
|
||||
// StorageAuthority is a mock
|
||||
type StorageAuthority struct {
|
||||
clk clock.Clock
|
||||
authorizedDomains map[string]bool
|
||||
}
|
||||
|
||||
// NewStorageAuthority creates a new mock storage authority
|
||||
// with the given clock.
|
||||
func NewStorageAuthority(clk clock.Clock) *StorageAuthority {
|
||||
return &StorageAuthority{clk: clk}
|
||||
}
|
||||
|
||||
const (
|
||||
test1KeyPublicJSON = `
|
||||
{
|
||||
|
@ -198,24 +206,32 @@ func (sa *StorageAuthority) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Regi
|
|||
|
||||
// GetAuthorization is a mock
|
||||
func (sa *StorageAuthority) GetAuthorization(id string) (core.Authorization, error) {
|
||||
if id == "valid" {
|
||||
exp := time.Now().AddDate(100, 0, 0)
|
||||
return core.Authorization{
|
||||
ID: "valid",
|
||||
Status: core.StatusValid,
|
||||
RegistrationID: 1,
|
||||
Expires: &exp,
|
||||
Identifier: core.AcmeIdentifier{Type: "dns", Value: "not-an-example.com"},
|
||||
Challenges: []core.Challenge{
|
||||
core.Challenge{
|
||||
ID: 23,
|
||||
Type: "dns",
|
||||
URI: "http://localhost:4300/acme/challenge/valid/23",
|
||||
},
|
||||
authz := core.Authorization{
|
||||
ID: "valid",
|
||||
Status: core.StatusValid,
|
||||
RegistrationID: 1,
|
||||
Identifier: core.AcmeIdentifier{Type: "dns", Value: "not-an-example.com"},
|
||||
Challenges: []core.Challenge{
|
||||
core.Challenge{
|
||||
ID: 23,
|
||||
Type: "dns",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
return core.Authorization{}, nil
|
||||
|
||||
if id == "valid" {
|
||||
exp := sa.clk.Now().AddDate(100, 0, 0)
|
||||
authz.Expires = &exp
|
||||
authz.Challenges[0].URI = "http://localhost:4300/acme/challenge/valid/23"
|
||||
return authz, nil
|
||||
} else if id == "expired" {
|
||||
exp := sa.clk.Now().AddDate(0, -1, 0)
|
||||
authz.Expires = &exp
|
||||
authz.Challenges[0].URI = "http://localhost:4300/acme/challenge/expired/23"
|
||||
return authz, nil
|
||||
}
|
||||
|
||||
return core.Authorization{}, fmt.Errorf("authz not found")
|
||||
}
|
||||
|
||||
// GetCertificate is a mock
|
||||
|
@ -318,7 +334,7 @@ func (sa *StorageAuthority) AddSCTReceipt(sct core.SignedCertificateTimestamp) (
|
|||
func (sa *StorageAuthority) GetLatestValidAuthorization(registrationID int64, identifier core.AcmeIdentifier) (authz core.Authorization, err error) {
|
||||
if registrationID == 1 && identifier.Type == "dns" {
|
||||
if sa.authorizedDomains[identifier.Value] || identifier.Value == "not-an-example.com" {
|
||||
exp := time.Now().AddDate(100, 0, 0)
|
||||
exp := sa.clk.Now().AddDate(100, 0, 0)
|
||||
return core.Authorization{Status: core.StatusValid, RegistrationID: 1, Expires: &exp, Identifier: identifier}, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/jmhodges/clock"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/mocks"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
|
@ -196,7 +198,7 @@ func setup(t *testing.T, port, retries int) (PublisherImpl, *x509.Certificate) {
|
|||
})
|
||||
test.AssertNotError(t, err, "Couldn't create new Publisher")
|
||||
pub.issuerBundle = append(pub.issuerBundle, base64.StdEncoding.EncodeToString(intermediatePEM.Bytes))
|
||||
pub.SA = &mocks.StorageAuthority{}
|
||||
pub.SA = mocks.NewStorageAuthority(clock.NewFake())
|
||||
|
||||
leafPEM, _ := pem.Decode([]byte(testLeaf))
|
||||
leaf, err := x509.ParseCertificate(leafPEM.Bytes)
|
||||
|
|
|
@ -32,6 +32,11 @@ import (
|
|||
// TODO(jsha): Read from a config file.
|
||||
const DefaultAuthorizationLifetime = 300 * 24 * time.Hour
|
||||
|
||||
// DefaultPendingAuthorizationLifetime is one week. If you can't respond to a
|
||||
// challenge this quickly, then you need to request a new challenge.
|
||||
// TODO(rlb): Read from a config file
|
||||
const DefaultPendingAuthorizationLifetime = 7 * 24 * time.Hour
|
||||
|
||||
// RegistrationAuthorityImpl defines an RA.
|
||||
//
|
||||
// NOTE: All of the fields in RegistrationAuthorityImpl need to be
|
||||
|
@ -46,12 +51,13 @@ type RegistrationAuthorityImpl struct {
|
|||
clk clock.Clock
|
||||
log *blog.AuditLogger
|
||||
// How long before a newly created authorization expires.
|
||||
authorizationLifetime time.Duration
|
||||
rlPolicies cmd.RateLimitConfig
|
||||
tiMu *sync.RWMutex
|
||||
totalIssuedCache int
|
||||
lastIssuedCount *time.Time
|
||||
maxContactsPerReg int
|
||||
authorizationLifetime time.Duration
|
||||
pendingAuthorizationLifetime time.Duration
|
||||
rlPolicies cmd.RateLimitConfig
|
||||
tiMu *sync.RWMutex
|
||||
totalIssuedCache int
|
||||
lastIssuedCount *time.Time
|
||||
maxContactsPerReg int
|
||||
}
|
||||
|
||||
// NewRegistrationAuthorityImpl constructs a new RA object.
|
||||
|
@ -60,10 +66,11 @@ func NewRegistrationAuthorityImpl(clk clock.Clock, logger *blog.AuditLogger, sta
|
|||
stats: stats,
|
||||
clk: clk,
|
||||
log: logger,
|
||||
authorizationLifetime: DefaultAuthorizationLifetime,
|
||||
rlPolicies: policies,
|
||||
tiMu: new(sync.RWMutex),
|
||||
maxContactsPerReg: maxContactsPerReg,
|
||||
authorizationLifetime: DefaultAuthorizationLifetime,
|
||||
pendingAuthorizationLifetime: DefaultPendingAuthorizationLifetime,
|
||||
rlPolicies: policies,
|
||||
tiMu: new(sync.RWMutex),
|
||||
maxContactsPerReg: maxContactsPerReg,
|
||||
}
|
||||
return ra
|
||||
}
|
||||
|
@ -275,7 +282,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
// Create validations. The WFE will update them with URIs before sending them out.
|
||||
challenges, combinations, err := ra.PA.ChallengesFor(identifier, ®.Key)
|
||||
|
||||
expires := ra.clk.Now().Add(ra.authorizationLifetime)
|
||||
expires := ra.clk.Now().Add(ra.pendingAuthorizationLifetime)
|
||||
|
||||
// Partially-filled object
|
||||
authz = core.Authorization{
|
||||
|
@ -284,8 +291,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
Status: core.StatusPending,
|
||||
Combinations: combinations,
|
||||
Challenges: challenges,
|
||||
// TODO(jsha): Pending authz should expire earlier than finalized authz.
|
||||
Expires: &expires,
|
||||
Expires: &expires,
|
||||
}
|
||||
|
||||
// Get a pending Auth first so we can get our ID back, then update with challenges
|
||||
|
@ -623,6 +629,12 @@ func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration,
|
|||
|
||||
// UpdateAuthorization updates an authorization with new values.
|
||||
func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) {
|
||||
// Refuse to update expired authorizations
|
||||
if base.Expires == nil || base.Expires.Before(ra.clk.Now()) {
|
||||
err = core.NotFoundError("Expired authorization")
|
||||
return
|
||||
}
|
||||
|
||||
// Copy information over that the client is allowed to supply
|
||||
authz = base
|
||||
if challengeIndex >= len(authz.Challenges) {
|
||||
|
|
|
@ -460,6 +460,22 @@ func TestUpdateAuthorization(t *testing.T) {
|
|||
t.Log("DONE TestUpdateAuthorization")
|
||||
}
|
||||
|
||||
func TestUpdateAuthorizationExpired(t *testing.T) {
|
||||
_, _, ra, fc, cleanUp := initAuthorities(t)
|
||||
defer cleanUp()
|
||||
|
||||
authz, err := ra.NewAuthorization(AuthzRequest, Registration.ID)
|
||||
test.AssertNotError(t, err, "NewAuthorization failed")
|
||||
|
||||
expiry := fc.Now().Add(-2 * time.Hour)
|
||||
authz.Expires = &expiry
|
||||
|
||||
response, err := makeResponse(authz.Challenges[ResponseIndex])
|
||||
|
||||
authz, err = ra.UpdateAuthorization(authz, ResponseIndex, response)
|
||||
test.AssertError(t, err, "Updated expired authorization")
|
||||
}
|
||||
|
||||
func TestUpdateAuthorizationReject(t *testing.T) {
|
||||
_, sa, ra, _, cleanUp := initAuthorities(t)
|
||||
defer cleanUp()
|
||||
|
|
|
@ -69,7 +69,7 @@ def get_ocsp(cert_file, url):
|
|||
with open(ocsp_resp_file, "w") as f:
|
||||
f.write(get_response)
|
||||
|
||||
ocsp_verify_cmd = "%s -CAfile ../test-ca.pem -respin %s" % (openssl_ocsp, ocsp_resp_file)
|
||||
ocsp_verify_cmd = "%s -CAfile ../test-root.pem -respin %s" % (openssl_ocsp, ocsp_resp_file)
|
||||
print ocsp_verify_cmd
|
||||
try:
|
||||
output = subprocess.check_output(ocsp_verify_cmd, shell=True)
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIJAJzxkS6o1QkIMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV
|
||||
BAMMFGhhcHB5IGhhY2tlciBmYWtlIENBMB4XDTE1MDQwNzIzNTAzOFoXDTI1MDQw
|
||||
NDIzNTAzOFowHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCCkd5mgXFErJ3F2M0E9dw+Ta/md5i
|
||||
8TDId01HberAApqmydG7UZYF3zLTSzNjlNSOmtybvrSGUnZ9r9tSQcL8VM6WUOM8
|
||||
tnIpiIjEA2QkBycMwvRmZ/B2ltPdYs/R9BqNwO1g18GDZrHSzUYtNKNeFI6Glamj
|
||||
7GK2Vr0SmiEamlNIR5ktAFsEErzf/d4jCF7sosMsJpMCm1p58QkP4LHLShVLXDa8
|
||||
BMfVoI+ipYcA08iNUFkgW8VWDclIDxcysa0psDDtMjX3+4aPkE/cefmP+1xOfUuD
|
||||
HOGV8XFynsP4EpTfVOZr0/g9gYQ7ZArqXX7GTQkFqduwPm/w5qxSPTarAgMBAAGj
|
||||
UDBOMB0GA1UdDgQWBBT7eE8S+WAVgyyfF380GbMuNupBiTAfBgNVHSMEGDAWgBT7
|
||||
eE8S+WAVgyyfF380GbMuNupBiTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAd9Da+Zv+TjMv7NTAmliqnWHY6d3UxEZN3hFEJ58IQVHbBZVZdW7zhRktB
|
||||
vR05Kweac0HJeK91TKmzvXl21IXLvh0gcNLU/uweD3no/snfdB4OoFompljThmgl
|
||||
zBqiqWoKBJQrLCA8w5UB+ReomRYd/EYXF/6TAfzm6hr//Xt5mPiUHPdvYt75lMAo
|
||||
vRxLSbF8TSQ6b7BYxISWjPgFASNNqJNHEItWsmQMtAjjwzb9cs01XH9pChVAWn9L
|
||||
oeMKa+SlHSYrWG93+EcrIH/dGU76uNOiaDzBSKvaehG53h25MHuO1anNICJvZovW
|
||||
rFo4Uv1EnkKJm3vJFe50eJGhEKlx
|
||||
MIIEHDCCAwSgAwIBAgICEl4wDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgY2Fj
|
||||
a2xpbmcgY3J5cHRvZ3JhcGhlciBmYWtlIFJPT1QwHhcNMTUxMDIwMjAzMTQyWhcN
|
||||
MjAxMDE4MjAzMTQyWjAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIKR3maBcUSsncXYzQT13D5
|
||||
Nr+Z3mLxMMh3TUdt6sACmqbJ0btRlgXfMtNLM2OU1I6a3Ju+tIZSdn2v21JBwvxU
|
||||
zpZQ4zy2cimIiMQDZCQHJwzC9GZn8HaW091iz9H0Go3A7WDXwYNmsdLNRi00o14U
|
||||
joaVqaPsYrZWvRKaIRqaU0hHmS0AWwQSvN/93iMIXuyiwywmkwKbWnnxCQ/gsctK
|
||||
FUtcNrwEx9Wgj6KlhwDTyI1QWSBbxVYNyUgPFzKxrSmwMO0yNff7ho+QT9x5+Y/7
|
||||
XE59S4Mc4ZXxcXKew/gSlN9U5mvT+D2BhDtkCupdfsZNCQWp27A+b/DmrFI9NqsC
|
||||
AwEAAaOCAVQwggFQMBIGA1UdEwEB/wQIMAYBAf8CAQAwQgYDVR0eBDswOaE3MAWC
|
||||
A21pbDAKhwgAAAAAAAAAADAihyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAADAOBgNVHQ8BAf8EBAMCAYYwMQYIKwYBBQUHAQEEJTAjMCEGCCsGAQUFBzAB
|
||||
hhVodHRwOi8vbG9jYWxob3N0OjQwMDMwHwYDVR0jBBgwFoAUz0nVEcH3uFO1Wlnl
|
||||
fuQ+5Dpx0EcwSwYDVR0gBEQwQjAIBgZngQwBAgEwNgYLKwYBBAGC3xMBAQEwJzAl
|
||||
BggrBgEFBQcCARYZaHR0cDovL2xvY2FsaG9zdDo0MDAwL2NwczAmBgNVHR8EHzAd
|
||||
MBugGaAXhhVodHRwOi8vbG9jYWxob3N0OjU1NTUwHQYDVR0OBBYEFPt4TxL5YBWD
|
||||
LJ8XfzQZsy426kGJMA0GCSqGSIb3DQEBCwUAA4IBAQCPKuy6CFUte+8CFWTm+jtP
|
||||
tnwLFnvznPGsF/GGb/JHRFJMvMoQ4ycvbOfY6jztQqmbf+R54pdPkpKxApQHvwcF
|
||||
Zd4IsgA39kQcQBQR8ToxsURrCMqTM5EXzXP/RFQHAf8aIcKuplxh1pjyJjbTEAx5
|
||||
n8W0HieqvWwy4JmBRI9N7KkpH4Xj5HBkesl4pGZOmt9kgYtN38jRX9BwJMM4IrXM
|
||||
EdHHgSwOW2BGr1PivJw3KYu20gw0ArPOLgc0M+3CCa8VipVVJwQks8HfXX58gd91
|
||||
kfobxltCCeRtKfeVWvF29TfxtHMxkwpZaGLxx+QKWytXqfj8kKF+tYl8PAE7DGJt
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGzCCAgOgAwIBAgIJANKZDvEMm9hrMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNV
|
||||
BAMMIGNhY2tsaW5nIGNyeXB0b2dyYXBoZXIgZmFrZSBST09UMB4XDTE1MTAyMDIw
|
||||
MzE0MloXDTI1MTAxNzIwMzE0MlowKzEpMCcGA1UEAwwgY2Fja2xpbmcgY3J5cHRv
|
||||
Z3JhcGhlciBmYWtlIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQCuiPhqvx+SJ1QQo+qFKJ05tDJ5IfOyxbtxuzQnC2JhHhB8PHonpUfYE/8Y96Pd
|
||||
MWg7+TDRwKYSe1p+Z1VtWk9WmrO2xNMUqJ0+wfSzkg7RIOYARBQNgpd/edGweCjc
|
||||
mpKe2m7SgBaMlmJWWoJtkYziejMGXnh2NIQStT0EDQTxIG42X5JVCOHl3vrYG/M8
|
||||
LkhU+1/Z3EwodMayh7XpAih33qr9pAaHLJYQ9I9q0GbXCBd7g50P6q1nc4DMOZxr
|
||||
lKbakL6OLKmk5iencbZIER0Mi2HR6W1aep/dlvOSXrrhfkdiuwrw9yRhG6VoCi8G
|
||||
ctwabow/+4NeRp8sB6I9zglZAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTPSdURwfe4U7VaWeV+5D7kOnHQRzANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAQOWe0K86JT5RsCQGFdPF5gx+YUTPLhsR8o+wnFc3R0GH
|
||||
mmVDz/FUfHg8RrpcIcc7Hn9PMpSChVJFxc0aOxGmetSQHOboieVq+wQ95fIgAkBE
|
||||
YtW0LvVCg1AqPwOZOLHWfrnhAz9YUkouBfpUQv+7P3XWsaiJEWdv9TgeknaUSxt/
|
||||
Md/tUGLQC7+LaFIss/28QQm0n19KPhQ9Tj/Fyb/SPPHhJ5WHAH52H/m9rQy6q7Ts
|
||||
8+ls03XFriP6QoU7c3Q+kW6TLw7mXGPccGHy8nY2GmBNwHA04fIVXTwok83wohnO
|
||||
ge17w48vRJ+gNCvr3V5ucNQlA/wbGS71nExCSGE5dA==
|
||||
-----END CERTIFICATE-----
|
|
@ -53,6 +53,7 @@ type WebFrontEndImpl struct {
|
|||
SA core.StorageGetter
|
||||
stats statsd.Statter
|
||||
log *blog.AuditLogger
|
||||
clk clock.Clock
|
||||
|
||||
// URL configuration parameters
|
||||
BaseURL string
|
||||
|
@ -117,7 +118,7 @@ func statusCodeFromError(err interface{}) int {
|
|||
}
|
||||
|
||||
// NewWebFrontEndImpl constructs a web service for Boulder
|
||||
func NewWebFrontEndImpl(stats statsd.Statter) (WebFrontEndImpl, error) {
|
||||
func NewWebFrontEndImpl(stats statsd.Statter, clk clock.Clock) (WebFrontEndImpl, error) {
|
||||
logger := blog.GetAuditLogger()
|
||||
logger.Notice("Web Front End Starting")
|
||||
|
||||
|
@ -128,6 +129,7 @@ func NewWebFrontEndImpl(stats statsd.Statter) (WebFrontEndImpl, error) {
|
|||
|
||||
return WebFrontEndImpl{
|
||||
log: logger,
|
||||
clk: clk,
|
||||
nonceService: nonceService,
|
||||
stats: stats,
|
||||
}, nil
|
||||
|
@ -881,6 +883,15 @@ func (wfe *WebFrontEndImpl) Challenge(
|
|||
notFound()
|
||||
return
|
||||
}
|
||||
|
||||
// After expiring, challenges are inaccessible
|
||||
if authz.Expires == nil || authz.Expires.Before(wfe.clk.Now()) {
|
||||
msg := fmt.Sprintf("Authorization %v expired in the past (%v)", authz.ID, *authz.Expires)
|
||||
logEvent.AddError(msg)
|
||||
wfe.sendError(response, "Expired authorization", msg, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Check that the requested challenge exists within the authorization
|
||||
challengeIndex := authz.FindChallenge(challengeID)
|
||||
if challengeIndex == -1 {
|
||||
|
@ -1130,6 +1141,14 @@ func (wfe *WebFrontEndImpl) Authorization(logEvent *requestEvent, response http.
|
|||
logEvent.Extra["AuthorizationStatus"] = authz.Status
|
||||
logEvent.Extra["AuthorizationExpires"] = authz.Expires
|
||||
|
||||
// After expiring, authorizations are inaccessible
|
||||
if authz.Expires == nil || authz.Expires.Before(wfe.clk.Now()) {
|
||||
msg := fmt.Sprintf("Authorization %v expired in the past (%v)", authz.ID, *authz.Expires)
|
||||
logEvent.AddError(msg)
|
||||
wfe.sendError(response, "Expired authorization", msg, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
wfe.prepAuthorizationForDisplay(&authz)
|
||||
|
||||
jsonReply, err := json.Marshal(authz)
|
||||
|
|
|
@ -197,9 +197,10 @@ func signRequest(t *testing.T, req string, nonceService *core.NonceService) stri
|
|||
return ret
|
||||
}
|
||||
|
||||
func setupWFE(t *testing.T) WebFrontEndImpl {
|
||||
func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock) {
|
||||
fc := clock.NewFake()
|
||||
stats, _ := statsd.NewNoopClient()
|
||||
wfe, err := NewWebFrontEndImpl(stats)
|
||||
wfe, err := NewWebFrontEndImpl(stats, fc)
|
||||
test.AssertNotError(t, err, "Unable to create WFE")
|
||||
|
||||
wfe.NewReg = wfe.BaseURL + NewRegPath
|
||||
|
@ -213,11 +214,10 @@ func setupWFE(t *testing.T) WebFrontEndImpl {
|
|||
wfe.log.SyslogWriter = mocks.NewSyslogWriter()
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.SA = mocks.NewStorageAuthority(fc)
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
|
||||
return wfe
|
||||
return wfe, fc
|
||||
}
|
||||
|
||||
// makePostRequest creates an http.Request with method POST, the provided body,
|
||||
|
@ -263,7 +263,7 @@ func addHeadIfGet(s []string) []string {
|
|||
}
|
||||
|
||||
func TestHandleFunc(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
var mux *http.ServeMux
|
||||
var rw *httptest.ResponseRecorder
|
||||
var stubCalled bool
|
||||
|
@ -460,7 +460,7 @@ func TestHandleFunc(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIndexPOST(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/")
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
|
@ -471,7 +471,7 @@ func TestIndexPOST(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPOST404(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
url, _ := url.Parse("/foobar")
|
||||
wfe.Index(newRequestEvent(), responseWriter, &http.Request{
|
||||
|
@ -482,7 +482,7 @@ func TestPOST404(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
wfe.IndexCacheDuration = time.Second * 10
|
||||
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
@ -510,7 +510,7 @@ func TestIndex(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDirectory(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
wfe.BaseURL = "http://localhost:4300"
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
@ -530,7 +530,7 @@ func TestDirectory(t *testing.T) {
|
|||
// TODO: Write additional test cases for:
|
||||
// - RA returns with a failure
|
||||
func TestIssueCertificate(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, fc := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
mockLog := wfe.log.SyslogWriter.(*mocks.SyslogWriter)
|
||||
|
@ -538,20 +538,18 @@ func TestIssueCertificate(t *testing.T) {
|
|||
// The mock CA we use always returns the same test certificate, with a Not
|
||||
// Before of 2015-09-22. Since we're currently using a real RA instead of a
|
||||
// mock (see below), that date would trigger failures for excessive
|
||||
// backdating. So we set the fakeClock's time to a time that matches that test
|
||||
// certificate.
|
||||
fakeClock := clock.NewFake()
|
||||
// backdating. So we set the fake clock's time to a time that matches that
|
||||
// test certificate.
|
||||
testTime := time.Date(2015, 9, 9, 22, 56, 0, 0, time.UTC)
|
||||
fakeClock.Add(fakeClock.Now().Sub(testTime))
|
||||
fc.Add(fc.Now().Sub(testTime))
|
||||
|
||||
// TODO: Use a mock RA so we can test various conditions of authorized, not
|
||||
// authorized, etc.
|
||||
stats, _ := statsd.NewNoopClient(nil)
|
||||
ra := ra.NewRegistrationAuthorityImpl(fakeClock, wfe.log, stats, cmd.RateLimitConfig{}, 0)
|
||||
ra.SA = &mocks.StorageAuthority{}
|
||||
ra := ra.NewRegistrationAuthorityImpl(fc, wfe.log, stats, cmd.RateLimitConfig{}, 0)
|
||||
ra.SA = mocks.NewStorageAuthority(fc)
|
||||
ra.CA = &MockCA{}
|
||||
ra.PA = &MockPA{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.RA = &ra
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
|
@ -668,10 +666,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetChallenge(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe, _ := setupWFE(t)
|
||||
|
||||
challengeURL := "/acme/challenge/valid/23"
|
||||
|
||||
|
@ -706,10 +701,7 @@ func TestGetChallenge(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestChallenge(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
var key jose.JsonWebKey
|
||||
|
@ -737,18 +729,23 @@ func TestChallenge(t *testing.T) {
|
|||
test.AssertEquals(
|
||||
t, responseWriter.Body.String(),
|
||||
`{"type":"dns","uri":"/acme/challenge/valid/23"}`)
|
||||
|
||||
// Expired challenges should be inaccessible
|
||||
challengeURL = "/acme/challenge/expired/23"
|
||||
responseWriter = httptest.NewRecorder()
|
||||
wfe.Challenge(newRequestEvent(), responseWriter,
|
||||
makePostRequestWithPath(challengeURL,
|
||||
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"}`)
|
||||
}
|
||||
|
||||
func TestNewRegistration(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
|
||||
key, err := jose.LoadPrivateKey([]byte(test2KeyPrivatePEM))
|
||||
test.AssertNotError(t, err, "Failed to load key")
|
||||
rsaKey, ok := key.(*rsa.PrivateKey)
|
||||
|
@ -918,7 +915,7 @@ func makeRevokeRequestJSON() ([]byte, error) {
|
|||
// registration when GetRegistrationByKey is called, and we want to get a
|
||||
// NoSuchRegistrationError for tests that pass regCheck = false to verifyPOST.
|
||||
type mockSANoSuchRegistration struct {
|
||||
mocks.StorageAuthority
|
||||
core.StorageGetter
|
||||
}
|
||||
|
||||
func (msa mockSANoSuchRegistration) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) {
|
||||
|
@ -940,8 +937,8 @@ func TestRevokeCertificateCertKey(t *testing.T) {
|
|||
revokeRequestJSON, err := makeRevokeRequestJSON()
|
||||
test.AssertNotError(t, err, "Failed to make revokeRequestJSON")
|
||||
|
||||
wfe := setupWFE(t)
|
||||
wfe.SA = &mockSANoSuchRegistration{mocks.StorageAuthority{}}
|
||||
wfe, fc := setupWFE(t)
|
||||
wfe.SA = &mockSANoSuchRegistration{mocks.NewStorageAuthority(fc)}
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
nonce, err := wfe.nonceService.Nonce()
|
||||
|
@ -959,7 +956,7 @@ func TestRevokeCertificateAccountKey(t *testing.T) {
|
|||
revokeRequestJSON, err := makeRevokeRequestJSON()
|
||||
test.AssertNotError(t, err, "Failed to make revokeRequestJSON")
|
||||
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
test1JWK, err := jose.LoadPrivateKey([]byte(test1KeyPrivatePEM))
|
||||
|
@ -979,7 +976,7 @@ func TestRevokeCertificateAccountKey(t *testing.T) {
|
|||
|
||||
// A revocation request signed by an unauthorized key.
|
||||
func TestRevokeCertificateWrongKey(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
nonce, err := wfe.nonceService.Nonce()
|
||||
test.AssertNotError(t, err, "Unable to create nonce")
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
@ -1028,12 +1025,10 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to marshal request")
|
||||
|
||||
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
|
||||
wfe := setupWFE(t)
|
||||
wfe, fc := setupWFE(t)
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mockSANoSuchRegistration{mocks.StorageAuthority{}}
|
||||
wfe.SA = &mockSANoSuchRegistration{mocks.NewStorageAuthority(fc)}
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
responseWriter := httptest.NewRecorder()
|
||||
responseWriter.Body.Reset()
|
||||
nonce, err := wfe.nonceService.Nonce()
|
||||
|
@ -1047,13 +1042,10 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAuthorization(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
// GET instead of POST should be rejected
|
||||
|
@ -1123,6 +1115,17 @@ func TestAuthorization(t *testing.T) {
|
|||
var authz core.Authorization
|
||||
err = json.Unmarshal([]byte(responseWriter.Body.String()), &authz)
|
||||
test.AssertNotError(t, err, "Couldn't unmarshal returned authorization object")
|
||||
|
||||
// Expired authorizations should be inaccessible
|
||||
authzURL := "/acme/authz/expired"
|
||||
responseWriter = httptest.NewRecorder()
|
||||
wfe.Authorization(newRequestEvent(), responseWriter, &http.Request{
|
||||
Method: "GET",
|
||||
URL: mustParseURL(authzURL),
|
||||
})
|
||||
test.AssertEquals(t, responseWriter.Code, http.StatusNotFound)
|
||||
test.AssertEquals(t, responseWriter.Body.String(),
|
||||
`{"type":"urn:acme:error:malformed","detail":"Expired authorization"}`)
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
|
@ -1135,14 +1138,9 @@ func contains(s []string, e string) bool {
|
|||
}
|
||||
|
||||
func TestRegistration(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
// Test invalid method
|
||||
|
@ -1228,13 +1226,7 @@ func TestRegistration(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTermsRedirect(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
|
||||
wfe.RA = &MockRegistrationAuthority{}
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
wfe.stats, _ = statsd.NewNoopClient()
|
||||
wfe.SubscriberAgreementURL = agreementURL
|
||||
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
path, _ := url.Parse("/terms")
|
||||
|
@ -1249,7 +1241,7 @@ func TestTermsRedirect(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIssuer(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
wfe.IssuerCacheDuration = time.Second * 10
|
||||
wfe.IssuerCert = []byte{0, 0, 1}
|
||||
|
||||
|
@ -1264,13 +1256,12 @@ func TestIssuer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetCertificate(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
|
||||
|
||||
wfe.CertCacheDuration = time.Second * 10
|
||||
wfe.CertNoCacheExpirationWindow = time.Hour * 24 * 7
|
||||
wfe.SA = &mocks.StorageAuthority{}
|
||||
|
||||
certPemBytes, _ := ioutil.ReadFile("test/178.crt")
|
||||
certBlock, _ := pem.Decode(certPemBytes)
|
||||
|
@ -1340,12 +1331,12 @@ func TestLogCsrPem(t *testing.T) {
|
|||
const certificateRequestJSON = `{
|
||||
"csr": "MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca-fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB-6dp48xkcX7Z_KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD-eeu8-z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7_C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT_DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq_Bbe7TBGVYZabUEh-LOskYwhgcOuThVN7tHnH5rhN-gb7cEdysjTb1QL-vOUwYgV75CB6PE5JVYK-cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub-fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G_W-Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd-L3IcyvHVKxNhIJXZVH0AOqh_1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY"
|
||||
}`
|
||||
wfe := setupWFE(t)
|
||||
wfe, fc := setupWFE(t)
|
||||
var certificateRequest core.CertificateRequest
|
||||
err := json.Unmarshal([]byte(certificateRequestJSON), &certificateRequest)
|
||||
test.AssertNotError(t, err, "Unable to parse certificateRequest")
|
||||
|
||||
mockSA := mocks.StorageAuthority{}
|
||||
mockSA := mocks.NewStorageAuthority(fc)
|
||||
reg, err := mockSA.GetRegistration(789)
|
||||
test.AssertNotError(t, err, "Unable to get registration")
|
||||
|
||||
|
@ -1363,7 +1354,7 @@ func TestLogCsrPem(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLengthRequired(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), &http.Request{
|
||||
Method: "POST",
|
||||
URL: mustParseURL("/"),
|
||||
|
@ -1374,7 +1365,7 @@ func TestLengthRequired(t *testing.T) {
|
|||
}
|
||||
|
||||
type mockSADifferentStoredKey struct {
|
||||
mocks.StorageAuthority
|
||||
core.StorageGetter
|
||||
}
|
||||
|
||||
func (sa mockSADifferentStoredKey) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) {
|
||||
|
@ -1388,8 +1379,8 @@ func (sa mockSADifferentStoredKey) GetRegistrationByKey(jwk jose.JsonWebKey) (co
|
|||
}
|
||||
|
||||
func TestVerifyPOSTUsesStoredKey(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe.SA = &mockSADifferentStoredKey{mocks.StorageAuthority{}}
|
||||
wfe, fc := setupWFE(t)
|
||||
wfe.SA = &mockSADifferentStoredKey{mocks.NewStorageAuthority(fc)}
|
||||
// signRequest signs with test1Key, but our special mock returns a
|
||||
// registration with test2Key
|
||||
_, _, _, err := wfe.verifyPOST(newRequestEvent(), makePostRequest(signRequest(t, `{"resource":"foo"}`, &wfe.nonceService)), true, "foo")
|
||||
|
@ -1397,7 +1388,7 @@ func TestVerifyPOSTUsesStoredKey(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBadKeyCSR(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
wfe, _ := setupWFE(t)
|
||||
responseWriter := httptest.NewRecorder()
|
||||
|
||||
// CSR with a bad (512 bit RSA) key.
|
||||
|
|
Loading…
Reference in New Issue