Adding Response Structures to PKI Config (#18376)

This commit is contained in:
AnPucel 2023-02-15 14:51:27 -08:00 committed by GitHub
parent 91446e129e
commit e05c8931b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 395 additions and 7 deletions

View File

@ -30,6 +30,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/stretchr/testify/require"
"github.com/armon/go-metrics"
@ -550,7 +552,6 @@ func generateURLSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s
if err != nil {
return err
}
if !reflect.DeepEqual(entries, expected) {
return fmt.Errorf("expected urls\n%#v\ndoes not match provided\n%#v\n", expected, entries)
}
@ -1983,6 +1984,7 @@ func TestBackend_PathFetchCertList(t *testing.T) {
Data: rootData,
MountPoint: "pki/",
})
if resp != nil && resp.IsError() {
t.Fatalf("failed to generate root, %#v", resp)
}
@ -2003,6 +2005,16 @@ func TestBackend_PathFetchCertList(t *testing.T) {
Data: urlsData,
MountPoint: "pki/",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/urls"), logical.UpdateOperation), resp, true)
resp, err = b.HandleRequest(context.Background(), &logical.Request{
Operation: logical.ReadOperation,
Path: "config/urls",
Storage: storage,
MountPoint: "pki/",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/urls"), logical.ReadOperation), resp, true)
if resp != nil && resp.IsError() {
t.Fatalf("failed to config urls, %#v", resp)
}
@ -2410,6 +2422,8 @@ func TestBackend_Root_Idempotency(t *testing.T) {
resp, err = CBWrite(b, s, "config/ca", map[string]interface{}{
"pem_bundle": pemBundleRootCA,
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/ca"), logical.UpdateOperation), resp, true)
require.NoError(t, err)
require.NotNil(t, resp, "expected ca info")
firstImportedKeys := resp.Data["imported_keys"].([]string)
@ -6013,11 +6027,16 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
b, s := CreateBackendWithStorage(t)
// Setting templated AIAs should succeed.
_, err := CBWrite(b, s, "config/cluster", map[string]interface{}{
resp, err := CBWrite(b, s, "config/cluster", map[string]interface{}{
"path": "http://localhost:8200/v1/pki",
"aia_path": "http://localhost:8200/cdn/pki",
})
require.NoError(t, err)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/cluster"), logical.UpdateOperation), resp, true)
resp, err = CBRead(b, s, "config/cluster")
require.NoError(t, err)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/cluster"), logical.ReadOperation), resp, true)
aiaData := map[string]interface{}{
"crl_distribution_points": "{{cluster_path}}/issuer/{{issuer_id}}/crl/der",
@ -6046,7 +6065,7 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
"enable_templating": false,
})
require.NoError(t, err)
resp, err := CBWrite(b, s, "root/generate/internal", rootData)
resp, err = CBWrite(b, s, "root/generate/internal", rootData)
requireSuccessNonNilResponse(t, resp, err)
issuerId := string(resp.Data["issuer_id"].(issuerID))

View File

@ -9,6 +9,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/sdk/logical"
@ -90,8 +92,11 @@ func TestBackend_CRLConfig(t *testing.T) {
"auto_rebuild_grace_period": tc.autoRebuildGracePeriod,
})
requireSuccessNonNilResponse(t, resp, err)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/crl"), logical.UpdateOperation), resp, true)
resp, err = CBRead(b, s, "config/crl")
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/crl"), logical.ReadOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err)
requireFieldsSetInResp(t, resp, "disable", "expiry", "ocsp_disable", "auto_rebuild", "auto_rebuild_grace_period")

View File

@ -11,6 +11,8 @@ import (
"fmt"
"testing"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/require"
)
@ -371,6 +373,7 @@ func TestIntegration_AutoIssuer(t *testing.T) {
"issuer_name": "root-1",
"key_type": "ec",
})
requireSuccessNonNilResponse(t, resp, err)
issuerIdOne := resp.Data["issuer_id"]
require.NotEmpty(t, issuerIdOne)
@ -381,12 +384,15 @@ func TestIntegration_AutoIssuer(t *testing.T) {
requireSuccessNonNilResponse(t, resp, err)
require.Equal(t, issuerIdOne, resp.Data["default"])
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/issuers"), logical.ReadOperation), resp, true)
// Enable the new config option.
_, err = CBWrite(b, s, "config/issuers", map[string]interface{}{
resp, err = CBWrite(b, s, "config/issuers", map[string]interface{}{
"default": issuerIdOne,
"default_follows_latest_issuer": true,
})
require.NoError(t, err)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/issuers"), logical.UpdateOperation), resp, true)
// Now generate the second root; it should become default.
resp, err = CBWrite(b, s, "root/generate/internal", map[string]interface{}{

View File

@ -2,6 +2,7 @@ package pki
import (
"context"
"net/http"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
@ -21,6 +22,28 @@ secret key and certificate.`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathImportIssuers,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"mapping": {
Type: framework.TypeMap,
Description: "A mapping of issuer_id to key_id for all issuers included in this request",
Required: true,
},
"imported_keys": {
Type: framework.TypeCommaStringSlice,
Description: "Net-new keys imported as a part of this request",
Required: true,
},
"imported_issuers": {
Type: framework.TypeCommaStringSlice,
Description: "Net-new issuers imported as a part of this request",
Required: true,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
@ -58,13 +81,44 @@ func pathConfigIssuers(b *backend) *framework.Path {
Default: false,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathCAIssuersRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
Required: true,
},
"default_follows_latest_issuer": {
Type: framework.TypeBool,
Description: `Whether the default issuer should automatically follow the latest generated or imported issuer. Defaults to false.`,
Required: true,
},
},
}},
},
},
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathCAIssuersWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
},
"default_follows_latest_issuer": {
Type: framework.TypeBool,
Description: `Whether the default issuer should automatically follow the latest generated or imported issuer. Defaults to false.`,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
@ -90,6 +144,23 @@ func pathReplaceRoot(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathCAIssuersWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
Required: true,
},
"default_follows_latest_issuer": {
Type: framework.TypeBool,
Description: `Whether the default issuer should automatically follow the latest generated or imported issuer. Defaults to false.`,
Required: true,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
@ -208,12 +279,35 @@ func pathConfigKeys(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathKeyDefaultWrite,
Callback: b.pathKeyDefaultWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
Required: true,
},
},
}},
},
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathKeyDefaultRead,
Callback: b.pathKeyDefaultRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
},
},
}},
},
ForwardPerformanceStandby: false,
ForwardPerformanceSecondary: false,
},

View File

@ -3,6 +3,7 @@ package pki
import (
"context"
"fmt"
"net/http"
"github.com/asaskevich/govalidator"
"github.com/hashicorp/vault/sdk/framework"
@ -41,9 +42,70 @@ For example: http://cdn.example.com/pr1/pki`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathWriteCluster,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"path": {
Type: framework.TypeString,
Description: `Canonical URI to this mount on this performance
replication cluster's external address. This is for resolving AIA URLs and
providing the {{cluster_path}} template parameter but might be used for other
purposes in the future.
This should only point back to this particular PR replica and should not ever
point to another PR cluster. It may point to any node in the PR replica,
including standby nodes, and need not always point to the active node.
For example: https://pr1.vault.example.com:8200/v1/pki`,
},
"aia_path": {
Type: framework.TypeString,
Description: `Optional URI to this mount's AIA distribution
point; may refer to an external non-Vault responder. This is for resolving AIA
URLs and providing the {{cluster_aia_path}} template parameter and will not
be used for other purposes. As such, unlike path above, this could safely
be an insecure transit mechanism (like HTTP without TLS).
For example: http://cdn.example.com/pr1/pki`,
},
},
}},
},
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathReadCluster,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"path": {
Type: framework.TypeString,
Description: `Canonical URI to this mount on this performance
replication cluster's external address. This is for resolving AIA URLs and
providing the {{cluster_path}} template parameter but might be used for other
purposes in the future.
This should only point back to this particular PR replica and should not ever
point to another PR cluster. It may point to any node in the PR replica,
including standby nodes, and need not always point to the active node.
For example: https://pr1.vault.example.com:8200/v1/pki`,
Required: true,
},
"aia_path": {
Type: framework.TypeString,
Description: `Optional URI to this mount's AIA distribution
point; may refer to an external non-Vault responder. This is for resolving AIA
URLs and providing the {{cluster_aia_path}} template parameter and will not
be used for other purposes. As such, unlike path above, this could safely
be an insecure transit mechanism (like HTTP without TLS).
For example: http://cdn.example.com/pr1/pki`,
},
},
}},
},
},
},

View File

@ -3,6 +3,7 @@ package pki
import (
"context"
"fmt"
"net/http"
"time"
"github.com/hashicorp/vault/helper/constants"
@ -110,9 +111,141 @@ existing CRL and OCSP paths will return the unified CRL instead of a response ba
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathCRLRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"expiry": {
Type: framework.TypeString,
Description: `The amount of time the generated CRL should be
valid; defaults to 72 hours`,
Required: true,
},
"disable": {
Type: framework.TypeBool,
Description: `If set to true, disables generating the CRL entirely.`,
Required: true,
},
"ocsp_disable": {
Type: framework.TypeBool,
Description: `If set to true, ocsp unauthorized responses will be returned.`,
Required: true,
},
"ocsp_expiry": {
Type: framework.TypeString,
Description: `The amount of time an OCSP response will be valid (controls
the NextUpdate field); defaults to 12 hours`,
Required: true,
},
"auto_rebuild": {
Type: framework.TypeBool,
Description: `If set to true, enables automatic rebuilding of the CRL`,
Required: true,
},
"auto_rebuild_grace_period": {
Type: framework.TypeString,
Description: `The time before the CRL expires to automatically rebuild it, when enabled. Must be shorter than the CRL expiry. Defaults to 12h.`,
Required: true,
},
"enable_delta": {
Type: framework.TypeBool,
Description: `Whether to enable delta CRLs between authoritative CRL rebuilds`,
Required: true,
},
"delta_rebuild_interval": {
Type: framework.TypeString,
Description: `The time between delta CRL rebuilds if a new revocation has occurred. Must be shorter than the CRL expiry. Defaults to 15m.`,
Required: true,
},
"cross_cluster_revocation": {
Type: framework.TypeBool,
Description: `Whether to enable a global, cross-cluster revocation queue.
Must be used with auto_rebuild=true.`,
Required: true,
},
"unified_crl": {
Type: framework.TypeBool,
Description: `If set to true enables global replication of revocation entries,
also enabling unified versions of OCSP and CRLs if their respective features are enabled.
disable for CRLs and ocsp_disable for OCSP.`,
Required: true,
},
"unified_crl_on_existing_paths": {
Type: framework.TypeBool,
Description: `If set to true,
existing CRL and OCSP paths will return the unified CRL instead of a response based on cluster-local data`,
Required: true,
},
},
}},
},
},
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathCRLWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"expiry": {
Type: framework.TypeString,
Description: `The amount of time the generated CRL should be
valid; defaults to 72 hours`,
Default: "72h",
},
"disable": {
Type: framework.TypeBool,
Description: `If set to true, disables generating the CRL entirely.`,
},
"ocsp_disable": {
Type: framework.TypeBool,
Description: `If set to true, ocsp unauthorized responses will be returned.`,
},
"ocsp_expiry": {
Type: framework.TypeString,
Description: `The amount of time an OCSP response will be valid (controls
the NextUpdate field); defaults to 12 hours`,
Default: "1h",
},
"auto_rebuild": {
Type: framework.TypeBool,
Description: `If set to true, enables automatic rebuilding of the CRL`,
},
"auto_rebuild_grace_period": {
Type: framework.TypeString,
Description: `The time before the CRL expires to automatically rebuild it, when enabled. Must be shorter than the CRL expiry. Defaults to 12h.`,
Default: "12h",
},
"enable_delta": {
Type: framework.TypeBool,
Description: `Whether to enable delta CRLs between authoritative CRL rebuilds`,
},
"delta_rebuild_interval": {
Type: framework.TypeString,
Description: `The time between delta CRL rebuilds if a new revocation has occurred. Must be shorter than the CRL expiry. Defaults to 15m.`,
Default: "15m",
},
"cross_cluster_revocation": {
Type: framework.TypeBool,
Description: `Whether to enable a global, cross-cluster revocation queue.
Must be used with auto_rebuild=true.`,
Required: false,
},
"unified_crl": {
Type: framework.TypeBool,
Description: `If set to true enables global replication of revocation entries,
also enabling unified versions of OCSP and CRLs if their respective features are enabled.
disable for CRLs and ocsp_disable for OCSP.`,
Required: false,
},
"unified_crl_on_existing_paths": {
Type: framework.TypeBool,
Description: `If set to true,
existing CRL and OCSP paths will return the unified CRL instead of a response based on cluster-local data`,
Required: false,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,

View File

@ -3,6 +3,7 @@ package pki
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/asaskevich/govalidator"
@ -48,9 +49,74 @@ to be set on all PR secondary clusters.`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathWriteURL,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"issuing_certificates": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the issuing certificate attribute. See also RFC 5280 Section 4.2.2.1.`,
},
"crl_distribution_points": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the CRL distribution points attribute. See also RFC 5280 Section 4.2.1.13.`,
},
"ocsp_servers": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the OCSP servers attribute. See also RFC 5280 Section 4.2.2.1.`,
},
"enable_templating": {
Type: framework.TypeBool,
Description: `Whether or not to enabling templating of the
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
and '{{cluster_path}}' are available, but the addresses are not checked for
URI validity until issuance time. This requires /config/cluster's path to be
set on all PR Secondary clusters.`,
Default: false,
},
},
}},
},
},
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathReadURL,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"issuing_certificates": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the issuing certificate attribute. See also RFC 5280 Section 4.2.2.1.`,
Required: true,
},
"crl_distribution_points": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the CRL distribution points attribute. See also RFC 5280 Section 4.2.1.13.`,
Required: true,
},
"ocsp_servers": {
Type: framework.TypeCommaStringSlice,
Description: `Comma-separated list of URLs to be used
for the OCSP servers attribute. See also RFC 5280 Section 4.2.2.1.`,
Required: true,
},
"enable_templating": {
Type: framework.TypeBool,
Description: `Whether or not to enable templating of the
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
and '{{cluster_path}}' are available, but the addresses are not checked for
URI validity until issuance time. This requires /config/cluster's path to be
set on all PR Secondary clusters.`,
Required: true,
},
},
}},
},
},
},

3
changelog/18376.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
openapi: Add openapi response definitions to pki/config_*.go
```