Add new API to PKI to list revoked certificates (#17779)
* Add new API to PKI to list revoked certificates - A new API that will return the list of serial numbers of revoked certificates on the local cluster. * Add cl * PR feedback
This commit is contained in:
parent
4e122214f7
commit
419ba9159c
|
@ -134,6 +134,7 @@ func Backend(conf *logical.BackendConfig) *backend {
|
|||
pathRotateDeltaCRL(&b),
|
||||
pathRevoke(&b),
|
||||
pathRevokeWithKey(&b),
|
||||
pathListCertsRevoked(&b),
|
||||
pathTidy(&b),
|
||||
pathTidyCancel(&b),
|
||||
pathTidyStatus(&b),
|
||||
|
|
|
@ -5889,6 +5889,81 @@ func TestPKI_EmptyCRLConfigUpgraded(t *testing.T) {
|
|||
require.Equal(t, resp.Data["delta_rebuild_interval"], defaultCrlConfig.DeltaRebuildInterval)
|
||||
}
|
||||
|
||||
func TestPKI_ListRevokedCerts(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, s := createBackendWithStorage(t)
|
||||
|
||||
// Test empty cluster
|
||||
resp, err := CBList(b, s, "certs/revoked")
|
||||
requireSuccessNonNilResponse(t, resp, err, "failed listing empty cluster")
|
||||
require.Empty(t, resp.Data, "response map contained data that we did not expect")
|
||||
|
||||
// Set up a mount that we can revoke under (We will create 3 leaf certs, 2 of which will be revoked)
|
||||
resp, err = CBWrite(b, s, "root/generate/internal", map[string]interface{}{
|
||||
"common_name": "test.com",
|
||||
"key_type": "ec",
|
||||
})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error generating root CA")
|
||||
requireFieldsSetInResp(t, resp, "serial_number")
|
||||
issuerSerial := resp.Data["serial_number"]
|
||||
|
||||
resp, err = CBWrite(b, s, "roles/test", map[string]interface{}{
|
||||
"allowed_domains": "test.com",
|
||||
"allow_subdomains": "true",
|
||||
"max_ttl": "1h",
|
||||
})
|
||||
requireSuccessNilResponse(t, resp, err, "error setting up pki role")
|
||||
|
||||
resp, err = CBWrite(b, s, "issue/test", map[string]interface{}{
|
||||
"common_name": "test1.test.com",
|
||||
})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error issuing cert 1")
|
||||
requireFieldsSetInResp(t, resp, "serial_number")
|
||||
serial1 := resp.Data["serial_number"]
|
||||
|
||||
resp, err = CBWrite(b, s, "issue/test", map[string]interface{}{
|
||||
"common_name": "test2.test.com",
|
||||
})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error issuing cert 2")
|
||||
requireFieldsSetInResp(t, resp, "serial_number")
|
||||
serial2 := resp.Data["serial_number"]
|
||||
|
||||
resp, err = CBWrite(b, s, "issue/test", map[string]interface{}{
|
||||
"common_name": "test3.test.com",
|
||||
})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error issuing cert 2")
|
||||
requireFieldsSetInResp(t, resp, "serial_number")
|
||||
serial3 := resp.Data["serial_number"]
|
||||
|
||||
resp, err = CBWrite(b, s, "revoke", map[string]interface{}{"serial_number": serial1})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error revoking cert 1")
|
||||
|
||||
resp, err = CBWrite(b, s, "revoke", map[string]interface{}{"serial_number": serial2})
|
||||
requireSuccessNonNilResponse(t, resp, err, "error revoking cert 2")
|
||||
|
||||
// Test that we get back the expected revoked serial numbers.
|
||||
resp, err = CBList(b, s, "certs/revoked")
|
||||
requireSuccessNonNilResponse(t, resp, err, "failed listing revoked certs")
|
||||
requireFieldsSetInResp(t, resp, "keys")
|
||||
revokedKeys := resp.Data["keys"].([]string)
|
||||
|
||||
require.Contains(t, revokedKeys, serial1)
|
||||
require.Contains(t, revokedKeys, serial2)
|
||||
require.Equal(t, 2, len(revokedKeys), "Expected 2 revoked entries got %d: %v", len(revokedKeys), revokedKeys)
|
||||
|
||||
// Test that listing our certs returns a different response
|
||||
resp, err = CBList(b, s, "certs")
|
||||
requireSuccessNonNilResponse(t, resp, err, "failed listing written certs")
|
||||
requireFieldsSetInResp(t, resp, "keys")
|
||||
certKeys := resp.Data["keys"].([]string)
|
||||
|
||||
require.Contains(t, certKeys, serial1)
|
||||
require.Contains(t, certKeys, serial2)
|
||||
require.Contains(t, certKeys, serial3)
|
||||
require.Contains(t, certKeys, issuerSerial)
|
||||
require.Equal(t, 4, len(certKeys), "Expected 4 cert entries got %d: %v", len(certKeys), certKeys)
|
||||
}
|
||||
|
||||
var (
|
||||
initTest sync.Once
|
||||
rsaCAKey string
|
||||
|
|
|
@ -18,6 +18,21 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
func pathListCertsRevoked(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "certs/revoked/?$",
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ListOperation: &framework.PathOperation{
|
||||
Callback: b.pathListRevokedCertsHandler,
|
||||
},
|
||||
},
|
||||
|
||||
HelpSynopsis: pathListRevokedHelpSyn,
|
||||
HelpDescription: pathListRevokedHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func pathRevoke(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: `revoke`,
|
||||
|
@ -466,6 +481,22 @@ func (b *backend) pathRotateDeltaCRLRead(ctx context.Context, req *logical.Reque
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathListRevokedCertsHandler(ctx context.Context, request *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
||||
sc := b.makeStorageContext(ctx, request.Storage)
|
||||
|
||||
revokedCerts, err := sc.listRevokedCerts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Normalize serial back to a format people are expecting.
|
||||
for i, serial := range revokedCerts {
|
||||
revokedCerts[i] = denormalizeSerial(serial)
|
||||
}
|
||||
|
||||
return logical.ListResponse(revokedCerts), nil
|
||||
}
|
||||
|
||||
const pathRevokeHelpSyn = `
|
||||
Revoke a certificate by serial number or with explicit certificate.
|
||||
|
||||
|
@ -493,3 +524,11 @@ Force a rebuild of the delta CRL.
|
|||
const pathRotateDeltaCRLHelpDesc = `
|
||||
Force a rebuild of the delta CRL. This can be used to force an update of the otherwise periodically-rebuilt delta CRLs.
|
||||
`
|
||||
|
||||
const pathListRevokedHelpSyn = `
|
||||
List all revoked serial numbers within the local cluster
|
||||
`
|
||||
|
||||
const pathListRevokedHelpDesc = `
|
||||
Returns a list of serial numbers for revoked certificates in the local cluster.
|
||||
`
|
||||
|
|
|
@ -1216,3 +1216,12 @@ func (sc *storageContext) writeAutoTidyConfig(config *tidyConfig) error {
|
|||
|
||||
return sc.Storage.Put(sc.Context, entry)
|
||||
}
|
||||
|
||||
func (sc *storageContext) listRevokedCerts() ([]string, error) {
|
||||
list, err := sc.Storage.List(sc.Context, revokedPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed listing revoked certs: %w", err)
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
secrets/pki: Add a new API that returns the serial numbers of revoked certificates on the local cluster
|
||||
```
|
|
@ -29,6 +29,7 @@ update your API calls accordingly.
|
|||
- [Sign Verbatim](#sign-verbatim)
|
||||
- [Revoke Certificate](#revoke-certificate)
|
||||
- [Revoke Certificate with Private Key](#revoke-certificate-with-private-key)
|
||||
- [List Revoked Certificates](#list-revoked-certificates)
|
||||
- [Accessing Authority Information](#accessing-authority-information)
|
||||
- [List Issuers](#list-issuers)
|
||||
- [Read Issuer Certificate](#read-issuer-certificate)
|
||||
|
@ -958,6 +959,36 @@ $ curl \
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
### List Revoked Certificates
|
||||
|
||||
This endpoint returns a list of serial numbers that have been revoked on the local cluster.
|
||||
|
||||
| Method | Path |
|
||||
|:-------|:------------------|
|
||||
| `LIST` | `/certs/revoked` |
|
||||
|
||||
#### Sample Request
|
||||
|
||||
```shell-session
|
||||
$ curl \
|
||||
--header "X-Vault-Token: ..." \
|
||||
--request LIST \
|
||||
http://127.0.0.1:8200/v1/pki/certs/revoked
|
||||
```
|
||||
|
||||
#### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"keys": [
|
||||
"3d:80:91:c3:c2:34:3b:81:69:3d:92:a3:80:69:db:53:04:26:ab:b4"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessing Authority Information
|
||||
|
|
Loading…
Reference in New Issue