Merge pull request #345 from smallstep/max/acmeLogCert

Add cert logging for acme/certificate api
This commit is contained in:
Max 2020-08-12 16:42:35 -07:00 committed by GitHub
commit 393f3efe69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 63 additions and 7 deletions

View file

@ -2,6 +2,8 @@ package api
import ( import (
"context" "context"
"crypto/x509"
"encoding/pem"
"fmt" "fmt"
"net/http" "net/http"
@ -162,6 +164,18 @@ func (h *Handler) GetCertificate(w http.ResponseWriter, r *http.Request) {
return return
} }
block, _ := pem.Decode(certBytes)
if block == nil {
api.WriteError(w, acme.ServerInternalErr(errors.New("failed to decode any certificates from generated certBytes")))
return
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
api.WriteError(w, acme.Wrap(err, "failed to parse generated leaf certificate"))
return
}
api.LogCertificate(w, cert)
w.Header().Set("Content-Type", "application/pem-certificate-chain; charset=utf-8") w.Header().Set("Content-Type", "application/pem-certificate-chain; charset=utf-8")
w.Write(certBytes) w.Write(certBytes)
} }

View file

@ -526,6 +526,43 @@ func TestHandlerGetCertificate(t *testing.T) {
problem: acme.ServerInternalErr(errors.New("force")), problem: acme.ServerInternalErr(errors.New("force")),
} }
}, },
"fail/decode-leaf-for-loggger": func(t *testing.T) test {
acc := &acme.Account{ID: "accID"}
ctx := context.WithValue(context.Background(), acme.AccContextKey, acc)
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
return test{
auth: &mockAcmeAuthority{
getCertificate: func(accID, id string) ([]byte, error) {
assert.Equals(t, accID, acc.ID)
assert.Equals(t, id, certID)
return []byte("foo"), nil
},
},
ctx: ctx,
statusCode: 500,
problem: acme.ServerInternalErr(errors.New("failed to decode any certificates from generated certBytes")),
}
},
"fail/parse-x509-leaf-for-logger": func(t *testing.T) test {
acc := &acme.Account{ID: "accID"}
ctx := context.WithValue(context.Background(), acme.AccContextKey, acc)
ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx)
return test{
auth: &mockAcmeAuthority{
getCertificate: func(accID, id string) ([]byte, error) {
assert.Equals(t, accID, acc.ID)
assert.Equals(t, id, certID)
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: []byte("foo"),
}), nil
},
},
ctx: ctx,
statusCode: 500,
problem: acme.ServerInternalErr(errors.New("failed to parse generated leaf certificate")),
}
},
"ok": func(t *testing.T) test { "ok": func(t *testing.T) test {
acc := &acme.Account{ID: "accID"} acc := &acme.Account{ID: "accID"}
ctx := context.WithValue(context.Background(), acme.AccContextKey, acc) ctx := context.WithValue(context.Background(), acme.AccContextKey, acc)
@ -565,7 +602,7 @@ func TestHandlerGetCertificate(t *testing.T) {
prob := tc.problem.ToACME() prob := tc.problem.ToACME()
assert.Equals(t, ae.Type, prob.Type) assert.Equals(t, ae.Type, prob.Type)
assert.Equals(t, ae.Detail, prob.Detail) assert.HasPrefix(t, ae.Detail, prob.Detail)
assert.Equals(t, ae.Identifier, prob.Identifier) assert.Equals(t, ae.Identifier, prob.Identifier)
assert.Equals(t, ae.Subproblems, prob.Subproblems) assert.Equals(t, ae.Subproblems, prob.Subproblems)
assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"})

View file

@ -395,7 +395,8 @@ func logOtt(w http.ResponseWriter, token string) {
} }
} }
func logCertificate(w http.ResponseWriter, cert *x509.Certificate) { // LogCertificate add certificate fields to the log message.
func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) {
if rl, ok := w.(logging.ResponseLogger); ok { if rl, ok := w.(logging.ResponseLogger); ok {
m := map[string]interface{}{ m := map[string]interface{}{
"serial": cert.SerialNumber, "serial": cert.SerialNumber,
@ -413,7 +414,11 @@ func logCertificate(w http.ResponseWriter, cert *x509.Certificate) {
if err != nil || len(rest) > 0 { if err != nil || len(rest) > 0 {
break break
} }
m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) if len(val.CredentialID) > 0 {
m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID)
} else {
m["provisioner"] = fmt.Sprintf("%s", val.Name)
}
break break
} }
} }

View file

@ -54,7 +54,7 @@ func (h *caHandler) Rekey(w http.ResponseWriter, r *http.Request) {
caPEM = certChainPEM[1] caPEM = certChainPEM[1]
} }
logCertificate(w, certChain[0]) LogCertificate(w, certChain[0])
JSONStatus(w, &SignResponse{ JSONStatus(w, &SignResponse{
ServerPEM: certChainPEM[0], ServerPEM: certChainPEM[0],
CaPEM: caPEM, CaPEM: caPEM,

View file

@ -25,7 +25,7 @@ func (h *caHandler) Renew(w http.ResponseWriter, r *http.Request) {
caPEM = certChainPEM[1] caPEM = certChainPEM[1]
} }
logCertificate(w, certChain[0]) LogCertificate(w, certChain[0])
JSONStatus(w, &SignResponse{ JSONStatus(w, &SignResponse{
ServerPEM: certChainPEM[0], ServerPEM: certChainPEM[0],
CaPEM: caPEM, CaPEM: caPEM,

View file

@ -91,7 +91,7 @@ func (h *caHandler) Revoke(w http.ResponseWriter, r *http.Request) {
// TODO: should probably be checking if the certificate was revoked here. // TODO: should probably be checking if the certificate was revoked here.
// Will need to thread that request down to the authority, so will need // Will need to thread that request down to the authority, so will need
// to add API for that. // to add API for that.
logCertificate(w, opts.Crt) LogCertificate(w, opts.Crt)
opts.MTLS = true opts.MTLS = true
} }

View file

@ -79,7 +79,7 @@ func (h *caHandler) Sign(w http.ResponseWriter, r *http.Request) {
if len(certChainPEM) > 1 { if len(certChainPEM) > 1 {
caPEM = certChainPEM[1] caPEM = certChainPEM[1]
} }
logCertificate(w, certChain[0]) LogCertificate(w, certChain[0])
JSONStatus(w, &SignResponse{ JSONStatus(w, &SignResponse{
ServerPEM: certChainPEM[0], ServerPEM: certChainPEM[0],
CaPEM: caPEM, CaPEM: caPEM,