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:
parent
a11f62abf2
commit
550fbdc41c
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue