1package config
2
3import (
4	"fmt"
5	"time"
6
7	"github.com/hashicorp/vault/api"
8)
9
10const (
11	// XXX Change use to api.EnvVaultSkipVerify once we've updated vendored
12	// vault to version 1.1.0 or newer.
13	EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
14
15	// DefaultVaultGrace is the default grace period before which to read a new
16	// secret from Vault. If a lease is due to expire in 15 seconds, Consul
17	// Template will read a new secret at that time minus this value.
18	DefaultVaultGrace = 15 * time.Second
19
20	// DefaultVaultRenewToken is the default value for if the Vault token should
21	// be renewed.
22	DefaultVaultRenewToken = true
23
24	// DefaultVaultUnwrapToken is the default value for if the Vault token should
25	// be unwrapped.
26	DefaultVaultUnwrapToken = false
27
28	// DefaultVaultRetryBase is the default value for the base time to use for
29	// exponential backoff.
30	DefaultVaultRetryBase = 250 * time.Millisecond
31
32	// DefaultVaultRetryMaxAttempts is the default maximum number of attempts to
33	// retry before quitting.
34	DefaultVaultRetryMaxAttempts = 5
35)
36
37// VaultConfig is the configuration for connecting to a vault server.
38type VaultConfig struct {
39	// Address is the URI to the Vault server.
40	Address *string `mapstructure:"address"`
41
42	// Enabled controls whether the Vault integration is active.
43	Enabled *bool `mapstructure:"enabled"`
44
45	// Grace is the amount of time before a lease is about to expire to force a
46	// new secret to be read.
47	Grace *time.Duration `mapstructure:"grace"`
48
49	// Namespace is the Vault namespace to use for reading/writing secrets. This can
50	// also be set via the VAULT_NAMESPACE environment variable.
51	Namespace *string `mapstructure:"namespace"`
52
53	// RenewToken renews the Vault token.
54	RenewToken *bool `mapstructure:"renew_token"`
55
56	// Retry is the configuration for specifying how to behave on failure.
57	Retry *RetryConfig `mapstructure:"retry"`
58
59	// SSL indicates we should use a secure connection while talking to Vault.
60	SSL *SSLConfig `mapstructure:"ssl"`
61
62	// Token is the Vault token to communicate with for requests. It may be
63	// a wrapped token or a real token. This can also be set via the VAULT_TOKEN
64	// environment variable, or via the VaultAgentTokenFile.
65	Token *string `mapstructure:"token" json:"-"`
66
67	// VaultAgentTokenFile is the path of file that contains a Vault Agent token.
68	// If vault_agent_token_file is specified:
69	//   - Consul Template will not try to renew the Vault token.
70	//   - Consul Template will periodically stat the file and update the token if it has
71	// changed.
72	VaultAgentTokenFile *string `mapstructure:"vault_agent_token_file" json:"-"`
73
74	// Transport configures the low-level network connection details.
75	Transport *TransportConfig `mapstructure:"transport"`
76
77	// UnwrapToken unwraps the provided Vault token as a wrapped token.
78	UnwrapToken *bool `mapstructure:"unwrap_token"`
79}
80
81// DefaultVaultConfig returns a configuration that is populated with the
82// default values.
83func DefaultVaultConfig() *VaultConfig {
84	v := &VaultConfig{
85		Retry:     DefaultRetryConfig(),
86		SSL:       DefaultSSLConfig(),
87		Transport: DefaultTransportConfig(),
88	}
89
90	// Force SSL when communicating with Vault.
91	v.SSL.Enabled = Bool(true)
92
93	return v
94}
95
96// Copy returns a deep copy of this configuration.
97func (c *VaultConfig) Copy() *VaultConfig {
98	if c == nil {
99		return nil
100	}
101
102	var o VaultConfig
103	o.Address = c.Address
104
105	o.Enabled = c.Enabled
106
107	o.Grace = c.Grace
108
109	o.Namespace = c.Namespace
110
111	o.RenewToken = c.RenewToken
112
113	if c.Retry != nil {
114		o.Retry = c.Retry.Copy()
115	}
116
117	if c.SSL != nil {
118		o.SSL = c.SSL.Copy()
119	}
120
121	o.Token = c.Token
122
123	o.VaultAgentTokenFile = c.VaultAgentTokenFile
124
125	if c.Transport != nil {
126		o.Transport = c.Transport.Copy()
127	}
128
129	o.UnwrapToken = c.UnwrapToken
130
131	return &o
132}
133
134// Merge combines all values in this configuration with the values in the other
135// configuration, with values in the other configuration taking precedence.
136// Maps and slices are merged, most other values are overwritten. Complex
137// structs define their own merge functionality.
138func (c *VaultConfig) Merge(o *VaultConfig) *VaultConfig {
139	if c == nil {
140		if o == nil {
141			return nil
142		}
143		return o.Copy()
144	}
145
146	if o == nil {
147		return c.Copy()
148	}
149
150	r := c.Copy()
151
152	if o.Address != nil {
153		r.Address = o.Address
154	}
155
156	if o.Enabled != nil {
157		r.Enabled = o.Enabled
158	}
159
160	if o.Grace != nil {
161		r.Grace = o.Grace
162	}
163
164	if o.Namespace != nil {
165		r.Namespace = o.Namespace
166	}
167
168	if o.RenewToken != nil {
169		r.RenewToken = o.RenewToken
170	}
171
172	if o.Retry != nil {
173		r.Retry = r.Retry.Merge(o.Retry)
174	}
175
176	if o.SSL != nil {
177		r.SSL = r.SSL.Merge(o.SSL)
178	}
179
180	if o.Token != nil {
181		r.Token = o.Token
182	}
183
184	if o.VaultAgentTokenFile != nil {
185		r.VaultAgentTokenFile = o.VaultAgentTokenFile
186	}
187
188	if o.Transport != nil {
189		r.Transport = r.Transport.Merge(o.Transport)
190	}
191
192	if o.UnwrapToken != nil {
193		r.UnwrapToken = o.UnwrapToken
194	}
195
196	return r
197}
198
199// Finalize ensures there no nil pointers.
200func (c *VaultConfig) Finalize() {
201	if c.Address == nil {
202		c.Address = stringFromEnv([]string{
203			api.EnvVaultAddress,
204		}, "")
205	}
206
207	if c.Grace == nil {
208		c.Grace = TimeDuration(DefaultVaultGrace)
209	}
210
211	if c.Namespace == nil {
212		c.Namespace = stringFromEnv([]string{"VAULT_NAMESPACE"}, "")
213	}
214
215	if c.RenewToken == nil {
216		default_renew := DefaultVaultRenewToken
217		if c.VaultAgentTokenFile != nil {
218			default_renew = false
219		}
220		c.RenewToken = boolFromEnv([]string{
221			"VAULT_RENEW_TOKEN",
222		}, default_renew)
223	}
224
225	if c.Retry == nil {
226		c.Retry = DefaultRetryConfig()
227	}
228	c.Retry.Finalize()
229
230	// Vault has custom SSL settings
231	if c.SSL == nil {
232		c.SSL = DefaultSSLConfig()
233	}
234	if c.SSL.Enabled == nil {
235		c.SSL.Enabled = Bool(true)
236	}
237	if c.SSL.CaCert == nil {
238		c.SSL.CaCert = stringFromEnv([]string{api.EnvVaultCACert}, "")
239	}
240	if c.SSL.CaPath == nil {
241		c.SSL.CaPath = stringFromEnv([]string{api.EnvVaultCAPath}, "")
242	}
243	if c.SSL.Cert == nil {
244		c.SSL.Cert = stringFromEnv([]string{api.EnvVaultClientCert}, "")
245	}
246	if c.SSL.Key == nil {
247		c.SSL.Key = stringFromEnv([]string{api.EnvVaultClientKey}, "")
248	}
249	if c.SSL.ServerName == nil {
250		c.SSL.ServerName = stringFromEnv([]string{api.EnvVaultTLSServerName}, "")
251	}
252	if c.SSL.Verify == nil {
253		c.SSL.Verify = antiboolFromEnv([]string{
254			EnvVaultSkipVerify, api.EnvVaultInsecure}, true)
255	}
256	c.SSL.Finalize()
257
258	// Order of precedence
259	// 1. `vault_agent_token_file` configuration value
260	// 2. `token` configuration value`
261	// 3. `VAULT_TOKEN` environment variable
262	if c.Token == nil {
263		c.Token = stringFromEnv([]string{
264			"VAULT_TOKEN",
265		}, "")
266	}
267
268	if c.VaultAgentTokenFile == nil {
269		if StringVal(c.Token) == "" {
270			if homePath != "" {
271				c.Token = stringFromFile([]string{
272					homePath + "/.vault-token",
273				}, "")
274			}
275		}
276	} else {
277		c.Token = stringFromFile([]string{*c.VaultAgentTokenFile}, "")
278	}
279
280	if c.Transport == nil {
281		c.Transport = DefaultTransportConfig()
282	}
283	c.Transport.Finalize()
284
285	if c.UnwrapToken == nil {
286		c.UnwrapToken = boolFromEnv([]string{
287			"VAULT_UNWRAP_TOKEN",
288		}, DefaultVaultUnwrapToken)
289	}
290
291	if c.Enabled == nil {
292		c.Enabled = Bool(StringPresent(c.Address))
293	}
294}
295
296// GoString defines the printable version of this struct.
297func (c *VaultConfig) GoString() string {
298	if c == nil {
299		return "(*VaultConfig)(nil)"
300	}
301
302	return fmt.Sprintf("&VaultConfig{"+
303		"Address:%s, "+
304		"Enabled:%s, "+
305		"Grace:%s, "+
306		"Namespace:%s,"+
307		"RenewToken:%s, "+
308		"Retry:%#v, "+
309		"SSL:%#v, "+
310		"Token:%t, "+
311		"VaultAgentTokenFile:%t, "+
312		"Transport:%#v, "+
313		"UnwrapToken:%s"+
314		"}",
315		StringGoString(c.Address),
316		BoolGoString(c.Enabled),
317		TimeDurationGoString(c.Grace),
318		StringGoString(c.Namespace),
319		BoolGoString(c.RenewToken),
320		c.Retry,
321		c.SSL,
322		StringPresent(c.Token),
323		StringPresent(c.VaultAgentTokenFile),
324		c.Transport,
325		BoolGoString(c.UnwrapToken),
326	)
327}
328