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),
|
pathRotateDeltaCRL(&b),
|
||||||
pathRevoke(&b),
|
pathRevoke(&b),
|
||||||
pathRevokeWithKey(&b),
|
pathRevokeWithKey(&b),
|
||||||
|
pathListCertsRevoked(&b),
|
||||||
pathTidy(&b),
|
pathTidy(&b),
|
||||||
pathTidyCancel(&b),
|
pathTidyCancel(&b),
|
||||||
pathTidyStatus(&b),
|
pathTidyStatus(&b),
|
||||||
|
|
|
@ -5889,6 +5889,81 @@ func TestPKI_EmptyCRLConfigUpgraded(t *testing.T) {
|
||||||
require.Equal(t, resp.Data["delta_rebuild_interval"], defaultCrlConfig.DeltaRebuildInterval)
|
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 (
|
var (
|
||||||
initTest sync.Once
|
initTest sync.Once
|
||||||
rsaCAKey string
|
rsaCAKey string
|
||||||
|
|
|
@ -18,6 +18,21 @@ import (
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"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 {
|
func pathRevoke(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: `revoke`,
|
Pattern: `revoke`,
|
||||||
|
@ -466,6 +481,22 @@ func (b *backend) pathRotateDeltaCRLRead(ctx context.Context, req *logical.Reque
|
||||||
return resp, nil
|
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 = `
|
const pathRevokeHelpSyn = `
|
||||||
Revoke a certificate by serial number or with explicit certificate.
|
Revoke a certificate by serial number or with explicit certificate.
|
||||||
|
|
||||||
|
@ -493,3 +524,11 @@ Force a rebuild of the delta CRL.
|
||||||
const pathRotateDeltaCRLHelpDesc = `
|
const pathRotateDeltaCRLHelpDesc = `
|
||||||
Force a rebuild of the delta CRL. This can be used to force an update of the otherwise periodically-rebuilt delta CRLs.
|
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)
|
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)
|
- [Sign Verbatim](#sign-verbatim)
|
||||||
- [Revoke Certificate](#revoke-certificate)
|
- [Revoke Certificate](#revoke-certificate)
|
||||||
- [Revoke Certificate with Private Key](#revoke-certificate-with-private-key)
|
- [Revoke Certificate with Private Key](#revoke-certificate-with-private-key)
|
||||||
|
- [List Revoked Certificates](#list-revoked-certificates)
|
||||||
- [Accessing Authority Information](#accessing-authority-information)
|
- [Accessing Authority Information](#accessing-authority-information)
|
||||||
- [List Issuers](#list-issuers)
|
- [List Issuers](#list-issuers)
|
||||||
- [Read Issuer Certificate](#read-issuer-certificate)
|
- [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
|
## Accessing Authority Information
|
||||||
|
|
Loading…
Reference in New Issue