diff --git a/cmd/activity-monitor/main.go b/cmd/activity-monitor/main.go index cf6db100d..763d3bba4 100644 --- a/cmd/activity-monitor/main.go +++ b/cmd/activity-monitor/main.go @@ -118,8 +118,8 @@ func main() { cmd.FailOnError(err, "Could not connect to statsd") auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) - cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) blog.SetAuditLogger(auditlogger) @@ -131,8 +131,6 @@ func main() { go cmd.ProfileCmd("AM", stats) - auditlogger.Info(app.VersionString()) - startMonitor(ch, auditlogger, stats) } diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index 30970a8b5..beffffed3 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -25,6 +25,7 @@ func main() { // Set up logging auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -65,8 +66,6 @@ func main() { cmd.FailOnError(err, "Unable to create CA RPC server") rpc.NewCertificateAuthorityServer(cas, cai) - auditlogger.Info(app.VersionString()) - err = cas.Start(c) cmd.FailOnError(err, "Unable to run CA RPC server") } diff --git a/cmd/boulder-publisher/main.go b/cmd/boulder-publisher/main.go index 448ca88df..9eda05194 100644 --- a/cmd/boulder-publisher/main.go +++ b/cmd/boulder-publisher/main.go @@ -23,6 +23,7 @@ func main() { // Set up logging auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -49,8 +50,6 @@ func main() { cmd.FailOnError(err, "Unable to create Publisher RPC server") rpc.NewPublisherServer(pubs, &pubi) - auditlogger.Info(app.VersionString()) - err = pubs.Start(c) cmd.FailOnError(err, "Unable to run Publisher RPC server") } diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go index 820907b58..a4d7886c6 100644 --- a/cmd/boulder-ra/main.go +++ b/cmd/boulder-ra/main.go @@ -29,6 +29,7 @@ func main() { // Set up logging auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -82,8 +83,6 @@ func main() { cmd.FailOnError(err, "Unable to create RA RPC server") rpc.NewRegistrationAuthorityServer(ras, &rai) - auditlogger.Info(app.VersionString()) - err = ras.Start(c) cmd.FailOnError(err, "Unable to run RA RPC server") } diff --git a/cmd/boulder-sa/main.go b/cmd/boulder-sa/main.go index f1a255eb2..44fd1dbf7 100644 --- a/cmd/boulder-sa/main.go +++ b/cmd/boulder-sa/main.go @@ -23,6 +23,7 @@ func main() { // Set up logging auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -46,8 +47,6 @@ func main() { cmd.FailOnError(err, "Unable to create SA RPC server") rpc.NewStorageAuthorityServer(sas, sai) - auditlogger.Info(app.VersionString()) - err = sas.Start(c) cmd.FailOnError(err, "Unable to run SA RPC server") } diff --git a/cmd/boulder-va/main.go b/cmd/boulder-va/main.go index b854cb0ca..d4adbcc73 100644 --- a/cmd/boulder-va/main.go +++ b/cmd/boulder-va/main.go @@ -27,6 +27,7 @@ func main() { // Set up logging auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -75,8 +76,6 @@ func main() { cmd.FailOnError(err, "Unable to create VA RPC server") rpc.NewValidationAuthorityServer(vas, vai) - auditlogger.Info(app.VersionString()) - err = vas.Start(c) cmd.FailOnError(err, "Unable to run VA RPC server") } diff --git a/cmd/boulder-wfe/main.go b/cmd/boulder-wfe/main.go index e100ff9c5..431ba2a56 100644 --- a/cmd/boulder-wfe/main.go +++ b/cmd/boulder-wfe/main.go @@ -66,6 +66,7 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -120,8 +121,6 @@ func main() { h, err := wfe.Handler() cmd.FailOnError(err, "Problem setting up HTTP handlers") - auditlogger.Info(app.VersionString()) - httpMonitor := metrics.NewHTTPMonitor(stats, h, "WFE") auditlogger.Info(fmt.Sprintf("Server running, listening on %s...\n", c.WFE.ListenAddress)) diff --git a/cmd/cert-checker/main.go b/cmd/cert-checker/main.go index 91dca3b35..2f01215cb 100644 --- a/cmd/cert-checker/main.go +++ b/cmd/cert-checker/main.go @@ -239,9 +239,9 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) blog.SetAuditLogger(auditlogger) - auditlogger.Info(app.VersionString()) saDbMap, err := sa.NewDbMap(c.CertChecker.DBConnect) cmd.FailOnError(err, "Could not connect to database") diff --git a/cmd/expiration-mailer/main.go b/cmd/expiration-mailer/main.go index 83337de62..c95c9c52a 100644 --- a/cmd/expiration-mailer/main.go +++ b/cmd/expiration-mailer/main.go @@ -230,14 +230,13 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() blog.SetAuditLogger(auditlogger) - auditlogger.Info(app.VersionString()) - go cmd.DebugServer(c.Mailer.DebugAddr) // Configure DB diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go index 02e1c6724..f807528fd 100644 --- a/cmd/ocsp-responder/main.go +++ b/cmd/ocsp-responder/main.go @@ -131,6 +131,7 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -141,8 +142,6 @@ func main() { go cmd.ProfileCmd("OCSP", stats) - auditlogger.Info(app.VersionString()) - config := c.OCSPResponder var source cfocsp.Source url, err := url.Parse(config.Source) diff --git a/cmd/ocsp-updater/main.go b/cmd/ocsp-updater/main.go index 5feb76085..af86fcdcb 100644 --- a/cmd/ocsp-updater/main.go +++ b/cmd/ocsp-updater/main.go @@ -205,6 +205,7 @@ func main() { auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats) cmd.FailOnError(err, "Could not connect to Syslog") + auditlogger.Info(app.VersionString()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 defer auditlogger.AuditPanic() @@ -229,8 +230,6 @@ func main() { } }() - auditlogger.Info(app.VersionString()) - updater := &OCSPUpdater{ cac: cac, dbMap: dbMap, diff --git a/sa/database.go b/sa/database.go index dc2ad3031..609f23665 100644 --- a/sa/database.go +++ b/sa/database.go @@ -119,7 +119,7 @@ func SetSQLDebug(dbMap *gorp.DbMap, state bool) { // SQLLogger adapts the AuditLogger to a format GORP can use. type SQLLogger struct { - log *blog.AuditLogger + log blog.SyslogWriter } // Printf adapts the AuditLogger to GORP's interface diff --git a/sa/storage-authority_test.go b/sa/storage-authority_test.go index 28138516b..c31243939 100644 --- a/sa/storage-authority_test.go +++ b/sa/storage-authority_test.go @@ -38,6 +38,7 @@ func initSA(t *testing.T) (*SQLStorageAuthority, clock.FakeClock, func()) { if err != nil { t.Fatalf("Failed to create dbMap: %s", err) } + dbMap.TraceOn("SQL: ", &SQLLogger{log}) fc := clock.NewFake() fc.Add(1 * time.Hour) diff --git a/test/amqp-integration-test.py b/test/amqp-integration-test.py index eac41c2d3..60eca0e4f 100644 --- a/test/amqp-integration-test.py +++ b/test/amqp-integration-test.py @@ -7,12 +7,13 @@ import socket import subprocess import sys import tempfile +import urllib2 import startservers class ExitStatus: - OK, PythonFailure, NodeFailure, Error, OCSPFailure = range(5) + OK, PythonFailure, NodeFailure, Error, OCSPFailure, CTFailure = range(6) class ProcInfo: @@ -62,6 +63,13 @@ def verify_ocsp_revoked(certFile, url): die(ExitStatus.OCSPFailure) pass +def verify_ct_submission(expectedSubmissions, url): + resp = urllib2.urlopen(url) + submissionStr = resp.read() + if int(submissionStr) != expectedSubmissions: + print "Expected %d submissions, found %d" % (expectedSubmissions, int(submissionStr)) + die(ExitStatus.CTFailure) + def run_node_test(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: @@ -91,6 +99,7 @@ def run_node_test(): # Also verify that the static OCSP responder, which answers with a # pre-signed, long-lived response for the CA cert, also works. verify_ocsp_good("../test-ca.der", issuer_ocsp_url) + verify_ct_submission(1, "http://localhost:4500/submissions") if subprocess.Popen(''' node revoke.js %s %s http://localhost:4000/acme/revoke-cert diff --git a/test/ct-test-srv/main.go b/test/ct-test-srv/main.go index 7b55d836e..8e4b8cef8 100644 --- a/test/ct-test-srv/main.go +++ b/test/ct-test-srv/main.go @@ -10,47 +10,70 @@ package main import ( "encoding/json" + "fmt" "io/ioutil" "log" "net/http" + "sync/atomic" ) type ctSubmissionRequest struct { Chain []string `json:"chain"` } -func handler(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" || r.URL.Path != "/ct/v1/add-chain" { +type integrationSrv struct { + submissions int64 +} + +func (is *integrationSrv) handler(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/ct/v1/add-chain": + if r.Method != "POST" { + http.NotFound(w, r) + return + } + bodyBytes, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + } + + var addChainReq ctSubmissionRequest + err = json.Unmarshal(bodyBytes, &addChainReq) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + } + + w.WriteHeader(http.StatusOK) + // id is a sha256 of a random EC key. Generate your own with: + // openssl ecparam -name prime256v1 -genkey -outform der | openssl sha256 -binary | base64 + w.Write([]byte(`{ + "sct_version": 0, + "id": "8fjM8cvLPOhzCFwI62IYJhjkOcvWFLx1dMJbs0uhxJU=", + "timestamp": 1442400000, + "extensions": "", + "signature": "BAMARzBFAiBB5wKED8KqKhADT37n0y28fZIPiGbCfZRVKq0wNo0hrwIhAOIa2tPBF/rB1y30Y/ROh4LBmJ0mItAbTWy8XZKh7Wcp" + }`)) + atomic.AddInt64(&is.submissions, 1) + case "/submissions": + if r.Method != "GET" { + http.NotFound(w, r) + return + } + + submissions := atomic.LoadInt64(&is.submissions) + w.WriteHeader(http.StatusOK) + w.Write([]byte(fmt.Sprintf("%d", submissions))) + default: http.NotFound(w, r) return } - bodyBytes, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - } - - var addChainReq ctSubmissionRequest - err = json.Unmarshal(bodyBytes, &addChainReq) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - } - - w.WriteHeader(http.StatusOK) - // id is a sha256 of a random EC key. Generate your own with: - // openssl ecparam -name prime256v1 -genkey -outform der | openssl sha256 -binary | base64 - w.Write([]byte(`{ - "sct_version": 0, - "id": "8fjM8cvLPOhzCFwI62IYJhjkOcvWFLx1dMJbs0uhxJU=", - "timestamp": 1442400000, - "extensions": "", - "signature": "BAMARzBFAiBB5wKED8KqKhADT37n0y28fZIPiGbCfZRVKq0wNo0hrwIhAOIa2tPBF/rB1y30Y/ROh4LBmJ0mItAbTWy8XZKh7Wcp" - }`)) } func main() { + is := integrationSrv{} s := &http.Server{ - Addr: ":4500", - Handler: http.HandlerFunc(handler), + Addr: "localhost:4500", + Handler: http.HandlerFunc(is.handler), } log.Fatal(s.ListenAndServe()) } diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 5a46a7abb..743e674da 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -153,8 +153,9 @@ func (mrw BodylessResponseWriter) Write(buf []byte) (int, error) { // * Respond http.StatusMethodNotAllowed for HTTP methods other than // those listed. // -// * Never send a body in response to a HEAD request. (Anything -// written by the handler will be discarded if the method is HEAD.) +// * Never send a body in response to a HEAD request. Anything +// written by the handler will be discarded if the method is HEAD. Also, all +// handlers that accept GET automatically accept HEAD. func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h func(http.ResponseWriter, *http.Request), methods ...string) { methodsOK := make(map[string]bool) for _, m := range methods { @@ -169,14 +170,14 @@ func (wfe *WebFrontEndImpl) HandleFunc(mux *http.ServeMux, pattern string, h fun } response.Header().Set("Access-Control-Allow-Origin", "*") - switch request.Method { - case "HEAD": + // Return a bodyless response to HEAD for any resource that allows GET. + if _, ok := methodsOK["GET"]; ok && request.Method == "HEAD" { // We'll be sending an error anyway, but we // should still comply with HTTP spec by not // sending a body. response = BodylessResponseWriter{response} - case "OPTIONS": - // TODO, #469 + h(response, request) + return } if _, ok := methodsOK[request.Method]; !ok { diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index 7b99cf7c5..461044216 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -307,9 +307,15 @@ func TestHandleFunc(t *testing.T) { // Disallowed method special case: response to HEAD has got no body runWrappedHandler(&http.Request{Method: "HEAD"}, "GET", "POST") - test.AssertEquals(t, stubCalled, false) + test.AssertEquals(t, stubCalled, true) test.AssertEquals(t, rw.Body.String(), "") - test.AssertEquals(t, sortHeader(rw.Header().Get("Allow")), "GET, POST") + + // HEAD doesn't work with POST-only endpoints + runWrappedHandler(&http.Request{Method: "HEAD"}, "POST") + test.AssertEquals(t, stubCalled, false) + test.AssertEquals(t, rw.Header().Get("Content-Type"), "application/problem+json") + test.AssertEquals(t, rw.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Method not allowed"}`) + test.AssertEquals(t, rw.Header().Get("Allow"), "POST") } func TestStandardHeaders(t *testing.T) {