mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-18 04:27:02 +02:00
* Adds automated ACME tests using Caddy. * Do not use CheckSignatureFrom method to validate TLS-ALPN-01 challenges * Uncomment TLS-ALPN test. * Fix validation of tls-alpn-01 keyAuthz Surprisingly, this failure was not caught by our earlier, but unmerged acme.sh tests: > 2023-06-07T19:35:27.6963070Z [32mPASS[0m builtin/logical/pkiext/pkiext_binary.Test_ACME/group/acme.sh_tls-alpn (33.06s) from https://github.com/hashicorp/vault/pull/20987. Notably, we had two failures: 1. The extension's raw value is not used, but is instead an OCTET STRING encoded version: > The extension has the following ASN.1 [X.680] format : > > Authorization ::= OCTET STRING (SIZE (32)) > > The extnValue of the id-pe-acmeIdentifier extension is the ASN.1 > DER encoding [X.690] of the Authorization structure, which > contains the SHA-256 digest of the key authorization for the > challenge. 2. Unlike DNS, the SHA-256 is directly embedded in the authorization, as evidenced by the `SIZE (32)` annotation in the quote above: we were instead expecting this to be url base-64 encoded, which would have a different size. This failure was caught by Matt, testing with Caddy. :-) Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Quick gofmt run. * Fix challenge encoding in TLS-ALPN-01 challenge tests * Rename a PKI test helper that retrieves the Vault cluster listener's cert to distinguish it from the method that retrieves the PKI mount's CA cert. Combine a couple of Docker file copy commands into one. --------- Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> Co-authored-by: Steve Clark <steven.clark@hashicorp.com> Co-authored-by: Alexander Scheel <alex.scheel@hashicorp.com>
161 lines
5.1 KiB
Go
161 lines
5.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package pkiext_binary
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"path"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
type VaultPkiMount struct {
|
|
*VaultPkiCluster
|
|
mount string
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateClusterConfig(config map[string]interface{}) error {
|
|
defaultPath := "https://" + vpm.cluster.ClusterNodes[0].ContainerIPAddress + ":8200/v1/" + vpm.mount
|
|
defaults := map[string]interface{}{
|
|
"path": defaultPath,
|
|
"aia_path": defaultPath,
|
|
}
|
|
|
|
_, err := vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/config/cluster", mergeWithDefaults(config, defaults))
|
|
return err
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateClusterConfigLocalAddr() (string, error) {
|
|
basePath := fmt.Sprintf("https://%s/v1/%s", vpm.GetActiveContainerHostPort(), vpm.mount)
|
|
return basePath, vpm.UpdateClusterConfig(map[string]interface{}{
|
|
"path": basePath,
|
|
})
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateAcmeConfig(enable bool, config map[string]interface{}) error {
|
|
defaults := map[string]interface{}{
|
|
"enabled": enable,
|
|
}
|
|
|
|
_, err := vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/config/acme", mergeWithDefaults(config, defaults))
|
|
return err
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) GenerateRootInternal(props map[string]interface{}) (*api.Secret, error) {
|
|
defaults := map[string]interface{}{
|
|
"common_name": "root-test.com",
|
|
"key_type": "ec",
|
|
"issuer_name": "root",
|
|
}
|
|
|
|
return vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/root/generate/internal", mergeWithDefaults(props, defaults))
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) GenerateIntermediateInternal(props map[string]interface{}) (*api.Secret, error) {
|
|
defaults := map[string]interface{}{
|
|
"common_name": "intermediary-test.com",
|
|
"key_type": "ec",
|
|
"issuer_name": "intermediary",
|
|
}
|
|
|
|
return vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/intermediate/generate/internal", mergeWithDefaults(props, defaults))
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) SignIntermediary(signingIssuer string, csr interface{}, props map[string]interface{}) (*api.Secret, error) {
|
|
defaults := map[string]interface{}{
|
|
"csr": csr,
|
|
}
|
|
|
|
return vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/issuer/"+signingIssuer+"/sign-intermediate",
|
|
mergeWithDefaults(props, defaults))
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) ImportBundle(pemBundle interface{}, props map[string]interface{}) (*api.Secret, error) {
|
|
defaults := map[string]interface{}{
|
|
"pem_bundle": pemBundle,
|
|
}
|
|
|
|
return vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/issuers/import/bundle", mergeWithDefaults(props, defaults))
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateDefaultIssuer(issuerId string, props map[string]interface{}) error {
|
|
defaults := map[string]interface{}{
|
|
"default": issuerId,
|
|
}
|
|
|
|
_, err := vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/config/issuers", mergeWithDefaults(props, defaults))
|
|
|
|
return err
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateIssuer(issuerRef string, props map[string]interface{}) error {
|
|
defaults := map[string]interface{}{}
|
|
|
|
_, err := vpm.GetActiveNode().Logical().JSONMergePatch(context.Background(),
|
|
vpm.mount+"/issuer/"+issuerRef, mergeWithDefaults(props, defaults))
|
|
|
|
return err
|
|
}
|
|
|
|
func (vpm *VaultPkiMount) UpdateRole(roleName string, config map[string]interface{}) error {
|
|
defaults := map[string]interface{}{}
|
|
|
|
_, err := vpm.GetActiveNode().Logical().WriteWithContext(context.Background(),
|
|
vpm.mount+"/roles/"+roleName, mergeWithDefaults(config, defaults))
|
|
|
|
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
|
|
}
|
|
|
|
// GetCACertPEM retrieves the PKI mount's PEM-encoded CA certificate.
|
|
func (vpm *VaultPkiMount) GetCACertPEM() (string, error) {
|
|
caCertPath := path.Join(vpm.mount, "/cert/ca")
|
|
resp, err := vpm.GetActiveNode().Logical().ReadWithContext(context.Background(), caCertPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return resp.Data["certificate"].(string), nil
|
|
}
|
|
|
|
func mergeWithDefaults(config map[string]interface{}, defaults map[string]interface{}) map[string]interface{} {
|
|
myConfig := config
|
|
if myConfig == nil {
|
|
myConfig = map[string]interface{}{}
|
|
}
|
|
for key, value := range defaults {
|
|
if origVal, exists := config[key]; !exists {
|
|
myConfig[key] = value
|
|
} else {
|
|
myConfig[key] = origVal
|
|
}
|
|
}
|
|
|
|
return myConfig
|
|
}
|