Merge branch 'master' into cert-limit
This commit is contained in:
commit
6e3f0e18c6
|
@ -27,6 +27,16 @@ import (
|
||||||
"github.com/letsencrypt/boulder/sa"
|
"github.com/letsencrypt/boulder/sa"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cacheCtrlHandler struct {
|
||||||
|
http.Handler
|
||||||
|
MaxAge time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cacheCtrlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", c.MaxAge/time.Second))
|
||||||
|
c.Handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
DBSource maps a given Database schema to a CA Key Hash, so we can pick
|
DBSource maps a given Database schema to a CA Key Hash, so we can pick
|
||||||
from among them when presented with OCSP requests for different certs.
|
from among them when presented with OCSP requests for different certs.
|
||||||
|
@ -158,9 +168,9 @@ func main() {
|
||||||
killTimeout, err := time.ParseDuration(c.OCSPResponder.ShutdownKillTimeout)
|
killTimeout, err := time.ParseDuration(c.OCSPResponder.ShutdownKillTimeout)
|
||||||
cmd.FailOnError(err, "Couldn't parse shutdown kill timeout")
|
cmd.FailOnError(err, "Couldn't parse shutdown kill timeout")
|
||||||
|
|
||||||
// Configure HTTP
|
|
||||||
m := http.NewServeMux()
|
m := http.NewServeMux()
|
||||||
m.Handle(c.OCSPResponder.Path, cfocsp.Responder{Source: source})
|
m.Handle(c.OCSPResponder.Path,
|
||||||
|
handler(source, c.OCSPResponder.MaxAge.Duration))
|
||||||
|
|
||||||
httpMonitor := metrics.NewHTTPMonitor(stats, m, "OCSP")
|
httpMonitor := metrics.NewHTTPMonitor(stats, m, "OCSP")
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
|
@ -179,3 +189,10 @@ func main() {
|
||||||
|
|
||||||
app.Run()
|
app.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handler(src cfocsp.Source, maxAge time.Duration) http.Handler {
|
||||||
|
return &cacheCtrlHandler{
|
||||||
|
Handler: cfocsp.Responder{Source: src},
|
||||||
|
MaxAge: maxAge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCacheControl(t *testing.T) {
|
||||||
|
src := make(ocsp.InMemorySource)
|
||||||
|
h := handler(src, 10*time.Second)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
r, err := http.NewRequest("GET", "/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
expected := "max-age=10"
|
||||||
|
actual := w.Header().Get("Cache-Control")
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("Cache-Control value: want %#v, got %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
cmd/shell.go
27
cmd/shell.go
|
@ -163,6 +163,9 @@ type Config struct {
|
||||||
|
|
||||||
Path string
|
Path string
|
||||||
ListenAddress string
|
ListenAddress string
|
||||||
|
// MaxAge is the max-age to set in the Cache-Controler response
|
||||||
|
// header. It is a time.Duration formatted string.
|
||||||
|
MaxAge JSONDuration
|
||||||
|
|
||||||
ShutdownStopTimeout string
|
ShutdownStopTimeout string
|
||||||
ShutdownKillTimeout string
|
ShutdownKillTimeout string
|
||||||
|
@ -431,3 +434,27 @@ func DebugServer(addr string) {
|
||||||
log.Printf("booting debug server at %#v", addr)
|
log.Printf("booting debug server at %#v", addr)
|
||||||
log.Println(http.Serve(ln, nil))
|
log.Println(http.Serve(ln, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JSONDuration struct {
|
||||||
|
time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrDurationMustBeString = errors.New("cannot JSON unmarshal something other than a string into a JSONDuration")
|
||||||
|
|
||||||
|
func (d *JSONDuration) UnmarshalJSON(b []byte) error {
|
||||||
|
s := ""
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*json.UnmarshalTypeError); ok {
|
||||||
|
return ErrDurationMustBeString
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dd, err := time.ParseDuration(s)
|
||||||
|
d.Duration = dd
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d JSONDuration) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(d.Duration.String()), nil
|
||||||
|
}
|
||||||
|
|
|
@ -108,7 +108,6 @@ type StorageGetter interface {
|
||||||
GetAuthorization(string) (Authorization, error)
|
GetAuthorization(string) (Authorization, error)
|
||||||
GetLatestValidAuthorization(int64, AcmeIdentifier) (Authorization, error)
|
GetLatestValidAuthorization(int64, AcmeIdentifier) (Authorization, error)
|
||||||
GetCertificate(string) (Certificate, error)
|
GetCertificate(string) (Certificate, error)
|
||||||
GetCertificateByShortSerial(string) (Certificate, error)
|
|
||||||
GetCertificateStatus(string) (CertificateStatus, error)
|
GetCertificateStatus(string) (CertificateStatus, error)
|
||||||
AlreadyDeniedCSR([]string) (bool, error)
|
AlreadyDeniedCSR([]string) (bool, error)
|
||||||
CountCertificatesRange(time.Time, time.Time) (int64, error)
|
CountCertificatesRange(time.Time, time.Time) (int64, error)
|
||||||
|
|
|
@ -231,11 +231,6 @@ func (sa *MockSA) GetCertificate(serial string) (core.Certificate, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCertificateByShortSerial is a mock
|
|
||||||
func (sa *MockSA) GetCertificateByShortSerial(serial string) (core.Certificate, error) {
|
|
||||||
return sa.GetCertificate("0000000000000000" + serial)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCertificateStatus is a mock
|
// GetCertificateStatus is a mock
|
||||||
func (sa *MockSA) GetCertificateStatus(serial string) (core.CertificateStatus, error) {
|
func (sa *MockSA) GetCertificateStatus(serial string) (core.CertificateStatus, error) {
|
||||||
// Serial ee == 238.crt
|
// Serial ee == 238.crt
|
||||||
|
|
|
@ -54,7 +54,6 @@ const (
|
||||||
MethodGetAuthorization = "GetAuthorization" // SA
|
MethodGetAuthorization = "GetAuthorization" // SA
|
||||||
MethodGetLatestValidAuthorization = "GetLatestValidAuthorization" // SA
|
MethodGetLatestValidAuthorization = "GetLatestValidAuthorization" // SA
|
||||||
MethodGetCertificate = "GetCertificate" // SA
|
MethodGetCertificate = "GetCertificate" // SA
|
||||||
MethodGetCertificateByShortSerial = "GetCertificateByShortSerial" // SA
|
|
||||||
MethodGetCertificateStatus = "GetCertificateStatus" // SA
|
MethodGetCertificateStatus = "GetCertificateStatus" // SA
|
||||||
MethodMarkCertificateRevoked = "MarkCertificateRevoked" // SA
|
MethodMarkCertificateRevoked = "MarkCertificateRevoked" // SA
|
||||||
MethodUpdateOCSP = "UpdateOCSP" // SA
|
MethodUpdateOCSP = "UpdateOCSP" // SA
|
||||||
|
@ -940,22 +939,6 @@ func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error
|
||||||
return jsonResponse, nil
|
return jsonResponse, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
rpc.Handle(MethodGetCertificateByShortSerial, func(req []byte) (response []byte, err error) {
|
|
||||||
cert, err := impl.GetCertificateByShortSerial(string(req))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonResponse, err := json.Marshal(cert)
|
|
||||||
if err != nil {
|
|
||||||
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
|
|
||||||
errorCondition(MethodGetCertificateByShortSerial, err, req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonResponse, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
rpc.Handle(MethodGetCertificateStatus, func(req []byte) (response []byte, err error) {
|
rpc.Handle(MethodGetCertificateStatus, func(req []byte) (response []byte, err error) {
|
||||||
status, err := impl.GetCertificateStatus(string(req))
|
status, err := impl.GetCertificateStatus(string(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1080,7 +1063,7 @@ func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error
|
||||||
err = impl.AddSCTReceipt(core.SignedCertificateTimestamp(sct))
|
err = impl.AddSCTReceipt(core.SignedCertificateTimestamp(sct))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
|
// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
|
||||||
errorCondition(MethodGetCertificateByShortSerial, err, req)
|
errorCondition(MethodAddSCTReceipt, err, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1179,18 +1162,6 @@ func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certifica
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCertificateByShortSerial sends a request to search for a certificate by
|
|
||||||
// the predictable portion of its serial number.
|
|
||||||
func (cac StorageAuthorityClient) GetCertificateByShortSerial(id string) (cert core.Certificate, err error) {
|
|
||||||
jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificateByShortSerial, []byte(id))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(jsonCert, &cert)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCertificateStatus sends a request to obtain the current status of a
|
// GetCertificateStatus sends a request to obtain the current status of a
|
||||||
// certificate by ID
|
// certificate by ID
|
||||||
func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.CertificateStatus, err error) {
|
func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.CertificateStatus, err error) {
|
||||||
|
|
|
@ -237,21 +237,6 @@ func (ssa *SQLStorageAuthority) GetLatestValidAuthorization(registrationId int64
|
||||||
return ssa.GetAuthorization(auth.ID)
|
return ssa.GetAuthorization(auth.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCertificateByShortSerial takes an id consisting of the first, sequential half of a
|
|
||||||
// serial number and returns the first certificate whose full serial number is
|
|
||||||
// lexically greater than that id. This allows clients to query on the known
|
|
||||||
// sequential half of our serial numbers to enumerate all certificates.
|
|
||||||
func (ssa *SQLStorageAuthority) GetCertificateByShortSerial(shortSerial string) (cert core.Certificate, err error) {
|
|
||||||
if len(shortSerial) != 16 {
|
|
||||||
err = errors.New("Invalid certificate short serial " + shortSerial)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ssa.dbMap.SelectOne(&cert, "SELECT * FROM certificates WHERE serial LIKE :shortSerial",
|
|
||||||
map[string]interface{}{"shortSerial": shortSerial + "%"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCertificate takes a serial number and returns the corresponding
|
// GetCertificate takes a serial number and returns the corresponding
|
||||||
// certificate, or error if it does not exist.
|
// certificate, or error if it does not exist.
|
||||||
func (ssa *SQLStorageAuthority) GetCertificate(serial string) (core.Certificate, error) {
|
func (ssa *SQLStorageAuthority) GetCertificate(serial string) (core.Certificate, error) {
|
||||||
|
|
|
@ -301,12 +301,7 @@ func TestAddCertificate(t *testing.T) {
|
||||||
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
||||||
test.AssertEquals(t, digest, "qWoItDZmR4P9eFbeYgXXP3SR4ApnkQj8x4LsB_ORKBo")
|
test.AssertEquals(t, digest, "qWoItDZmR4P9eFbeYgXXP3SR4ApnkQj8x4LsB_ORKBo")
|
||||||
|
|
||||||
// Example cert serial is 0x21bd4, so a prefix of all zeroes should fetch it.
|
retrievedCert, err := sa.GetCertificate("000000000000000000000000000000021bd4")
|
||||||
retrievedCert, err := sa.GetCertificateByShortSerial("0000000000000000")
|
|
||||||
test.AssertNotError(t, err, "Couldn't get www.eff.org.der by short serial")
|
|
||||||
test.AssertByteEquals(t, certDER, retrievedCert.DER)
|
|
||||||
|
|
||||||
retrievedCert, err = sa.GetCertificate("000000000000000000000000000000021bd4")
|
|
||||||
test.AssertNotError(t, err, "Couldn't get www.eff.org.der by full serial")
|
test.AssertNotError(t, err, "Couldn't get www.eff.org.der by full serial")
|
||||||
test.AssertByteEquals(t, certDER, retrievedCert.DER)
|
test.AssertByteEquals(t, certDER, retrievedCert.DER)
|
||||||
|
|
||||||
|
@ -324,12 +319,7 @@ func TestAddCertificate(t *testing.T) {
|
||||||
test.AssertNotError(t, err, "Couldn't add test-cert.der")
|
test.AssertNotError(t, err, "Couldn't add test-cert.der")
|
||||||
test.AssertEquals(t, digest2, "CMVYqWzyqUW7pfBF2CxL0Uk6I0Upsk7p4EWSnd_vYx4")
|
test.AssertEquals(t, digest2, "CMVYqWzyqUW7pfBF2CxL0Uk6I0Upsk7p4EWSnd_vYx4")
|
||||||
|
|
||||||
// Example cert serial is 0x21bd4, so a prefix of all zeroes should fetch it.
|
retrievedCert2, err := sa.GetCertificate("0000ff00000000000002238054509817da5a")
|
||||||
retrievedCert2, err := sa.GetCertificateByShortSerial("0000ff0000000000")
|
|
||||||
test.AssertNotError(t, err, "Couldn't get test-cert.der")
|
|
||||||
test.AssertByteEquals(t, certDER2, retrievedCert2.DER)
|
|
||||||
|
|
||||||
retrievedCert2, err = sa.GetCertificate("0000ff00000000000002238054509817da5a")
|
|
||||||
test.AssertNotError(t, err, "Couldn't get test-cert.der")
|
test.AssertNotError(t, err, "Couldn't get test-cert.der")
|
||||||
test.AssertByteEquals(t, certDER2, retrievedCert2.DER)
|
test.AssertByteEquals(t, certDER2, retrievedCert2.DER)
|
||||||
|
|
||||||
|
@ -340,19 +330,6 @@ func TestAddCertificate(t *testing.T) {
|
||||||
test.Assert(t, certificateStatus2.OCSPLastUpdated.IsZero(), "OCSPLastUpdated should be nil")
|
test.Assert(t, certificateStatus2.OCSPLastUpdated.IsZero(), "OCSPLastUpdated should be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetCertificateByShortSerial tests some failure conditions for GetCertificate.
|
|
||||||
// Success conditions are tested above in TestAddCertificate.
|
|
||||||
func TestGetCertificateByShortSerial(t *testing.T) {
|
|
||||||
sa, _, cleanUp := initSA(t)
|
|
||||||
defer cleanUp()
|
|
||||||
|
|
||||||
_, err := sa.GetCertificateByShortSerial("")
|
|
||||||
test.AssertError(t, err, "Should've failed on empty serial")
|
|
||||||
|
|
||||||
_, err = sa.GetCertificateByShortSerial("01020304050607080102030405060708")
|
|
||||||
test.AssertError(t, err, "Should've failed on too-long serial")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeniedCSR(t *testing.T) {
|
func TestDeniedCSR(t *testing.T) {
|
||||||
key, _ := rsa.GenerateKey(rand.Reader, 512)
|
key, _ := rsa.GenerateKey(rand.Reader, 512)
|
||||||
template := &x509.CertificateRequest{
|
template := &x509.CertificateRequest{
|
||||||
|
|
|
@ -149,6 +149,7 @@
|
||||||
"source": "mysql+tcp://boulder@localhost:3306/boulder_sa_integration",
|
"source": "mysql+tcp://boulder@localhost:3306/boulder_sa_integration",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"listenAddress": "localhost:4002",
|
"listenAddress": "localhost:4002",
|
||||||
|
"maxAge": "10s",
|
||||||
"shutdownStopTimeout": "10s",
|
"shutdownStopTimeout": "10s",
|
||||||
"shutdownKillTimeout": "1m",
|
"shutdownKillTimeout": "1m",
|
||||||
"debugAddr": "localhost:8005"
|
"debugAddr": "localhost:8005"
|
||||||
|
|
Loading…
Reference in New Issue