backport of commit 000d754c40b5daaae21e97dd548d3c308c7c6475 (#20870)
Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
a1d3c88f56
commit
da127db836
|
@ -269,5 +269,10 @@ func verifyEabPayload(acmeState *acmeState, ac *acmeContext, outer *jwsCtx, expe
|
||||||
return nil, fmt.Errorf("eab payload does not match outer JWK key: %w", ErrMalformed)
|
return nil, fmt.Errorf("eab payload does not match outer JWK key: %w", ErrMalformed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if eabEntry.AcmeDirectory != ac.acmeDirectory {
|
||||||
|
// This EAB was not created for this specific ACME directory, reject it
|
||||||
|
return nil, fmt.Errorf("%w: failed to verify eab", ErrUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
return eabEntry, nil
|
return eabEntry, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc {
|
||||||
return nil, fmt.Errorf("%w: Can not perform ACME operations until migration has completed", ErrServerInternal)
|
return nil, fmt.Errorf("%w: Can not perform ACME operations until migration has completed", ErrServerInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
acmeBaseUrl, clusterBase, err := getAcmeBaseUrl(sc, r.Path)
|
acmeBaseUrl, clusterBase, err := getAcmeBaseUrl(sc, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,10 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
acmeDirectory := getAcmeDirectory(data)
|
acmeDirectory, err := getAcmeDirectory(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
acmeCtx := &acmeContext{
|
acmeCtx := &acmeContext{
|
||||||
baseUrl: acmeBaseUrl,
|
baseUrl: acmeBaseUrl,
|
||||||
|
@ -237,19 +240,18 @@ func buildAcmeFrameworkPaths(b *backend, patternFunc func(b *backend, pattern st
|
||||||
return patterns
|
return patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAcmeBaseUrl(sc *storageContext, path string) (*url.URL, *url.URL, error) {
|
func getAcmeBaseUrl(sc *storageContext, r *logical.Request) (*url.URL, *url.URL, error) {
|
||||||
baseUrl, err := getBasePathFromClusterConfig(sc)
|
baseUrl, err := getBasePathFromClusterConfig(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryPrefix := ""
|
directoryPrefix, err := getAcmeDirectory(r)
|
||||||
lastIndex := strings.LastIndex(path, "/acme/")
|
if err != nil {
|
||||||
if lastIndex != -1 {
|
return nil, nil, err
|
||||||
directoryPrefix = path[0:lastIndex]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseUrl.JoinPath(directoryPrefix, "/acme/"), baseUrl, nil
|
return baseUrl.JoinPath(directoryPrefix), baseUrl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBasePathFromClusterConfig(sc *storageContext) (*url.URL, error) {
|
func getBasePathFromClusterConfig(sc *storageContext) (*url.URL, error) {
|
||||||
|
@ -291,11 +293,21 @@ func getAcmeIssuer(sc *storageContext, issuerName string) (*issuerEntry, error)
|
||||||
return nil, fmt.Errorf("%w: issuer missing proper issuance usage or key", ErrServerInternal)
|
return nil, fmt.Errorf("%w: issuer missing proper issuance usage or key", ErrServerInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAcmeDirectory(data *framework.FieldData) string {
|
// getAcmeDirectory return the base acme directory path, without a leading '/' and including
|
||||||
requestedIssuer := getRequestedAcmeIssuerFromPath(data)
|
// the trailing /acme/ folder which is the root of all our various directories
|
||||||
requestedRole := getRequestedAcmeRoleFromPath(data)
|
func getAcmeDirectory(r *logical.Request) (string, error) {
|
||||||
|
acmePath := r.Path
|
||||||
|
if !strings.HasPrefix(acmePath, "/") {
|
||||||
|
acmePath = "/" + acmePath
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("issuer-%s::role-%s", requestedIssuer, requestedRole)
|
lastIndex := strings.LastIndex(acmePath, "/acme/")
|
||||||
|
if lastIndex == -1 {
|
||||||
|
return "", fmt.Errorf("%w: unable to determine acme base folder path: %s", ErrServerInternal, acmePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the leading '/' and return our base path with the /acme/
|
||||||
|
return strings.TrimLeft(acmePath[0:lastIndex]+"/acme/", "/"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAcmeRoleAndIssuer(sc *storageContext, data *framework.FieldData, config *acmeConfigEntry) (*roleEntry, *issuerEntry, error) {
|
func getAcmeRoleAndIssuer(sc *storageContext, data *framework.FieldData, config *acmeConfigEntry) (*roleEntry, *issuerEntry, error) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ package pki
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/sdk/framework"
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
|
@ -92,15 +93,20 @@ func TestACMEIssuerRoleLoading(t *testing.T) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var acmePath string
|
||||||
fieldRaw := map[string]interface{}{}
|
fieldRaw := map[string]interface{}{}
|
||||||
if tt.roleName != "" {
|
|
||||||
fieldRaw["role"] = tt.roleName
|
|
||||||
}
|
|
||||||
if tt.issuerName != "" {
|
if tt.issuerName != "" {
|
||||||
fieldRaw[issuerRefParam] = tt.issuerName
|
fieldRaw[issuerRefParam] = tt.issuerName
|
||||||
|
acmePath = "issuer/" + tt.issuerName + "/"
|
||||||
|
}
|
||||||
|
if tt.roleName != "" {
|
||||||
|
fieldRaw["role"] = tt.roleName
|
||||||
|
acmePath = acmePath + "roles/" + tt.roleName + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := f(context.Background(), &logical.Request{Storage: s}, &framework.FieldData{
|
acmePath = strings.TrimLeft(acmePath+"/acme/directory", "/")
|
||||||
|
|
||||||
|
resp, err := f(context.Background(), &logical.Request{Path: acmePath, Storage: s}, &framework.FieldData{
|
||||||
Raw: fieldRaw,
|
Raw: fieldRaw,
|
||||||
Schema: getCsrSignVerbatimSchemaFields(),
|
Schema: getCsrSignVerbatimSchemaFields(),
|
||||||
})
|
})
|
||||||
|
|
|
@ -218,7 +218,6 @@ func Backend(conf *logical.BackendConfig) *backend {
|
||||||
|
|
||||||
// ACME
|
// ACME
|
||||||
pathAcmeConfig(&b),
|
pathAcmeConfig(&b),
|
||||||
pathAcmeEabCreate(&b),
|
|
||||||
pathAcmeEabList(&b),
|
pathAcmeEabList(&b),
|
||||||
pathAcmeEabDelete(&b),
|
pathAcmeEabDelete(&b),
|
||||||
},
|
},
|
||||||
|
@ -248,6 +247,7 @@ func Backend(conf *logical.BackendConfig) *backend {
|
||||||
acmePaths = append(acmePaths, pathAcmeChallenge(&b)...)
|
acmePaths = append(acmePaths, pathAcmeChallenge(&b)...)
|
||||||
acmePaths = append(acmePaths, pathAcmeAuthorization(&b)...)
|
acmePaths = append(acmePaths, pathAcmeAuthorization(&b)...)
|
||||||
acmePaths = append(acmePaths, pathAcmeRevoke(&b)...)
|
acmePaths = append(acmePaths, pathAcmeRevoke(&b)...)
|
||||||
|
acmePaths = append(acmePaths, pathAcmeNewEab(&b)...) // auth'd API that lives underneath the various /acme paths
|
||||||
|
|
||||||
for _, acmePath := range acmePaths {
|
for _, acmePath := range acmePaths {
|
||||||
b.Backend.Paths = append(b.Backend.Paths, acmePath)
|
b.Backend.Paths = append(b.Backend.Paths, acmePath)
|
||||||
|
@ -268,6 +268,7 @@ func Backend(conf *logical.BackendConfig) *backend {
|
||||||
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+")
|
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+")
|
||||||
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+/finalize")
|
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+/finalize")
|
||||||
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+/cert")
|
b.PathsSpecial.Unauthenticated = append(b.PathsSpecial.Unauthenticated, acmePrefix+"acme/order/+/cert")
|
||||||
|
// We specifically do NOT add acme/new-eab to this as it should be auth'd
|
||||||
}
|
}
|
||||||
|
|
||||||
if constants.IsEnterprise {
|
if constants.IsEnterprise {
|
||||||
|
|
|
@ -6862,9 +6862,8 @@ func TestProperAuthing(t *testing.T) {
|
||||||
"unified-crl/delta/pem": shouldBeUnauthedReadList,
|
"unified-crl/delta/pem": shouldBeUnauthedReadList,
|
||||||
"unified-ocsp": shouldBeUnauthedWriteOnly,
|
"unified-ocsp": shouldBeUnauthedWriteOnly,
|
||||||
"unified-ocsp/dGVzdAo=": shouldBeUnauthedReadList,
|
"unified-ocsp/dGVzdAo=": shouldBeUnauthedReadList,
|
||||||
"acme/new-eab": shouldBeAuthed,
|
"eab": shouldBeAuthed,
|
||||||
"acme/eab": shouldBeAuthed,
|
"eab/" + eabKid: shouldBeAuthed,
|
||||||
"acme/eab/" + eabKid: shouldBeAuthed,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ACME based paths to the test suite
|
// Add ACME based paths to the test suite
|
||||||
|
@ -6881,6 +6880,9 @@ func TestProperAuthing(t *testing.T) {
|
||||||
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90"] = shouldBeUnauthedWriteOnly
|
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90"] = shouldBeUnauthedWriteOnly
|
||||||
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90/finalize"] = shouldBeUnauthedWriteOnly
|
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90/finalize"] = shouldBeUnauthedWriteOnly
|
||||||
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90/cert"] = shouldBeUnauthedWriteOnly
|
paths[acmePrefix+"acme/order/13b80844-e60d-42d2-b7e9-152a8e834b90/cert"] = shouldBeUnauthedWriteOnly
|
||||||
|
|
||||||
|
// Make sure this new-eab path is auth'd
|
||||||
|
paths[acmePrefix+"acme/new-eab"] = shouldBeAuthed
|
||||||
}
|
}
|
||||||
|
|
||||||
for path, checkerType := range paths {
|
for path, checkerType := range paths {
|
||||||
|
@ -6938,7 +6940,7 @@ func TestProperAuthing(t *testing.T) {
|
||||||
if strings.Contains(raw_path, "acme/") && strings.Contains(raw_path, "{order_id}") {
|
if strings.Contains(raw_path, "acme/") && strings.Contains(raw_path, "{order_id}") {
|
||||||
raw_path = strings.ReplaceAll(raw_path, "{order_id}", "13b80844-e60d-42d2-b7e9-152a8e834b90")
|
raw_path = strings.ReplaceAll(raw_path, "{order_id}", "13b80844-e60d-42d2-b7e9-152a8e834b90")
|
||||||
}
|
}
|
||||||
if strings.Contains(raw_path, "acme/eab") && strings.Contains(raw_path, "{key_id}") {
|
if strings.Contains(raw_path, "eab") && strings.Contains(raw_path, "{key_id}") {
|
||||||
raw_path = strings.ReplaceAll(raw_path, "{key_id}", eabKid)
|
raw_path = strings.ReplaceAll(raw_path, "{key_id}", eabKid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
*/
|
*/
|
||||||
func pathAcmeEabList(b *backend) *framework.Path {
|
func pathAcmeEabList(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "acme/eab/?$",
|
Pattern: "eab/?$",
|
||||||
|
|
||||||
DisplayAttrs: &framework.DisplayAttributes{
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
OperationPrefix: operationPrefixPKI,
|
OperationPrefix: operationPrefixPKI,
|
||||||
|
@ -45,16 +45,17 @@ func pathAcmeEabList(b *backend) *framework.Path {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathAcmeEabCreate(b *backend) *framework.Path {
|
func pathAcmeNewEab(b *backend) []*framework.Path {
|
||||||
|
return buildAcmeFrameworkPaths(b, patternAcmeNewEab, "/new-eab")
|
||||||
|
}
|
||||||
|
|
||||||
|
func patternAcmeNewEab(b *backend, pattern string) *framework.Path {
|
||||||
|
fields := map[string]*framework.FieldSchema{}
|
||||||
|
addFieldsForACMEPath(fields, pattern)
|
||||||
|
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "acme/new-eab",
|
Pattern: pattern,
|
||||||
|
Fields: fields,
|
||||||
DisplayAttrs: &framework.DisplayAttributes{
|
|
||||||
OperationPrefix: operationPrefixPKI,
|
|
||||||
},
|
|
||||||
|
|
||||||
Fields: map[string]*framework.FieldSchema{},
|
|
||||||
|
|
||||||
Operations: map[logical.Operation]framework.OperationHandler{
|
Operations: map[logical.Operation]framework.OperationHandler{
|
||||||
logical.UpdateOperation: &framework.PathOperation{
|
logical.UpdateOperation: &framework.PathOperation{
|
||||||
Callback: b.pathAcmeCreateEab,
|
Callback: b.pathAcmeCreateEab,
|
||||||
|
@ -67,15 +68,18 @@ func pathAcmeEabCreate(b *backend) *framework.Path {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
HelpSynopsis: "Generate or list external account bindings to be used for ACME",
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
HelpDescription: `Generate single use id/key pairs to be used for ACME EAB or list
|
OperationPrefix: operationPrefixPKI,
|
||||||
identifiers that have been generated but yet to be used.`,
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: "Generate external account bindings to be used for ACME",
|
||||||
|
HelpDescription: `Generate single use id/key pairs to be used for ACME EAB.`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathAcmeEabDelete(b *backend) *framework.Path {
|
func pathAcmeEabDelete(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "acme/eab/" + uuidNameRegex("key_id"),
|
Pattern: "eab/" + uuidNameRegex("key_id"),
|
||||||
|
|
||||||
DisplayAttrs: &framework.DisplayAttributes{
|
DisplayAttrs: &framework.DisplayAttributes{
|
||||||
OperationPrefix: operationPrefixPKI,
|
OperationPrefix: operationPrefixPKI,
|
||||||
|
@ -113,6 +117,7 @@ type eabType struct {
|
||||||
KeyType string `json:"key-type"`
|
KeyType string `json:"key-type"`
|
||||||
KeyBits int `json:"key-bits"`
|
KeyBits int `json:"key-bits"`
|
||||||
PrivateBytes []byte `json:"private-bytes"`
|
PrivateBytes []byte `json:"private-bytes"`
|
||||||
|
AcmeDirectory string `json:"acme-directory"`
|
||||||
CreatedOn time.Time `json:"created-on"`
|
CreatedOn time.Time `json:"created-on"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +144,7 @@ func (b *backend) pathAcmeListEab(ctx context.Context, r *logical.Request, _ *fr
|
||||||
keyInfos[eab.KeyID] = map[string]interface{}{
|
keyInfos[eab.KeyID] = map[string]interface{}{
|
||||||
"key_type": eab.KeyType,
|
"key_type": eab.KeyType,
|
||||||
"key_bits": eab.KeyBits,
|
"key_bits": eab.KeyBits,
|
||||||
|
"acme_directory": eab.AcmeDirectory,
|
||||||
"created_on": eab.CreatedOn.Format(time.RFC3339),
|
"created_on": eab.CreatedOn.Format(time.RFC3339),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +156,7 @@ func (b *backend) pathAcmeListEab(ctx context.Context, r *logical.Request, _ *fr
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) pathAcmeCreateEab(ctx context.Context, r *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
func (b *backend) pathAcmeCreateEab(ctx context.Context, r *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
kid := genUuid()
|
kid := genUuid()
|
||||||
size := 32
|
size := 32
|
||||||
bytes, err := uuid.GenerateRandomBytesWithReader(size, rand.Reader)
|
bytes, err := uuid.GenerateRandomBytesWithReader(size, rand.Reader)
|
||||||
|
@ -158,11 +164,17 @@ func (b *backend) pathAcmeCreateEab(ctx context.Context, r *logical.Request, _ *
|
||||||
return nil, fmt.Errorf("failed generating eab key: %w", err)
|
return nil, fmt.Errorf("failed generating eab key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acmeDirectory, err := getAcmeDirectory(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
eab := &eabType{
|
eab := &eabType{
|
||||||
KeyID: kid,
|
KeyID: kid,
|
||||||
KeyType: "hs",
|
KeyType: "hs",
|
||||||
KeyBits: size * 8,
|
KeyBits: size * 8,
|
||||||
PrivateBytes: bytes,
|
PrivateBytes: bytes,
|
||||||
|
AcmeDirectory: acmeDirectory,
|
||||||
CreatedOn: time.Now(),
|
CreatedOn: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +192,7 @@ func (b *backend) pathAcmeCreateEab(ctx context.Context, r *logical.Request, _ *
|
||||||
"key_type": eab.KeyType,
|
"key_type": eab.KeyType,
|
||||||
"key_bits": eab.KeyBits,
|
"key_bits": eab.KeyBits,
|
||||||
"key": encodedKey,
|
"key": encodedKey,
|
||||||
|
"acme_directory": eab.AcmeDirectory,
|
||||||
"created_on": eab.CreatedOn.Format(time.RFC3339),
|
"created_on": eab.CreatedOn.Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package pki
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-uuid"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestACME_EabVaultAPIs verify the various Vault auth'd APIs for EAB work as expected,
|
|
||||||
// with creation, listing and deletions.
|
|
||||||
func TestACME_EabVaultAPIs(t *testing.T) {
|
|
||||||
b, s := CreateBackendWithStorage(t)
|
|
||||||
|
|
||||||
var ids []string
|
|
||||||
|
|
||||||
// Generate an EAB
|
|
||||||
resp, err := CBWrite(b, s, "acme/new-eab", map[string]interface{}{})
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "Failed generating eab")
|
|
||||||
requireFieldsSetInResp(t, resp, "id", "key_type", "key_bits", "key", "created_on")
|
|
||||||
require.Equal(t, "hs", resp.Data["key_type"])
|
|
||||||
require.Equal(t, 256, resp.Data["key_bits"])
|
|
||||||
ids = append(ids, resp.Data["id"].(string))
|
|
||||||
_, err = uuid.ParseUUID(resp.Data["id"].(string))
|
|
||||||
require.NoError(t, err, "failed parsing id as a uuid")
|
|
||||||
|
|
||||||
_, err = base64.RawURLEncoding.DecodeString(resp.Data["key"].(string))
|
|
||||||
require.NoError(t, err, "failed base64 decoding private key")
|
|
||||||
require.NoError(t, err, "failed parsing private key")
|
|
||||||
|
|
||||||
// Generate another EAB
|
|
||||||
resp, err = CBWrite(b, s, "acme/new-eab", map[string]interface{}{})
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "Failed generating eab")
|
|
||||||
ids = append(ids, resp.Data["id"].(string))
|
|
||||||
|
|
||||||
// List our EABs
|
|
||||||
resp, err = CBList(b, s, "acme/eab/")
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "failed list")
|
|
||||||
|
|
||||||
require.ElementsMatch(t, ids, resp.Data["keys"])
|
|
||||||
keyInfo := resp.Data["key_info"].(map[string]interface{})
|
|
||||||
id0Map := keyInfo[ids[0]].(map[string]interface{})
|
|
||||||
require.Equal(t, "hs", id0Map["key_type"])
|
|
||||||
require.Equal(t, 256, id0Map["key_bits"])
|
|
||||||
require.NotEmpty(t, id0Map["created_on"])
|
|
||||||
_, err = time.Parse(time.RFC3339, id0Map["created_on"].(string))
|
|
||||||
require.NoError(t, err, "failed to parse created_on date: %s", id0Map["created_on"])
|
|
||||||
|
|
||||||
id1Map := keyInfo[ids[1]].(map[string]interface{})
|
|
||||||
|
|
||||||
require.Equal(t, "hs", id1Map["key_type"])
|
|
||||||
require.Equal(t, 256, id1Map["key_bits"])
|
|
||||||
require.NotEmpty(t, id1Map["created_on"])
|
|
||||||
|
|
||||||
// Delete an EAB
|
|
||||||
resp, err = CBDelete(b, s, "acme/eab/"+ids[0])
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "failed deleting eab identifier")
|
|
||||||
require.Len(t, resp.Warnings, 0, "no warnings should have been set on delete")
|
|
||||||
|
|
||||||
// Make sure it's really gone
|
|
||||||
resp, err = CBList(b, s, "acme/eab/")
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "failed list post delete")
|
|
||||||
require.Len(t, resp.Data["keys"], 1)
|
|
||||||
require.Contains(t, resp.Data["keys"], ids[1])
|
|
||||||
|
|
||||||
// Delete the same EAB again, we should just get a warning but still success.
|
|
||||||
resp, err = CBDelete(b, s, "acme/eab/"+ids[0])
|
|
||||||
requireSuccessNonNilResponse(t, resp, err, "failed deleting eab identifier")
|
|
||||||
require.Len(t, resp.Warnings, 1, "expected a warning to be set on repeated delete call")
|
|
||||||
}
|
|
|
@ -45,16 +45,16 @@ func TestAcmeBasicWorkflow(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
prefixUrl string
|
prefixUrl string
|
||||||
}{
|
}{
|
||||||
{"root", ""},
|
{"root", "acme/"},
|
||||||
{"role", "/roles/test-role"},
|
{"role", "roles/test-role/acme/"},
|
||||||
{"issuer", "/issuer/int-ca"},
|
{"issuer", "issuer/int-ca/acme/"},
|
||||||
{"issuer_role", "/issuer/int-ca/roles/test-role"},
|
{"issuer_role", "issuer/int-ca/roles/test-role/acme/"},
|
||||||
}
|
}
|
||||||
testCtx := context.Background()
|
testCtx := context.Background()
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
baseAcmeURL := "/v1/pki" + tc.prefixUrl + "/acme/"
|
baseAcmeURL := "/v1/pki/" + tc.prefixUrl
|
||||||
accountKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
accountKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
require.NoError(t, err, "failed creating rsa key")
|
require.NoError(t, err, "failed creating rsa key")
|
||||||
|
|
||||||
|
@ -329,7 +329,19 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
baseAcmeURL := "/v1/pki/acme/"
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
prefixUrl string
|
||||||
|
}{
|
||||||
|
{"root", "acme/"},
|
||||||
|
{"role", "roles/test-role/acme/"},
|
||||||
|
{"issuer", "issuer/int-ca/acme/"},
|
||||||
|
{"issuer_role", "issuer/int-ca/roles/test-role/acme/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
baseAcmeURL := "/v1/pki/" + tc.prefixUrl
|
||||||
accountKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
accountKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err, "failed creating ec key")
|
require.NoError(t, err, "failed creating ec key")
|
||||||
|
|
||||||
|
@ -346,7 +358,26 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
require.ErrorContains(t, err, "urn:ietf:params:acme:error:externalAccountRequired",
|
require.ErrorContains(t, err, "urn:ietf:params:acme:error:externalAccountRequired",
|
||||||
"expected failure creating an account without eab")
|
"expected failure creating an account without eab")
|
||||||
|
|
||||||
kid, eabKeyBytes := getEABKey(t, client)
|
// Test fetch, list, delete workflow
|
||||||
|
kid, _ := getEABKey(t, client, tc.prefixUrl)
|
||||||
|
resp, err := client.Logical().ListWithContext(testCtx, "pki/eab")
|
||||||
|
require.NoError(t, err, "failed to list eab tokens")
|
||||||
|
require.NotNil(t, resp, "list response for eab tokens should not be nil")
|
||||||
|
require.Contains(t, resp.Data, "keys")
|
||||||
|
require.Contains(t, resp.Data, "key_info")
|
||||||
|
require.Len(t, resp.Data["keys"], 1)
|
||||||
|
require.Contains(t, resp.Data["keys"], kid)
|
||||||
|
|
||||||
|
_, err = client.Logical().DeleteWithContext(testCtx, "pki/eab/"+kid)
|
||||||
|
require.NoError(t, err, "failed to delete eab")
|
||||||
|
|
||||||
|
// List eabs should return zero results
|
||||||
|
resp, err = client.Logical().ListWithContext(testCtx, "pki/eab")
|
||||||
|
require.NoError(t, err, "failed to list eab tokens")
|
||||||
|
require.Nil(t, resp, "list response for eab tokens should have been nil")
|
||||||
|
|
||||||
|
// fetch a new EAB
|
||||||
|
kid, eabKeyBytes := getEABKey(t, client, tc.prefixUrl)
|
||||||
acct := &acme.Account{
|
acct := &acme.Account{
|
||||||
ExternalAccountBinding: &acme.ExternalAccountBinding{
|
ExternalAccountBinding: &acme.ExternalAccountBinding{
|
||||||
KID: kid,
|
KID: kid,
|
||||||
|
@ -355,7 +386,7 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can list our key
|
// Make sure we can list our key
|
||||||
resp, err := client.Logical().ListWithContext(context.Background(), "pki/acme/eab")
|
resp, err = client.Logical().ListWithContext(testCtx, "pki/eab")
|
||||||
require.NoError(t, err, "failed to list eab tokens")
|
require.NoError(t, err, "failed to list eab tokens")
|
||||||
require.NotNil(t, resp, "list response for eab tokens should not be nil")
|
require.NotNil(t, resp, "list response for eab tokens should not be nil")
|
||||||
require.Contains(t, resp.Data, "keys")
|
require.Contains(t, resp.Data, "keys")
|
||||||
|
@ -370,6 +401,7 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
keyBits := infoForKid["key_bits"].(json.Number)
|
keyBits := infoForKid["key_bits"].(json.Number)
|
||||||
require.Equal(t, "256", keyBits.String())
|
require.Equal(t, "256", keyBits.String())
|
||||||
require.Equal(t, "hs", infoForKid["key_type"])
|
require.Equal(t, "hs", infoForKid["key_type"])
|
||||||
|
require.Equal(t, tc.prefixUrl, infoForKid["acme_directory"])
|
||||||
|
|
||||||
// Create new account with EAB
|
// Create new account with EAB
|
||||||
t.Logf("Testing register on %s", baseAcmeURL)
|
t.Logf("Testing register on %s", baseAcmeURL)
|
||||||
|
@ -377,7 +409,7 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
require.NoError(t, err, "failed registering new account with eab")
|
require.NoError(t, err, "failed registering new account with eab")
|
||||||
|
|
||||||
// Make sure our EAB is no longer available
|
// Make sure our EAB is no longer available
|
||||||
resp, err = client.Logical().ListWithContext(context.Background(), "pki/acme/eab")
|
resp, err = client.Logical().ListWithContext(context.Background(), "pki/eab")
|
||||||
require.NoError(t, err, "failed to list eab tokens")
|
require.NoError(t, err, "failed to list eab tokens")
|
||||||
require.Nil(t, resp, "list response for eab tokens should have been nil due to empty list")
|
require.Nil(t, resp, "list response for eab tokens should have been nil due to empty list")
|
||||||
|
|
||||||
|
@ -399,6 +431,8 @@ func TestAcmeBasicWorkflowWithEab(t *testing.T) {
|
||||||
// We can lookup/find an existing account without EAB if we have the account key
|
// We can lookup/find an existing account without EAB if we have the account key
|
||||||
_, err = acmeClient.GetReg(testCtx /* unused url */, "")
|
_, err = acmeClient.GetReg(testCtx /* unused url */, "")
|
||||||
require.NoError(t, err, "expected to lookup existing account without eab")
|
require.NoError(t, err, "expected to lookup existing account without eab")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAcmeNonce a basic test that will validate we get back a nonce with the proper status codes
|
// TestAcmeNonce a basic test that will validate we get back a nonce with the proper status codes
|
||||||
|
@ -540,6 +574,41 @@ func TestAcmeAccountsCrossingDirectoryPath(t *testing.T) {
|
||||||
// swallows the error we are sending back to a no account error
|
// swallows the error we are sending back to a no account error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAcmeEabCrossingDirectoryPath make sure that if an account attempts to use a different ACME
|
||||||
|
// directory path that an EAB was created within we get an error.
|
||||||
|
func TestAcmeEabCrossingDirectoryPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cluster, client, _ := setupAcmeBackend(t)
|
||||||
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
|
// Enable EAB
|
||||||
|
_, err := client.Logical().WriteWithContext(context.Background(), "pki/config/acme", map[string]interface{}{
|
||||||
|
"enabled": true,
|
||||||
|
"eab_policy": "always-required",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
baseAcmeURL := "/v1/pki/acme/"
|
||||||
|
accountKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
require.NoError(t, err, "failed creating rsa key")
|
||||||
|
|
||||||
|
testCtx := context.Background()
|
||||||
|
acmeClient := getAcmeClientForCluster(t, cluster, baseAcmeURL, accountKey)
|
||||||
|
|
||||||
|
// fetch a new EAB
|
||||||
|
kid, eabKeyBytes := getEABKey(t, client, "roles/test-role/acme/")
|
||||||
|
acct := &acme.Account{
|
||||||
|
ExternalAccountBinding: &acme.ExternalAccountBinding{
|
||||||
|
KID: kid,
|
||||||
|
Key: eabKeyBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new account
|
||||||
|
_, err = acmeClient.Register(testCtx, acct, func(tosURL string) bool { return true })
|
||||||
|
require.ErrorContains(t, err, "failed to verify eab", "should have failed as EAB is for a different directory")
|
||||||
|
}
|
||||||
|
|
||||||
// TestAcmeDisabledWithEnvVar verifies if VAULT_DISABLE_PUBLIC_ACME is set that we completely
|
// TestAcmeDisabledWithEnvVar verifies if VAULT_DISABLE_PUBLIC_ACME is set that we completely
|
||||||
// disable the ACME service
|
// disable the ACME service
|
||||||
func TestAcmeDisabledWithEnvVar(t *testing.T) {
|
func TestAcmeDisabledWithEnvVar(t *testing.T) {
|
||||||
|
@ -1010,8 +1079,8 @@ func getAcmeClientForCluster(t *testing.T, cluster *vault.TestCluster, baseUrl s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEABKey(t *testing.T, client *api.Client) (string, []byte) {
|
func getEABKey(t *testing.T, client *api.Client, baseUrl string) (string, []byte) {
|
||||||
resp, err := client.Logical().WriteWithContext(ctx, "pki/acme/new-eab", map[string]interface{}{})
|
resp, err := client.Logical().WriteWithContext(ctx, path.Join("pki/", baseUrl, "/new-eab"), map[string]interface{}{})
|
||||||
require.NoError(t, err, "failed getting eab key")
|
require.NoError(t, err, "failed getting eab key")
|
||||||
require.NotNil(t, resp, "eab key returned nil response")
|
require.NotNil(t, resp, "eab key returned nil response")
|
||||||
require.NotEmpty(t, resp.Data["id"], "eab key response missing id field")
|
require.NotEmpty(t, resp.Data["id"], "eab key response missing id field")
|
||||||
|
@ -1022,5 +1091,12 @@ func getEABKey(t *testing.T, client *api.Client) (string, []byte) {
|
||||||
privateKeyBytes, err := base64.RawURLEncoding.DecodeString(base64Key)
|
privateKeyBytes, err := base64.RawURLEncoding.DecodeString(base64Key)
|
||||||
require.NoError(t, err, "failed base 64 decoding eab key response")
|
require.NoError(t, err, "failed base 64 decoding eab key response")
|
||||||
|
|
||||||
|
require.Equal(t, "hs", resp.Data["key_type"], "eab key_type field mis-match")
|
||||||
|
require.Equal(t, json.Number("256"), resp.Data["key_bits"], "eab key_bits field mis-match")
|
||||||
|
require.Equal(t, baseUrl, resp.Data["acme_directory"], "eab acme_directory field mis-match")
|
||||||
|
require.NotEmpty(t, resp.Data["created_on"], "empty created_on field")
|
||||||
|
_, err = time.Parse(time.RFC3339, resp.Data["created_on"].(string))
|
||||||
|
require.NoError(t, err, "failed parsing eab created_on field")
|
||||||
|
|
||||||
return kid, privateKeyBytes
|
return kid, privateKeyBytes
|
||||||
}
|
}
|
||||||
|
|
|
@ -1529,7 +1529,7 @@ func (b *backend) doTidyAcme(ctx context.Context, req *logical.Request, logger h
|
||||||
b.tidyStatus.acmeAccountsCount = uint(len(thumbprints))
|
b.tidyStatus.acmeAccountsCount = uint(len(thumbprints))
|
||||||
b.tidyStatusLock.Unlock()
|
b.tidyStatusLock.Unlock()
|
||||||
|
|
||||||
baseUrl, _, err := getAcmeBaseUrl(sc, req.Path)
|
baseUrl, _, err := getAcmeBaseUrl(sc, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ func Test_ACME(t *testing.T) {
|
||||||
|
|
||||||
tc := map[string]func(t *testing.T, cluster *VaultPkiCluster){
|
tc := map[string]func(t *testing.T, cluster *VaultPkiCluster){
|
||||||
"certbot": SubtestACMECertbot,
|
"certbot": SubtestACMECertbot,
|
||||||
|
"certbot eab": SubtestACMECertbotEab,
|
||||||
"acme ip sans": SubtestACMEIPAndDNS,
|
"acme ip sans": SubtestACMEIPAndDNS,
|
||||||
"acme wildcard": SubtestACMEWildcardDNS,
|
"acme wildcard": SubtestACMEWildcardDNS,
|
||||||
"acme prevents ica": SubtestACMEPreventsICADNS,
|
"acme prevents ica": SubtestACMEPreventsICADNS,
|
||||||
|
@ -153,6 +154,111 @@ func SubtestACMECertbot(t *testing.T, cluster *VaultPkiCluster) {
|
||||||
require.NotEqual(t, 0, retcode, "expected non-zero retcode double revoke command result")
|
require.NotEqual(t, 0, retcode, "expected non-zero retcode double revoke command result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SubtestACMECertbotEab(t *testing.T, cluster *VaultPkiCluster) {
|
||||||
|
mountName := "pki-certbot-eab"
|
||||||
|
pki, err := cluster.CreateAcmeMount(mountName)
|
||||||
|
require.NoError(t, err, "failed setting up acme mount")
|
||||||
|
|
||||||
|
err = pki.UpdateAcmeConfig(true, map[string]interface{}{
|
||||||
|
"eab_policy": "new-account-required",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
eabId, base64EabKey, err := pki.GetEabKey("acme/")
|
||||||
|
|
||||||
|
directory := "https://" + pki.GetActiveContainerIP() + ":8200/v1/" + mountName + "/acme/directory"
|
||||||
|
vaultNetwork := pki.GetContainerNetworkName()
|
||||||
|
|
||||||
|
logConsumer, logStdout, logStderr := getDockerLog(t)
|
||||||
|
|
||||||
|
t.Logf("creating on network: %v", vaultNetwork)
|
||||||
|
runner, err := hDocker.NewServiceRunner(hDocker.RunOptions{
|
||||||
|
ImageRepo: "docker.mirror.hashicorp.services/certbot/certbot",
|
||||||
|
ImageTag: "latest",
|
||||||
|
ContainerName: "vault_pki_certbot_eab_test",
|
||||||
|
NetworkName: vaultNetwork,
|
||||||
|
Entrypoint: []string{"sleep", "45"},
|
||||||
|
LogConsumer: logConsumer,
|
||||||
|
LogStdout: logStdout,
|
||||||
|
LogStderr: logStderr,
|
||||||
|
})
|
||||||
|
require.NoError(t, err, "failed creating service runner")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := runner.Start(ctx, true, false)
|
||||||
|
require.NoError(t, err, "could not start container")
|
||||||
|
require.NotNil(t, result, "could not start container")
|
||||||
|
|
||||||
|
defer runner.Stop(context.Background(), result.Container.ID)
|
||||||
|
|
||||||
|
networks, err := runner.GetNetworkAndAddresses(result.Container.ID)
|
||||||
|
require.NoError(t, err, "could not read container's IP address")
|
||||||
|
require.Contains(t, networks, vaultNetwork, "expected to contain vault network")
|
||||||
|
|
||||||
|
ipAddr := networks[vaultNetwork]
|
||||||
|
hostname := "certbot-eab-acme-client.dadgarcorp.com"
|
||||||
|
|
||||||
|
err = pki.AddHostname(hostname, ipAddr)
|
||||||
|
require.NoError(t, err, "failed to update vault host files")
|
||||||
|
|
||||||
|
certbotCmd := []string{
|
||||||
|
"certbot",
|
||||||
|
"certonly",
|
||||||
|
"--no-eff-email",
|
||||||
|
"--email", "certbot.client@dadgarcorp.com",
|
||||||
|
"--eab-kid", eabId,
|
||||||
|
"--eab-hmac-key", base64EabKey,
|
||||||
|
"--agree-tos",
|
||||||
|
"--no-verify-ssl",
|
||||||
|
"--standalone",
|
||||||
|
"--non-interactive",
|
||||||
|
"--server", directory,
|
||||||
|
"-d", hostname,
|
||||||
|
}
|
||||||
|
logCatCmd := []string{"cat", "/var/log/letsencrypt/letsencrypt.log"}
|
||||||
|
|
||||||
|
stdout, stderr, retcode, err := runner.RunCmdWithOutput(ctx, result.Container.ID, certbotCmd)
|
||||||
|
t.Logf("Certbot Issue Command: %v\nstdout: %v\nstderr: %v\n", certbotCmd, string(stdout), string(stderr))
|
||||||
|
if err != nil || retcode != 0 {
|
||||||
|
logsStdout, logsStderr, _, _ := runner.RunCmdWithOutput(ctx, result.Container.ID, logCatCmd)
|
||||||
|
t.Logf("Certbot logs\nstdout: %v\nstderr: %v\n", string(logsStdout), string(logsStderr))
|
||||||
|
}
|
||||||
|
require.NoError(t, err, "got error running issue command")
|
||||||
|
require.Equal(t, 0, retcode, "expected zero retcode issue command result")
|
||||||
|
|
||||||
|
certbotRevokeCmd := []string{
|
||||||
|
"certbot",
|
||||||
|
"revoke",
|
||||||
|
"--no-eff-email",
|
||||||
|
"--email", "certbot.client@dadgarcorp.com",
|
||||||
|
"--agree-tos",
|
||||||
|
"--no-verify-ssl",
|
||||||
|
"--non-interactive",
|
||||||
|
"--no-delete-after-revoke",
|
||||||
|
"--cert-name", hostname,
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, stderr, retcode, err = runner.RunCmdWithOutput(ctx, result.Container.ID, certbotRevokeCmd)
|
||||||
|
t.Logf("Certbot Revoke Command: %v\nstdout: %v\nstderr: %v\n", certbotRevokeCmd, string(stdout), string(stderr))
|
||||||
|
if err != nil || retcode != 0 {
|
||||||
|
logsStdout, logsStderr, _, _ := runner.RunCmdWithOutput(ctx, result.Container.ID, logCatCmd)
|
||||||
|
t.Logf("Certbot logs\nstdout: %v\nstderr: %v\n", string(logsStdout), string(logsStderr))
|
||||||
|
}
|
||||||
|
require.NoError(t, err, "got error running revoke command")
|
||||||
|
require.Equal(t, 0, retcode, "expected zero retcode revoke command result")
|
||||||
|
|
||||||
|
// Revoking twice should fail.
|
||||||
|
stdout, stderr, retcode, err = runner.RunCmdWithOutput(ctx, result.Container.ID, certbotRevokeCmd)
|
||||||
|
t.Logf("Certbot Double Revoke Command: %v\nstdout: %v\nstderr: %v\n", certbotRevokeCmd, string(stdout), string(stderr))
|
||||||
|
if err != nil || retcode == 0 {
|
||||||
|
logsStdout, logsStderr, _, _ := runner.RunCmdWithOutput(ctx, result.Container.ID, logCatCmd)
|
||||||
|
t.Logf("Certbot logs\nstdout: %v\nstderr: %v\n", string(logsStdout), string(logsStderr))
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err, "got error running double revoke command")
|
||||||
|
require.NotEqual(t, 0, retcode, "expected non-zero retcode double revoke command result")
|
||||||
|
}
|
||||||
|
|
||||||
func SubtestACMEIPAndDNS(t *testing.T, cluster *VaultPkiCluster) {
|
func SubtestACMEIPAndDNS(t *testing.T, cluster *VaultPkiCluster) {
|
||||||
pki, err := cluster.CreateAcmeMount("pki-ip-dns-sans")
|
pki, err := cluster.CreateAcmeMount("pki-ip-dns-sans")
|
||||||
require.NoError(t, err, "failed setting up acme mount")
|
require.NoError(t, err, "failed setting up acme mount")
|
||||||
|
|
|
@ -5,7 +5,9 @@ package pkiext_binary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
)
|
)
|
||||||
|
@ -114,6 +116,23 @@ func (vpm *VaultPkiMount) UpdateRole(roleName string, config map[string]interfac
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vpm *VaultPkiMount) GetEabKey(acmeDirectory string) (string, string, error) {
|
||||||
|
eabPath := path.Join(vpm.mount, acmeDirectory, "/new-eab")
|
||||||
|
resp, err := vpm.GetActiveNode().Logical().WriteWithContext(context.Background(), eabPath, map[string]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("failed fetching eab from %s: %w", eabPath, err)
|
||||||
|
}
|
||||||
|
eabId := resp.Data["id"].(string)
|
||||||
|
base64EabKey := resp.Data["key"].(string)
|
||||||
|
// just make sure we get something valid back from the server, we still want to pass back the base64 version
|
||||||
|
// to the caller...
|
||||||
|
_, err = base64.RawURLEncoding.DecodeString(base64EabKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("failed decoding key response field: %s: %w", base64EabKey, err)
|
||||||
|
}
|
||||||
|
return eabId, base64EabKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
func mergeWithDefaults(config map[string]interface{}, defaults map[string]interface{}) map[string]interface{} {
|
func mergeWithDefaults(config map[string]interface{}, defaults map[string]interface{}) map[string]interface{} {
|
||||||
myConfig := config
|
myConfig := config
|
||||||
if myConfig == nil {
|
if myConfig == nil {
|
||||||
|
|
Loading…
Reference in New Issue