Return revocation info within existing certs/<serial> api (#17774)

* Return revocation info within existing certs/<serial> api

 - The api already returned both the certificate and a revocation_time
   field populated. Update the api to return revocation_time_rfc3339
   as we do elsewhere and also the issuer id if it was revoked.
 - This will allow callers to associate a revoked cert with an issuer

* Add cl

* PR feedback (docs update)
This commit is contained in:
Steven Clark 2022-11-02 13:06:04 -04:00 committed by GitHub
parent a11f62abf2
commit 550fbdc41c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/errutil" "github.com/hashicorp/vault/sdk/helper/errutil"
@ -156,6 +157,9 @@ func (b *backend) pathFetchRead(ctx context.Context, req *logical.Request, data
var certificate []byte var certificate []byte
var fullChain []byte var fullChain []byte
var revocationTime int64 var revocationTime int64
var revocationIssuerId string
var revocationTimeRfc3339 string
response = &logical.Response{ response = &logical.Response{
Data: map[string]interface{}{}, Data: map[string]interface{}{},
} }
@ -322,6 +326,11 @@ func (b *backend) pathFetchRead(ctx context.Context, req *logical.Request, data
return logical.ErrorResponse(fmt.Sprintf("Error decoding revocation entry for serial %s: %s", serial, err)), nil return logical.ErrorResponse(fmt.Sprintf("Error decoding revocation entry for serial %s: %s", serial, err)), nil
} }
revocationTime = revInfo.RevocationTime revocationTime = revInfo.RevocationTime
revocationIssuerId = revInfo.CertificateIssuer.String()
if !revInfo.RevocationTimeUTC.IsZero() {
revocationTimeRfc3339 = revInfo.RevocationTimeUTC.Format(time.RFC3339Nano)
}
} }
reply: reply:
@ -354,6 +363,12 @@ reply:
default: default:
response.Data["certificate"] = string(certificate) response.Data["certificate"] = string(certificate)
response.Data["revocation_time"] = revocationTime response.Data["revocation_time"] = revocationTime
response.Data["revocation_time_rfc3339"] = revocationTimeRfc3339
// Only output this field if we have a value for it as it doesn't make sense for a
// bunch of code paths that go through here
if revocationIssuerId != "" {
response.Data["issuer_id"] = revocationIssuerId
}
if len(fullChain) > 0 { if len(fullChain) > 0 {
response.Data["ca_chain"] = string(fullChain) response.Data["ca_chain"] = string(fullChain)

View File

@ -1,6 +1,7 @@
package pki package pki
import ( import (
"encoding/json"
"testing" "testing"
"time" "time"
@ -63,6 +64,7 @@ func TestAutoTidy(t *testing.T) {
require.NotNil(t, resp) require.NotNil(t, resp)
require.NotEmpty(t, resp.Data) require.NotEmpty(t, resp.Data)
require.NotEmpty(t, resp.Data["issuer_id"]) require.NotEmpty(t, resp.Data["issuer_id"])
issuerId := resp.Data["issuer_id"]
// Run tidy so status is not empty when we run it later... // Run tidy so status is not empty when we run it later...
_, err = client.Logical().Write("pki/tidy", map[string]interface{}{ _, err = client.Logical().Write("pki/tidy", map[string]interface{}{
@ -101,6 +103,17 @@ func TestAutoTidy(t *testing.T) {
leafSerial := resp.Data["serial_number"].(string) leafSerial := resp.Data["serial_number"].(string)
leafCert := parseCert(t, resp.Data["certificate"].(string)) leafCert := parseCert(t, resp.Data["certificate"].(string))
// Read cert before revoking
resp, err = client.Logical().Read("pki/cert/" + leafSerial)
require.NoError(t, err)
require.NotNil(t, resp)
require.NotNil(t, resp.Data)
require.NotEmpty(t, resp.Data["certificate"])
revocationTime, err := (resp.Data["revocation_time"].(json.Number)).Int64()
require.Equal(t, int64(0), revocationTime, "revocation time was not zero")
require.Empty(t, resp.Data["revocation_time_rfc3339"], "revocation_time_rfc3339 was not empty")
require.Empty(t, resp.Data["issuer_id"], "issuer_id was not empty")
_, err = client.Logical().Write("pki/revoke", map[string]interface{}{ _, err = client.Logical().Write("pki/revoke", map[string]interface{}{
"serial_number": leafSerial, "serial_number": leafSerial,
}) })
@ -112,6 +125,22 @@ func TestAutoTidy(t *testing.T) {
require.NotNil(t, resp) require.NotNil(t, resp)
require.NotNil(t, resp.Data) require.NotNil(t, resp.Data)
require.NotEmpty(t, resp.Data["certificate"]) require.NotEmpty(t, resp.Data["certificate"])
revocationTime, err = (resp.Data["revocation_time"].(json.Number)).Int64()
require.NoError(t, err, "failed converting %s to int", resp.Data["revocation_time"])
revTime := time.Unix(revocationTime, 0)
now := time.Now()
if !(now.After(revTime) && now.Add(-10*time.Minute).Before(revTime)) {
t.Fatalf("parsed revocation time not within the last 10 minutes current time: %s, revocation time: %s", now, revTime)
}
utcLoc, err := time.LoadLocation("UTC")
require.NoError(t, err, "failed to parse UTC location?")
rfc3339RevocationTime, err := time.Parse(time.RFC3339Nano, resp.Data["revocation_time_rfc3339"].(string))
require.NoError(t, err, "failed parsing revocation_time_rfc3339 field: %s", resp.Data["revocation_time_rfc3339"])
require.Equal(t, revTime.In(utcLoc), rfc3339RevocationTime.Truncate(time.Second),
"revocation times did not match revocation_time: %s, "+"rfc3339 time: %s", revTime, rfc3339RevocationTime)
require.Equal(t, issuerId, resp.Data["issuer_id"], "issuer_id on leaf cert did not match")
// Wait for cert to expire and the safety buffer to elapse. // Wait for cert to expire and the safety buffer to elapse.
time.Sleep(time.Until(leafCert.NotAfter) + 3*time.Second) time.Sleep(time.Until(leafCert.NotAfter) + 3*time.Second)

3
changelog/17774.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/pki: Return new fields revocation_time_rfc3339 and issuer_id to existing certificate serial lookup api if it is revoked
```

View File

@ -1314,7 +1314,10 @@ $ curl \
```json ```json
{ {
"data": { "data": {
"certificate": "-----BEGIN CERTIFICATE-----\nMIIGmDCCBYCgAwIBAgIHBzEB3fTzhTANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE\n..." "certificate": "-----BEGIN CERTIFICATE-----\nMIIGmDCCBYCgAwIBAgIHBzEB3fTzhTANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE\n...",
"revocation_time": 1667400107,
"revocation_time_rfc3339": "2022-11-02T14:41:47.327515Z",
"issuer_id": "e27bf456-51e1-d937-0001-4a609184fd9b"
} }
} }
``` ```