mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-07 07:07:05 +02:00
This is a follow-up to our initial work[0] to address RFC-5952 §4 conformance for IPv6 addresses in Vault. The initial pass focused on the vault server configuration and start-up routines. This follow-up focuses on Agent and Proxy, with a few minor improvements for server. The approach generally mirrors the server implementation but also adds support for normalization with CLI configuration overrides. One aspect we do not normalize currently is Agent/Proxy client creation to the Vault server with credentials taken from environment variables, as it would require larger changes to the `api` module. In practice this ought to be fine for the majority of cases. [0]: https://github.com/hashicorp/vault/pull/29228
2547 lines
63 KiB
Go
2547 lines
63 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package config
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-test/deep"
|
|
ctconfig "github.com/hashicorp/consul-template/config"
|
|
"github.com/hashicorp/vault/command/agentproxyshared"
|
|
"github.com/hashicorp/vault/internalshared/configutil"
|
|
"github.com/hashicorp/vault/sdk/helper/pointerutil"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
func FloatPtr(t float64) *float64 {
|
|
return &t
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "unix",
|
|
Address: "/path/to/socket",
|
|
TLSDisable: true,
|
|
SocketMode: "configmode",
|
|
SocketUser: "configuser",
|
|
SocketGroup: "configgroup",
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:3000",
|
|
Role: "metrics_only",
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Role: "default",
|
|
Address: "127.0.0.1:8400",
|
|
TLSKeyFile: "/path/to/cakey.pem",
|
|
TLSCertFile: "/path/to/cacert.pem",
|
|
},
|
|
},
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: true,
|
|
ForceAutoAuthToken: false,
|
|
Persist: &agentproxyshared.PersistConfig{
|
|
Type: "kubernetes",
|
|
Path: "/vault/agent-cache/",
|
|
KeepAfterImport: true,
|
|
ExitOnErr: true,
|
|
ServiceAccountTokenFile: "/tmp/serviceaccount/token",
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
CACert: "config_ca_cert",
|
|
CAPath: "config_ca_path",
|
|
TLSSkipVerifyRaw: interface{}("true"),
|
|
TLSSkipVerify: true,
|
|
ClientCert: "config_client_cert",
|
|
ClientKey: "config_client_key",
|
|
Retry: &Retry{
|
|
NumRetries: 12,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
|
|
config, err = LoadConfigFile("./test-fixtures/config-cache-embedded-type.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected.Vault.TLSSkipVerifyRaw = interface{}(true)
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigDir_AgentCache(t *testing.T) {
|
|
config, err := LoadConfig("./test-fixtures/config-dir-cache/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "unix",
|
|
Address: "/path/to/socket",
|
|
TLSDisable: true,
|
|
SocketMode: "configmode",
|
|
SocketUser: "configuser",
|
|
SocketGroup: "configgroup",
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:3000",
|
|
Role: "metrics_only",
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Role: "default",
|
|
Address: "127.0.0.1:8400",
|
|
TLSKeyFile: "/path/to/cakey.pem",
|
|
TLSCertFile: "/path/to/cacert.pem",
|
|
},
|
|
},
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: true,
|
|
ForceAutoAuthToken: false,
|
|
Persist: &agentproxyshared.PersistConfig{
|
|
Type: "kubernetes",
|
|
Path: "/vault/agent-cache/",
|
|
KeepAfterImport: true,
|
|
ExitOnErr: true,
|
|
ServiceAccountTokenFile: "/tmp/serviceaccount/token",
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
CACert: "config_ca_cert",
|
|
CAPath: "config_ca_path",
|
|
TLSSkipVerifyRaw: interface{}("true"),
|
|
TLSSkipVerify: true,
|
|
ClientCert: "config_client_cert",
|
|
ClientKey: "config_client_key",
|
|
Retry: &Retry{
|
|
NumRetries: 12,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
|
|
config, err = LoadConfigFile("./test-fixtures/config-dir-cache/config-cache1.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config2, err := LoadConfigFile("./test-fixtures/config-dir-cache/config-cache2.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mergedConfig := config.Merge(config2)
|
|
|
|
mergedConfig.Prune()
|
|
if diff := deep.Equal(mergedConfig, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigDir_AutoAuthAndListener(t *testing.T) {
|
|
config, err := LoadConfig("./test-fixtures/config-dir-auto-auth-and-listener/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
|
|
config, err = LoadConfigFile("./test-fixtures/config-dir-auto-auth-and-listener/config1.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config2, err := LoadConfigFile("./test-fixtures/config-dir-auto-auth-and-listener/config2.hcl")
|
|
|
|
mergedConfig := config.Merge(config2)
|
|
|
|
mergedConfig.Prune()
|
|
if diff := deep.Equal(mergedConfig, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigDir_VaultBlock(t *testing.T) {
|
|
config, err := LoadConfig("./test-fixtures/config-dir-vault-block/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
CACert: "config_ca_cert",
|
|
CAPath: "config_ca_path",
|
|
TLSSkipVerifyRaw: interface{}("true"),
|
|
TLSSkipVerify: true,
|
|
ClientCert: "config_client_cert",
|
|
ClientKey: "config_client_key",
|
|
Retry: &Retry{
|
|
NumRetries: 12,
|
|
},
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
|
|
config, err = LoadConfigFile("./test-fixtures/config-dir-vault-block/config1.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config2, err := LoadConfigFile("./test-fixtures/config-dir-vault-block/config2.hcl")
|
|
|
|
mergedConfig := config.Merge(config2)
|
|
|
|
mergedConfig.Prune()
|
|
if diff := deep.Equal(mergedConfig, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_NoListeners(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-no-listeners.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: true,
|
|
ForceAutoAuthToken: false,
|
|
Persist: &agentproxyshared.PersistConfig{
|
|
Type: "kubernetes",
|
|
Path: "/vault/agent-cache/",
|
|
KeepAfterImport: true,
|
|
ExitOnErr: true,
|
|
ServiceAccountTokenFile: "/tmp/serviceaccount/token",
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
CACert: "config_ca_cert",
|
|
CAPath: "config_ca_path",
|
|
TLSSkipVerifyRaw: interface{}("true"),
|
|
TLSSkipVerify: true,
|
|
ClientCert: "config_client_cert",
|
|
ClientKey: "config_client_key",
|
|
Retry: &Retry{
|
|
NumRetries: 12,
|
|
},
|
|
},
|
|
Templates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
// Test_LoadConfigFile_AutoAuth_AddrConformance verifies basic config file
|
|
// loading in addition to RFC-5942 §4 normalization of auto-auth methods.
|
|
// See: https://rfc-editor.org/rfc/rfc5952.html
|
|
func Test_LoadConfigFile_AutoAuth_AddrConformance(t *testing.T) {
|
|
t.Setenv("TEST_AAD_ENV", "aad")
|
|
|
|
for name, method := range map[string]*Method{
|
|
"aws": {
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "aws-namespace/",
|
|
Config: map[string]any{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
"azure": {
|
|
Type: "azure",
|
|
MountPath: "auth/azure",
|
|
Namespace: "azure-namespace/",
|
|
Config: map[string]any{
|
|
"authenticate_from_environment": true,
|
|
"role": "dev-role",
|
|
"resource": "https://[2001:0:0:1::1]",
|
|
},
|
|
},
|
|
"gcp": {
|
|
Type: "gcp",
|
|
MountPath: "auth/gcp",
|
|
Namespace: "gcp-namespace/",
|
|
Config: map[string]any{
|
|
"role": "dev-role",
|
|
"service_account": "https://[2001:db8:ac3:fe4::1]",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-auto-auth-" + name + ".hcl")
|
|
require.NoError(t, err)
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "unix",
|
|
Address: "/path/to/socket",
|
|
TLSDisable: true,
|
|
AgentAPI: &configutil.AgentAPI{
|
|
EnableQuit: true,
|
|
},
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "2001:db8::1:8200", // Normalized
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Address: "[2001:0:0:1::1]:3000", // Normalized
|
|
Role: "metrics_only",
|
|
TLSDisable: true,
|
|
},
|
|
{
|
|
Type: "tcp",
|
|
Role: "default",
|
|
Address: "2001:db8:0:1:1:1:1:1:8400", // Normalized
|
|
TLSKeyFile: "/path/to/cakey.pem",
|
|
TLSCertFile: "/path/to/cacert.pem",
|
|
},
|
|
},
|
|
LogFile: "/var/log/vault/vault-agent.log",
|
|
},
|
|
Vault: &Vault{
|
|
Address: "https://[2001:db8::1]:8200", // Address is normalized
|
|
Retry: &Retry{
|
|
NumRetries: 12, // Default number of retries when a vault stanza is set
|
|
},
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: method, // Method properties are normalized correctly
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
{
|
|
Type: "file",
|
|
WrapTTL: 5 * time.Minute,
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath2",
|
|
AAD: "aad",
|
|
DeriveKey: true,
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
require.EqualValues(t, expected, config)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Method_Wrapping(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-method-wrapping.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
ExitOnError: false,
|
|
WrapTTL: 5 * time.Minute,
|
|
MaxBackoff: 2 * time.Minute,
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Method_InitialBackoff(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-method-initial-backoff.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
ExitOnError: false,
|
|
WrapTTL: 5 * time.Minute,
|
|
MinBackoff: 5 * time.Second,
|
|
MaxBackoff: 2 * time.Minute,
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Method_ExitOnErr(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-method-exit-on-err.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
ExitOnError: true,
|
|
WrapTTL: 5 * time.Minute,
|
|
MinBackoff: 5 * time.Second,
|
|
MaxBackoff: 2 * time.Minute,
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_NoAutoAuth(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-no-auto_auth.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
Cache: &Cache{},
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AgentCache_InconsisentAutoAuth(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-cache-inconsistent-auto_auth.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should not return an error for this config, err: %v", err)
|
|
}
|
|
if config == nil {
|
|
t.Fatal("config was nil")
|
|
}
|
|
err = config.ValidateConfig()
|
|
if err == nil {
|
|
t.Fatal("ValidateConfig should return an error when use_auto_auth_token=true and no auto_auth section present")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AgentCache_ForceAutoAuthNoMethod(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-cache-force-token-no-auth-method.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should not return an error for this config, err: %v", err)
|
|
}
|
|
if config == nil {
|
|
t.Fatal("config was nil")
|
|
}
|
|
err = config.ValidateConfig()
|
|
if err == nil {
|
|
t.Fatal("ValidateConfig should return an error when use_auto_auth_token=force and no auto_auth section present")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AgentCache_NoListeners(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-cache-no-listeners.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should return an error for this config")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AutoAuth_Wrapped_Multiple_Sinks(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-auto_auth-wrapped-multiple-sinks.hcl")
|
|
if err == nil {
|
|
t.Fatalf("LoadConfigFile should return an error for this config, err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AutoAuth_Nosinks_Nocache_Notemplates(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-auto_auth-nosinks-nocache-notemplates.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should not return an error for this config, err: %v", err)
|
|
}
|
|
if config == nil {
|
|
t.Fatal("config was nil")
|
|
}
|
|
err = config.ValidateConfig()
|
|
if err == nil {
|
|
t.Fatal("ValidateConfig should return an error when auto_auth configured and there are no sinks, caches or templates")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AutoAuth_Both_Wrapping_Types(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-method-wrapping-and-sink-wrapping.hcl")
|
|
if err == nil {
|
|
t.Fatalf("LoadConfigFile should return an error for this config")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_AgentCache_AutoAuth_Method_wrapping(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-cache-auto_auth-method-wrapping.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should not return an error for this config, err: %v", err)
|
|
}
|
|
if config == nil {
|
|
t.Fatal("config was nil")
|
|
}
|
|
err = config.ValidateConfig()
|
|
if err == nil {
|
|
t.Fatal("ValidateConfig should return an error when auth_auth.method.wrap_ttl nonzero and cache.use_auto_auth_token=true")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_APIProxy_And_Cache_Same_Config(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-api_proxy-cache.hcl")
|
|
if err != nil {
|
|
t.Fatalf("LoadConfigFile should not return an error for this config, err: %v", err)
|
|
}
|
|
if config == nil {
|
|
t.Fatal("config was nil")
|
|
}
|
|
err = config.ValidateConfig()
|
|
if err == nil {
|
|
t.Fatal("ValidateConfig should return an error when cache and api_proxy try and configure the same value")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_AutoAuth_NoSink(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-auto_auth-no-sink.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_AutoAuth_Force(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-auto_auth-force.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: true,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: "force",
|
|
ForceAutoAuthToken: true,
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_AutoAuth_True(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-auto_auth-true.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: "true",
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Agent_AutoAuth_APIProxyAllConfig(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-api_proxy-auto_auth-all-api_proxy-config.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
},
|
|
APIProxy: &APIProxy{
|
|
UseAutoAuthToken: true,
|
|
UseAutoAuthTokenRaw: "force",
|
|
ForceAutoAuthToken: true,
|
|
EnforceConsistency: "always",
|
|
WhenInconsistent: "forward",
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_AutoAuth_False(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-auto_auth-false.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Cache: &Cache{
|
|
UseAutoAuthToken: false,
|
|
UseAutoAuthTokenRaw: "false",
|
|
ForceAutoAuthToken: false,
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_Persist(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-cache-persist-false.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
Cache: &Cache{
|
|
Persist: &agentproxyshared.PersistConfig{
|
|
Type: "kubernetes",
|
|
Path: "/vault/agent-cache/",
|
|
KeepAfterImport: false,
|
|
ExitOnErr: false,
|
|
ServiceAccountTokenFile: "",
|
|
},
|
|
},
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_AgentCache_PersistMissingType(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/config-cache-persist-empty-type.hcl")
|
|
if err == nil || os.IsNotExist(err) {
|
|
t.Fatal("expected error or file is missing")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_TemplateConfig(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
fixturePath string
|
|
expectedTemplateConfig TemplateConfig
|
|
}{
|
|
"set-true": {
|
|
"./test-fixtures/config-template_config.hcl",
|
|
TemplateConfig{
|
|
ExitOnRetryFailure: true,
|
|
StaticSecretRenderInt: 1 * time.Minute,
|
|
MaxConnectionsPerHost: 100,
|
|
LeaseRenewalThreshold: FloatPtr(0.8),
|
|
},
|
|
},
|
|
"empty": {
|
|
"./test-fixtures/config-template_config-empty.hcl",
|
|
TemplateConfig{
|
|
ExitOnRetryFailure: false,
|
|
MaxConnectionsPerHost: 10,
|
|
},
|
|
},
|
|
"missing": {
|
|
"./test-fixtures/config-template_config-missing.hcl",
|
|
TemplateConfig{
|
|
MaxConnectionsPerHost: 10,
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
config, err := LoadConfigFile(tc.fixturePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
NumRetries: 5,
|
|
},
|
|
},
|
|
TemplateConfig: &tc.expectedTemplateConfig,
|
|
Templates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_TemplateConfig_MergeConfigs tests that in the case of multiple config files,
|
|
// a provided TemplateConfig in one file is properly preserved in the merged config
|
|
func TestLoadConfigFile_TemplateConfig_MergeConfigs(t *testing.T) {
|
|
configPaths := []string{
|
|
"./test-fixtures/config-template_config.hcl",
|
|
"./test-fixtures/config-template-min-no-auth.hcl",
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
NumRetries: 5,
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
ExitOnRetryFailure: true,
|
|
StaticSecretRenderInt: 1 * time.Minute,
|
|
MaxConnectionsPerHost: 100,
|
|
LeaseRenewalThreshold: FloatPtr(0.8),
|
|
},
|
|
Templates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template2.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render2.txt"),
|
|
},
|
|
},
|
|
}
|
|
|
|
cfg := NewConfig()
|
|
for _, path := range configPaths {
|
|
configFromPath, err := LoadConfigFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg = cfg.Merge(configFromPath)
|
|
}
|
|
|
|
cfg.Prune()
|
|
if diff := deep.Equal(cfg, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Template tests template definitions in Vault Agent
|
|
func TestLoadConfigFile_Template(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
fixturePath string
|
|
expectedTemplates []*ctconfig.TemplateConfig
|
|
}{
|
|
"min": {
|
|
fixturePath: "./test-fixtures/config-template-min.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
},
|
|
},
|
|
"full": {
|
|
fixturePath: "./test-fixtures/config-template-full.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Backup: pointerutil.BoolPtr(true),
|
|
Command: []string{"restart service foo"},
|
|
CommandTimeout: pointerutil.TimeDurationPtr("60s"),
|
|
Contents: pointerutil.StringPtr("{{ keyOrDefault \"service/redis/maxconns@east-aws\" \"5\" }}"),
|
|
CreateDestDirs: pointerutil.BoolPtr(true),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
ErrMissingKey: pointerutil.BoolPtr(true),
|
|
LeftDelim: pointerutil.StringPtr("<<"),
|
|
Perms: pointerutil.FileModePtr(0o655),
|
|
RightDelim: pointerutil.StringPtr(">>"),
|
|
SandboxPath: pointerutil.StringPtr("/path/on/disk/where"),
|
|
Exec: &ctconfig.ExecConfig{
|
|
Command: []string{"foo"},
|
|
Timeout: pointerutil.TimeDurationPtr("10s"),
|
|
},
|
|
|
|
Wait: &ctconfig.WaitConfig{
|
|
Min: pointerutil.TimeDurationPtr("10s"),
|
|
Max: pointerutil.TimeDurationPtr("40s"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"many": {
|
|
fixturePath: "./test-fixtures/config-template-many.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
ErrMissingKey: pointerutil.BoolPtr(false),
|
|
CreateDestDirs: pointerutil.BoolPtr(true),
|
|
Command: []string{"restart service foo"},
|
|
Perms: pointerutil.FileModePtr(0o600),
|
|
},
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template2.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render2.txt"),
|
|
Backup: pointerutil.BoolPtr(true),
|
|
Perms: pointerutil.FileModePtr(0o755),
|
|
Wait: &ctconfig.WaitConfig{
|
|
Min: pointerutil.TimeDurationPtr("2s"),
|
|
Max: pointerutil.TimeDurationPtr("10s"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
config, err := LoadConfigFile(tc.fixturePath)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Templates: tc.expectedTemplates,
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Template_NoSinks tests template definitions without sinks in Vault Agent
|
|
func TestLoadConfigFile_Template_NoSinks(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
fixturePath string
|
|
expectedTemplates []*ctconfig.TemplateConfig
|
|
}{
|
|
"min": {
|
|
fixturePath: "./test-fixtures/config-template-min-nosink.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
},
|
|
},
|
|
"full": {
|
|
fixturePath: "./test-fixtures/config-template-full-nosink.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Backup: pointerutil.BoolPtr(true),
|
|
Command: []string{"restart service foo"},
|
|
CommandTimeout: pointerutil.TimeDurationPtr("60s"),
|
|
Contents: pointerutil.StringPtr("{{ keyOrDefault \"service/redis/maxconns@east-aws\" \"5\" }}"),
|
|
CreateDestDirs: pointerutil.BoolPtr(true),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
ErrMissingKey: pointerutil.BoolPtr(true),
|
|
LeftDelim: pointerutil.StringPtr("<<"),
|
|
Perms: pointerutil.FileModePtr(0o655),
|
|
RightDelim: pointerutil.StringPtr(">>"),
|
|
SandboxPath: pointerutil.StringPtr("/path/on/disk/where"),
|
|
|
|
Wait: &ctconfig.WaitConfig{
|
|
Min: pointerutil.TimeDurationPtr("10s"),
|
|
Max: pointerutil.TimeDurationPtr("40s"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"many": {
|
|
fixturePath: "./test-fixtures/config-template-many-nosink.hcl",
|
|
expectedTemplates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
ErrMissingKey: pointerutil.BoolPtr(false),
|
|
CreateDestDirs: pointerutil.BoolPtr(true),
|
|
Command: []string{"restart service foo"},
|
|
Perms: pointerutil.FileModePtr(0o600),
|
|
},
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template2.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render2.txt"),
|
|
Backup: pointerutil.BoolPtr(true),
|
|
Perms: pointerutil.FileModePtr(0o755),
|
|
Wait: &ctconfig.WaitConfig{
|
|
Min: pointerutil.TimeDurationPtr("2s"),
|
|
Max: pointerutil.TimeDurationPtr("10s"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
config, err := LoadConfigFile(tc.fixturePath)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: nil,
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Templates: tc.expectedTemplates,
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Template_WithCache tests ensures that cache {} stanza is
|
|
// permitted in vault agent configuration with template(s)
|
|
func TestLoadConfigFile_Template_WithCache(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-template-with-cache.hcl")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
},
|
|
Cache: &Cache{},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Templates: []*ctconfig.TemplateConfig{
|
|
{
|
|
Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"),
|
|
Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"),
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Vault_Retry(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-vault-retry.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
NumRetries: 5,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Vault_Retry_Empty(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-vault-retry-empty.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_EnforceConsistency(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-consistency.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "",
|
|
},
|
|
Cache: &Cache{
|
|
EnforceConsistency: "always",
|
|
WhenInconsistent: "retry",
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_EnforceConsistency_APIProxy(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-consistency-apiproxy.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
Listeners: []*configutil.Listener{
|
|
{
|
|
Type: "tcp",
|
|
Address: "127.0.0.1:8300",
|
|
TLSDisable: true,
|
|
},
|
|
},
|
|
PidFile: "",
|
|
},
|
|
APIProxy: &APIProxy{
|
|
EnforceConsistency: "always",
|
|
WhenInconsistent: "retry",
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_All(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-all.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"auto-auth", "caching", "templating", "proxying"},
|
|
DisableIdleConnsAPIProxy: true,
|
|
DisableIdleConnsAutoAuth: true,
|
|
DisableIdleConnsTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Auto_Auth(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-auto-auth.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"auto-auth"},
|
|
DisableIdleConnsAPIProxy: false,
|
|
DisableIdleConnsAutoAuth: true,
|
|
DisableIdleConnsTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Templating(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-templating.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"templating"},
|
|
DisableIdleConnsAPIProxy: false,
|
|
DisableIdleConnsAutoAuth: false,
|
|
DisableIdleConnsTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Caching(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-caching.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"caching"},
|
|
DisableIdleConnsAPIProxy: true,
|
|
DisableIdleConnsAutoAuth: false,
|
|
DisableIdleConnsTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Proxying(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-proxying.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"proxying"},
|
|
DisableIdleConnsAPIProxy: true,
|
|
DisableIdleConnsAutoAuth: false,
|
|
DisableIdleConnsTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Empty(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-empty.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{},
|
|
DisableIdleConnsAPIProxy: false,
|
|
DisableIdleConnsAutoAuth: false,
|
|
DisableIdleConnsTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Idle_Conns_Env(t *testing.T) {
|
|
err := os.Setenv(DisableIdleConnsEnv, "auto-auth,caching,templating")
|
|
defer os.Unsetenv(DisableIdleConnsEnv)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-idle-connections-empty.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableIdleConns: []string{"auto-auth", "caching", "templating"},
|
|
DisableIdleConnsAPIProxy: true,
|
|
DisableIdleConnsAutoAuth: true,
|
|
DisableIdleConnsTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_Value_Disable_Idle_Conns(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-disable-idle-connections.hcl")
|
|
if err == nil {
|
|
t.Fatal("should have error, it didn't")
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_All(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-all.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"auto-auth", "caching", "templating", "proxying"},
|
|
DisableKeepAlivesAPIProxy: true,
|
|
DisableKeepAlivesAutoAuth: true,
|
|
DisableKeepAlivesTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Auto_Auth(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-auto-auth.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"auto-auth"},
|
|
DisableKeepAlivesAPIProxy: false,
|
|
DisableKeepAlivesAutoAuth: true,
|
|
DisableKeepAlivesTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Templating(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-templating.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"templating"},
|
|
DisableKeepAlivesAPIProxy: false,
|
|
DisableKeepAlivesAutoAuth: false,
|
|
DisableKeepAlivesTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Caching(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-caching.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"caching"},
|
|
DisableKeepAlivesAPIProxy: true,
|
|
DisableKeepAlivesAutoAuth: false,
|
|
DisableKeepAlivesTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Proxying(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-proxying.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"proxying"},
|
|
DisableKeepAlivesAPIProxy: true,
|
|
DisableKeepAlivesAutoAuth: false,
|
|
DisableKeepAlivesTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Empty(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-empty.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{},
|
|
DisableKeepAlivesAPIProxy: false,
|
|
DisableKeepAlivesAutoAuth: false,
|
|
DisableKeepAlivesTemplating: false,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Disable_Keep_Alives_Env(t *testing.T) {
|
|
err := os.Setenv(DisableKeepAlivesEnv, "auto-auth,caching,templating")
|
|
defer os.Unsetenv(DisableKeepAlivesEnv)
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config, err := LoadConfigFile("./test-fixtures/config-disable-keep-alives-empty.hcl")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := &Config{
|
|
SharedConfig: &configutil.SharedConfig{
|
|
PidFile: "./pidfile",
|
|
},
|
|
DisableKeepAlives: []string{"auto-auth", "caching", "templating"},
|
|
DisableKeepAlivesAPIProxy: true,
|
|
DisableKeepAlivesAutoAuth: true,
|
|
DisableKeepAlivesTemplating: true,
|
|
AutoAuth: &AutoAuth{
|
|
Method: &Method{
|
|
Type: "aws",
|
|
MountPath: "auth/aws",
|
|
Namespace: "my-namespace/",
|
|
Config: map[string]interface{}{
|
|
"role": "foobar",
|
|
},
|
|
},
|
|
Sinks: []*Sink{
|
|
{
|
|
Type: "file",
|
|
DHType: "curve25519",
|
|
DHPath: "/tmp/file-foo-dhpath",
|
|
AAD: "foobar",
|
|
Config: map[string]interface{}{
|
|
"path": "/tmp/file-foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
TemplateConfig: &TemplateConfig{
|
|
MaxConnectionsPerHost: DefaultTemplateConfigMaxConnsPerHost,
|
|
},
|
|
Vault: &Vault{
|
|
Address: "http://127.0.0.1:1111",
|
|
Retry: &Retry{
|
|
ctconfig.DefaultRetryAttempts,
|
|
},
|
|
},
|
|
}
|
|
|
|
config.Prune()
|
|
if diff := deep.Equal(config, expected); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFile_Bad_Value_Disable_Keep_Alives(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-disable-keep-alives.hcl")
|
|
if err == nil {
|
|
t.Fatal("should have error, it didn't")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_Simple loads and validates an env_template config
|
|
func TestLoadConfigFile_EnvTemplates_Simple(t *testing.T) {
|
|
cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-simple.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := cfg.ValidateConfig(); err != nil {
|
|
t.Fatalf("validation error: %s", err)
|
|
}
|
|
|
|
expectedKey := "MY_DATABASE_USER"
|
|
found := false
|
|
for _, envTemplate := range cfg.EnvTemplates {
|
|
if *envTemplate.MapToEnvironmentVariable == expectedKey {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("expected environment variable name to be populated")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_Complex loads and validates an env_template config
|
|
func TestLoadConfigFile_EnvTemplates_Complex(t *testing.T) {
|
|
cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-complex.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := cfg.ValidateConfig(); err != nil {
|
|
t.Fatalf("validation error: %s", err)
|
|
}
|
|
|
|
expectedKeys := []string{
|
|
"FOO_PASSWORD",
|
|
"FOO_USER",
|
|
}
|
|
|
|
envExists := func(key string) bool {
|
|
for _, envTmpl := range cfg.EnvTemplates {
|
|
if *envTmpl.MapToEnvironmentVariable == key {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
for _, expected := range expectedKeys {
|
|
if !envExists(expected) {
|
|
t.Fatalf("expected environment variable %s", expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_WithSource loads and validates an
|
|
// env_template config with "source" instead of "contents"
|
|
func TestLoadConfigFile_EnvTemplates_WithSource(t *testing.T) {
|
|
cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-with-source.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := cfg.ValidateConfig(); err != nil {
|
|
t.Fatalf("validation error: %s", err)
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_NoName ensures that env_template with no name triggers an error
|
|
func TestLoadConfigFile_EnvTemplates_NoName(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-no-name.hcl")
|
|
if err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_ExecInvalidSignal ensures that an invalid signal triggers an error
|
|
func TestLoadConfigFile_EnvTemplates_ExecInvalidSignal(t *testing.T) {
|
|
_, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-invalid-signal.hcl")
|
|
if err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_ExecSimple validates the exec section with default parameters
|
|
func TestLoadConfigFile_EnvTemplates_ExecSimple(t *testing.T) {
|
|
cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-simple.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := cfg.ValidateConfig(); err != nil {
|
|
t.Fatalf("validation error: %s", err)
|
|
}
|
|
|
|
expectedCmd := []string{"/path/to/my/app", "arg1", "arg2"}
|
|
if !slices.Equal(cfg.Exec.Command, expectedCmd) {
|
|
t.Fatal("exec.command does not have expected value")
|
|
}
|
|
|
|
// check defaults
|
|
if cfg.Exec.RestartOnSecretChanges != "always" {
|
|
t.Fatalf("expected cfg.Exec.RestartOnSecretChanges to be 'always', got '%s'", cfg.Exec.RestartOnSecretChanges)
|
|
}
|
|
|
|
if cfg.Exec.RestartStopSignal != syscall.SIGTERM {
|
|
t.Fatalf("expected cfg.Exec.RestartStopSignal to be 'syscall.SIGTERM', got '%s'", cfg.Exec.RestartStopSignal)
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_EnvTemplates_ExecComplex validates the exec section with non-default parameters
|
|
func TestLoadConfigFile_EnvTemplates_ExecComplex(t *testing.T) {
|
|
cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-complex.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := cfg.ValidateConfig(); err != nil {
|
|
t.Fatalf("validation error: %s", err)
|
|
}
|
|
|
|
if !slices.Equal(cfg.Exec.Command, []string{"env"}) {
|
|
t.Fatal("exec.command does not have expected value")
|
|
}
|
|
|
|
if cfg.Exec.RestartOnSecretChanges != "never" {
|
|
t.Fatalf("expected cfg.Exec.RestartOnSecretChanges to be 'never', got %q", cfg.Exec.RestartOnSecretChanges)
|
|
}
|
|
|
|
if cfg.Exec.RestartStopSignal != syscall.SIGINT {
|
|
t.Fatalf("expected cfg.Exec.RestartStopSignal to be 'syscall.SIGINT', got %q", cfg.Exec.RestartStopSignal)
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Bad_EnvTemplates_MissingExec ensures that ValidateConfig
|
|
// errors when "env_template" stanza(s) are specified but "exec" is missing
|
|
func TestLoadConfigFile_Bad_EnvTemplates_MissingExec(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-missing-exec.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := config.ValidateConfig(); err == nil {
|
|
t.Fatal("expected an error from ValidateConfig: exec section is missing")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Bad_EnvTemplates_WithProxy ensures that ValidateConfig
|
|
// errors when both env_template and api_proxy stanzas are present
|
|
func TestLoadConfigFile_Bad_EnvTemplates_WithProxy(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-with-proxy.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := config.ValidateConfig(); err == nil {
|
|
t.Fatal("expected an error from ValidateConfig: listener / api_proxy are not compatible with env_template")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Bad_EnvTemplates_WithFileTemplates ensures that
|
|
// ValidateConfig errors when both env_template and template stanzas are present
|
|
func TestLoadConfigFile_Bad_EnvTemplates_WithFileTemplates(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-with-file-templates.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := config.ValidateConfig(); err == nil {
|
|
t.Fatal("expected an error from ValidateConfig: file template stanza is not compatible with env_template")
|
|
}
|
|
}
|
|
|
|
// TestLoadConfigFile_Bad_EnvTemplates_DisalowedFields ensure that
|
|
// ValidateConfig errors for disalowed env_template fields
|
|
func TestLoadConfigFile_Bad_EnvTemplates_DisalowedFields(t *testing.T) {
|
|
config, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-disalowed-fields.hcl")
|
|
if err != nil {
|
|
t.Fatalf("error loading config file: %s", err)
|
|
}
|
|
|
|
if err := config.ValidateConfig(); err == nil {
|
|
t.Fatal("expected an error from ValidateConfig: disallowed fields specified in env_template")
|
|
}
|
|
}
|