vault/builtin/logical/pki/path_config_ca.go
hashicorp-copywrite[bot] 0b12cdcfd1
[COMPLIANCE] License changes (#22290)
* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Updating the license from MPL to Business Source License.

Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl.

* add missing license headers

* Update copyright file headers to BUS-1.1

* Fix test that expected exact offset on hcl file

---------

Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
Co-authored-by: Sarah Thompson <sthompson@hashicorp.com>
Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
2023-08-10 18:14:03 -07:00

430 lines
13 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package pki
import (
"context"
"net/http"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
)
func pathConfigCA(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/ca",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixPKI,
OperationVerb: "configure",
OperationSuffix: "ca",
},
Fields: map[string]*framework.FieldSchema{
"pem_bundle": {
Type: framework.TypeString,
Description: `PEM-format, concatenated unencrypted
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,
},
"existing_keys": {
Type: framework.TypeCommaStringSlice,
Description: "Existing keys specified as part of the import bundle of this request",
Required: true,
},
"existing_issuers": {
Type: framework.TypeCommaStringSlice,
Description: "Existing issuers specified as part of the import bundle of this request",
Required: true,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
},
HelpSynopsis: pathConfigCAHelpSyn,
HelpDescription: pathConfigCAHelpDesc,
}
}
const pathConfigCAHelpSyn = `
Set the CA certificate and private key used for generated credentials.
`
const pathConfigCAHelpDesc = `
This sets the CA information used for credentials generated by this
by this mount. This must be a PEM-format, concatenated unencrypted
secret key and certificate.
For security reasons, the secret key cannot be retrieved later.
`
func pathConfigIssuers(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/issuers",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixPKI,
},
Fields: map[string]*framework.FieldSchema{
defaultRef: {
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.`,
Default: false,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathCAIssuersRead,
DisplayAttrs: &framework.DisplayAttributes{
OperationSuffix: "issuers-configuration",
},
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,
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "configure",
OperationSuffix: "issuers",
},
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,
},
},
HelpSynopsis: pathConfigIssuersHelpSyn,
HelpDescription: pathConfigIssuersHelpDesc,
}
}
func pathReplaceRoot(b *backend) *framework.Path {
return &framework.Path{
Pattern: "root/replace",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixPKI,
OperationVerb: "replace",
OperationSuffix: "root",
},
Fields: map[string]*framework.FieldSchema{
"default": {
Type: framework.TypeString,
Description: `Reference (name or identifier) to the default issuer.`,
Default: "next",
},
},
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,
},
},
HelpSynopsis: pathConfigIssuersHelpSyn,
HelpDescription: pathConfigIssuersHelpDesc,
}
}
func (b *backend) pathCAIssuersRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Cannot read defaults until migration has completed"), nil
}
sc := b.makeStorageContext(ctx, req.Storage)
config, err := sc.getIssuersConfig()
if err != nil {
return logical.ErrorResponse("Error loading issuers configuration: " + err.Error()), nil
}
return b.formatCAIssuerConfigRead(config), nil
}
func (b *backend) formatCAIssuerConfigRead(config *issuerConfigEntry) *logical.Response {
return &logical.Response{
Data: map[string]interface{}{
defaultRef: config.DefaultIssuerId,
"default_follows_latest_issuer": config.DefaultFollowsLatestIssuer,
},
}
}
func (b *backend) pathCAIssuersWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Since we're planning on updating issuers here, grab the lock so we've
// got a consistent view.
b.issuersLock.Lock()
defer b.issuersLock.Unlock()
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Cannot update defaults until migration has completed"), nil
}
sc := b.makeStorageContext(ctx, req.Storage)
// Validate the new default reference.
newDefault := data.Get(defaultRef).(string)
if len(newDefault) == 0 || newDefault == defaultRef {
return logical.ErrorResponse("Invalid issuer specification; must be non-empty and can't be 'default'."), nil
}
parsedIssuer, err := sc.resolveIssuerReference(newDefault)
if err != nil {
return logical.ErrorResponse("Error resolving issuer reference: " + err.Error()), nil
}
entry, err := sc.fetchIssuerById(parsedIssuer)
if err != nil {
return logical.ErrorResponse("Unable to fetch issuer: " + err.Error()), nil
}
// Get the other new parameters. This doesn't exist on the /root/replace
// variant of this call.
var followIssuer bool
followIssuersRaw, followOk := data.GetOk("default_follows_latest_issuer")
if followOk {
followIssuer = followIssuersRaw.(bool)
}
// Update the config
config, err := sc.getIssuersConfig()
if err != nil {
return logical.ErrorResponse("Unable to fetch existing issuers configuration: " + err.Error()), nil
}
config.DefaultIssuerId = parsedIssuer
if followOk {
config.DefaultFollowsLatestIssuer = followIssuer
}
// Add our warning if necessary.
response := b.formatCAIssuerConfigRead(config)
if len(entry.KeyID) == 0 {
msg := "This selected default issuer has no key associated with it. Some operations like issuing certificates and signing CRLs will be unavailable with the requested default issuer until a key is imported or the default issuer is changed."
response.AddWarning(msg)
b.Logger().Error(msg)
}
if err := sc.setIssuersConfig(config); err != nil {
return logical.ErrorResponse("Error updating issuer configuration: " + err.Error()), nil
}
return response, nil
}
const pathConfigIssuersHelpSyn = `Read and set the default issuer certificate for signing.`
const pathConfigIssuersHelpDesc = `
This path allows configuration of issuer parameters.
Presently, the "default" parameter controls which issuer is the default,
accessible by the existing signing paths (/root/sign-intermediate,
/root/sign-self-issued, /sign-verbatim, /sign/:role, and /issue/:role).
The /root/replace path is aliased to this path, with default taking the
value of the issuer with the name "next", if it exists.
`
func pathConfigKeys(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/keys",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: operationPrefixPKI,
},
Fields: map[string]*framework.FieldSchema{
defaultRef: {
Type: framework.TypeString,
Description: `Reference (name or identifier) of the default key.`,
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathKeyDefaultWrite,
DisplayAttrs: &framework.DisplayAttributes{
OperationVerb: "configure",
OperationSuffix: "keys",
},
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,
DisplayAttrs: &framework.DisplayAttributes{
OperationSuffix: "keys-configuration",
},
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,
},
},
HelpSynopsis: pathConfigKeysHelpSyn,
HelpDescription: pathConfigKeysHelpDesc,
}
}
func (b *backend) pathKeyDefaultRead(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Cannot read key defaults until migration has completed"), nil
}
sc := b.makeStorageContext(ctx, req.Storage)
config, err := sc.getKeysConfig()
if err != nil {
return logical.ErrorResponse("Error loading keys configuration: " + err.Error()), nil
}
return &logical.Response{
Data: map[string]interface{}{
defaultRef: config.DefaultKeyId,
},
}, nil
}
func (b *backend) pathKeyDefaultWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Since we're planning on updating keys here, grab the lock so we've
// got a consistent view.
b.issuersLock.Lock()
defer b.issuersLock.Unlock()
if b.useLegacyBundleCaStorage() {
return logical.ErrorResponse("Cannot update key defaults until migration has completed"), nil
}
newDefault := data.Get(defaultRef).(string)
if len(newDefault) == 0 || newDefault == defaultRef {
return logical.ErrorResponse("Invalid key specification; must be non-empty and can't be 'default'."), nil
}
sc := b.makeStorageContext(ctx, req.Storage)
parsedKey, err := sc.resolveKeyReference(newDefault)
if err != nil {
return logical.ErrorResponse("Error resolving issuer reference: " + err.Error()), nil
}
err = sc.updateDefaultKeyId(parsedKey)
if err != nil {
return logical.ErrorResponse("Error updating issuer configuration: " + err.Error()), nil
}
return &logical.Response{
Data: map[string]interface{}{
defaultRef: parsedKey,
},
}, nil
}
const pathConfigKeysHelpSyn = `Read and set the default key used for signing`
const pathConfigKeysHelpDesc = `
This path allows configuration of key parameters.
The "default" parameter controls which key is the default used by signing paths.
`