mirror of https://github.com/hashicorp/boundary
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
12 KiB
370 lines
12 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
// Package vault provides methods for commonly used vault actions that are used in end-to-end tests.
|
|
package vault
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/boundary/testing/internal/e2e"
|
|
"github.com/hashicorp/go-secure-stdlib/base62"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// CreateTokenResponse parses the json response from running `vault token create`
|
|
type CreateTokenResponse struct {
|
|
Auth struct {
|
|
Client_Token string
|
|
}
|
|
}
|
|
|
|
// SetupForBoundaryController verifies if appropriate credentials are set and
|
|
// adds the boundary controller policy to vault. Returns the policy name.
|
|
func SetupForBoundaryController(t testing.TB, boundaryControllerFilePath string) (boundaryPolicyName string) {
|
|
// Set up boundary policy
|
|
boundaryPolicyFilePath, err := filepath.Abs(boundaryControllerFilePath)
|
|
require.NoError(t, err)
|
|
boundaryPolicyName = WritePolicy(t, t.Context(), boundaryPolicyFilePath)
|
|
|
|
return boundaryPolicyName
|
|
}
|
|
|
|
// SetupLdapWithAd sets a Vault server up for LDAP against an Active Directory server. It
|
|
// enables the LDAP secrets engine, configures it and creates a static user
|
|
// according to what is in Active Directory. Note that this function does not put any
|
|
// clean-up in place to run after a test is complete. When applicable, callers
|
|
// should destroy the Vault LDAP policy this function creates.
|
|
func SetupLdapWithAd(t testing.TB, vaultLdapMountPath, ldapAddr, ldapAdmin, ldapAdminPw, ldapUser, ldapDn string) (string, error) {
|
|
// Enable LDAP secrets engine.
|
|
output := e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs("secrets", "enable", fmt.Sprintf("-path=%s", vaultLdapMountPath), "ldap"),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
// Define and write LDAP access policy to Vault.
|
|
vaultLdapPolicyFilePath := path.Join(t.TempDir(), "ldap-policy.hcl")
|
|
f, err := os.Create(vaultLdapPolicyFilePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = fmt.Fprintf(f, `
|
|
path "%[1]s/static-cred/%[2]s" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "%[1]s/static-role/%[2]s" {
|
|
capabilities = ["create", "read", "update", "patch", "delete", "list"]
|
|
}
|
|
`, vaultLdapMountPath, ldapUser)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = f.Sync()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_ = f.Close()
|
|
|
|
policyName := WritePolicy(t, t.Context(), vaultLdapPolicyFilePath)
|
|
|
|
// Configure LDAP secrets engine to point to AD service.
|
|
output = e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs(
|
|
"write",
|
|
fmt.Sprintf("%s/config", vaultLdapMountPath),
|
|
fmt.Sprintf("url=%s", ldapAddr),
|
|
fmt.Sprintf("binddn=cn=%s,%s", ldapAdmin, ldapDn),
|
|
fmt.Sprintf("bindpass=%s", ldapAdminPw),
|
|
"schema=ad",
|
|
"insecure_tls=true",
|
|
),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
// Create static LDAP user in Vault (already defined in AD server).
|
|
output = e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs(
|
|
"write",
|
|
fmt.Sprintf("%s/static-role/%s", vaultLdapMountPath, ldapUser),
|
|
fmt.Sprintf("dn=cn=%s,%s", ldapUser, ldapDn),
|
|
fmt.Sprintf("username=%s", ldapUser),
|
|
"rotation_period=24h",
|
|
),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
return policyName, nil
|
|
}
|
|
|
|
// SetupLdapWithOpenLdap sets a Vault server up for LDAP against an OpenLDAP server. It
|
|
// enables the LDAP secrets engine, configures it and creates a static user
|
|
// according to what is in OpenLDAP. Additionally, it sets up Vault's ability to
|
|
// manage LDAP users dynamically. Note that this function does not put any
|
|
// clean-up in place to run after a test is complete. When applicable, callers
|
|
// should destroy the Vault LDAP policy this function creates.
|
|
func SetupLdapWithOpenLdap(t testing.TB, vaultLdapMountPath, ldapAddr, ldapAdminDn, ldapAdminPw, ldapDn, ldapUser, ldapGroup string) (string, error) {
|
|
// Enable LDAP secrets engine.
|
|
output := e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs("secrets", "enable", fmt.Sprintf("-path=%s", vaultLdapMountPath), "ldap"),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
// Define and write LDAP access policy to Vault.
|
|
vaultLdapPolicyFilePath := path.Join(t.TempDir(), "ldap-policy.hcl")
|
|
f, err := os.Create(vaultLdapPolicyFilePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
_, err = fmt.Fprintf(f, `
|
|
path "%[1]s/static-cred/%[2]s" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "%[1]s/static-role/%[2]s" {
|
|
capabilities = ["create", "read", "update", "patch", "delete", "list"]
|
|
}
|
|
|
|
path "%[1]s/creds/%[3]s" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "%[1]s/role/%[3]s" {
|
|
capabilities = ["create", "read", "update", "patch", "delete", "list"]
|
|
}
|
|
`, vaultLdapMountPath, ldapUser, ldapGroup)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
err = f.Sync()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_ = f.Close()
|
|
|
|
policyName := WritePolicy(t, t.Context(), vaultLdapPolicyFilePath)
|
|
|
|
// Configure LDAP secrets engine to point to existing OpenLDAP server.
|
|
output = e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs(
|
|
"write",
|
|
fmt.Sprintf("%s/config", vaultLdapMountPath),
|
|
fmt.Sprintf("url=%s", ldapAddr),
|
|
fmt.Sprintf("binddn=%s", ldapAdminDn),
|
|
fmt.Sprintf("bindpass=%s", ldapAdminPw),
|
|
),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
// Create static LDAP user in Vault (already defined in OpenLDAP server).
|
|
output = e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs(
|
|
"write",
|
|
fmt.Sprintf("%s/static-role/%s", vaultLdapMountPath, ldapUser),
|
|
fmt.Sprintf("dn=cn=%s,%s", ldapUser, ldapDn),
|
|
fmt.Sprintf("username=%s", ldapUser),
|
|
"rotation_period=24h",
|
|
),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
// Create Vault dynamic role for LDAP group.
|
|
createLdif := fmt.Sprintf(`
|
|
dn: cn={{.Username}},%[1]s
|
|
changetype: add
|
|
objectClass: inetOrgPerson
|
|
cn: {{.Username}}
|
|
sn: {{.Username}}
|
|
uid: {{.Username}}
|
|
userPassword:{{.Password}}
|
|
|
|
dn: cn=%s,%[1]s
|
|
changetype: modify
|
|
add: uniqueMember
|
|
uniqueMember: cn={{.Username}},%[1]s
|
|
-
|
|
`, ldapDn, ldapGroup)
|
|
createLdif = strings.ReplaceAll(createLdif, "\t", "")
|
|
|
|
deleteLdif := fmt.Sprintf(`
|
|
dn: cn={{.Username}},%s
|
|
changetype: delete
|
|
`, ldapDn)
|
|
deleteLdif = strings.ReplaceAll(deleteLdif, "\t", "")
|
|
|
|
output = e2e.RunCommand(t.Context(), "vault",
|
|
e2e.WithArgs(
|
|
"write",
|
|
fmt.Sprintf("%s/role/%s", vaultLdapMountPath, ldapGroup),
|
|
"username_template=b_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}",
|
|
fmt.Sprintf("creation_ldif=%s", createLdif),
|
|
fmt.Sprintf("rollback_ldif=%s", deleteLdif),
|
|
fmt.Sprintf("deletion_ldif=%s", deleteLdif),
|
|
"default_ttl=1h",
|
|
"max_ttxl=24h",
|
|
),
|
|
)
|
|
if output.Err != nil {
|
|
return "", errors.New(strings.TrimSpace(string(output.Stderr)))
|
|
}
|
|
|
|
return policyName, nil
|
|
}
|
|
|
|
// CreateKvPrivateKeyCredential creates a private key credential in vault and
|
|
// creates a vault policy to be able to read that credential. Returns the secret
|
|
// and policy names. Note that this function does not put any clean-up in place
|
|
// to run after a test is complete. When applicable, callers should destroy the
|
|
// policy and secret this function creates.
|
|
func CreateKvPrivateKeyCredential(t testing.TB, secretPath string, user string, keyPath string) (secretName string, policyName string) {
|
|
secretName, err := base62.Random(16)
|
|
require.NoError(t, err)
|
|
|
|
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-pk-%s-policy.hcl", secretName))
|
|
f, err := os.Create(policyFilePath)
|
|
require.NoError(t, err)
|
|
|
|
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
|
secretPath,
|
|
secretName,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
|
|
|
// Create secret
|
|
output := e2e.RunCommand(context.Background(), "vault",
|
|
e2e.WithArgs(
|
|
"kv", "put",
|
|
"-mount", secretPath,
|
|
secretName,
|
|
"username="+user,
|
|
"private_key=@"+keyPath,
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
return secretName, policyName
|
|
}
|
|
|
|
// CreateKvPasswordCredential creates a username/password credential in vault
|
|
// and creates a vault policy to be able to read that credential. Returns the
|
|
// secret and policy names as well as the password for the secret. Note that
|
|
// this function does not put any clean-up in place to run after a test is
|
|
// complete. When applicable, callers should destroy the policy and secret this
|
|
// function creates.
|
|
func CreateKvPasswordCredential(t testing.TB, secretPath string, user string, providedPassword ...string) (secretName string, policyName string, password string) {
|
|
secretName, err := base62.Random(16)
|
|
require.NoError(t, err)
|
|
|
|
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-up-%s-policy.hcl", secretName))
|
|
f, err := os.Create(policyFilePath)
|
|
require.NoError(t, err)
|
|
|
|
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
|
secretPath,
|
|
secretName,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
|
|
|
// Use provided password or generate random one
|
|
if len(providedPassword) > 0 && providedPassword[0] != "" {
|
|
password = providedPassword[0]
|
|
} else {
|
|
password, err = base62.Random(16)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create secret
|
|
output := e2e.RunCommand(context.Background(), "vault",
|
|
e2e.WithArgs(
|
|
"kv", "put",
|
|
"-mount", secretPath,
|
|
secretName,
|
|
"username="+user,
|
|
"password="+password,
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
return secretName, policyName, password
|
|
}
|
|
|
|
// CreateKvPasswordDomainCredential creates a username/password/domain
|
|
// credential in vault and creates a vault policy to be able to read that
|
|
// credential. Returns the name of the policy. Note that this function does not
|
|
// put any clean-up in place to run after a test is complete. When applicable,
|
|
// callers should destroy the policy and secret this function creates.
|
|
func CreateKvPasswordDomainCredential(t testing.TB, secretPath string, user string, domain string, providedPassword ...string) (secretName string, policyName string, password string) {
|
|
secretName, err := base62.Random(16)
|
|
require.NoError(t, err)
|
|
|
|
policyFilePath := path.Join(t.TempDir(), fmt.Sprintf("kv-upd-%s-policy.hcl", secretName))
|
|
f, err := os.Create(policyFilePath)
|
|
require.NoError(t, err)
|
|
|
|
_, err = fmt.Fprintf(f, "path \"%s/data/%s\" { capabilities = [\"read\"] }\n",
|
|
secretPath,
|
|
secretName,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
policyName = WritePolicy(t, t.Context(), policyFilePath)
|
|
|
|
// Use provided password or generate random one
|
|
if len(providedPassword) > 0 && providedPassword[0] != "" {
|
|
password = providedPassword[0]
|
|
} else {
|
|
password, err = base62.Random(16)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create secret
|
|
output := e2e.RunCommand(context.Background(), "vault",
|
|
e2e.WithArgs(
|
|
"kv", "put",
|
|
"-mount", secretPath,
|
|
secretName,
|
|
"username="+user,
|
|
"password="+password,
|
|
"domain="+domain,
|
|
),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
return secretName, policyName, password
|
|
}
|
|
|
|
// WritePolicy adds a policy to vault. Provide a name for the policy that you want to create as well
|
|
// as the path to the file that contains the policy definition. Returns a policy name
|
|
func WritePolicy(t testing.TB, ctx context.Context, policyFilePath string) string {
|
|
policyName, err := base62.Random(16)
|
|
require.NoError(t, err)
|
|
|
|
output := e2e.RunCommand(ctx, "vault",
|
|
e2e.WithArgs("policy", "write", policyName, policyFilePath),
|
|
)
|
|
require.NoError(t, output.Err, string(output.Stderr))
|
|
|
|
return policyName
|
|
}
|