constancy 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -9
- data/lib/constancy.rb +2 -0
- data/lib/constancy/cli/config_command.rb +26 -18
- data/lib/constancy/config.rb +56 -103
- data/lib/constancy/sync_target.rb +11 -3
- data/lib/constancy/token_source.rb +94 -0
- data/lib/constancy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f4ece9e3ea596821b57e8e4e2f4e11d70833df49f3480082a4438a1a018a69b
|
4
|
+
data.tar.gz: 5cead608d291729245435375774093004032877b5e4e447aa7ebf0ff8a7b0e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b40eb6bc0491fce5719d10c44cd11b154f71ea6415a5f5564b116ed7814a8d796deb046cf18e6d74cf17b8da15a5726a094141ae491506d1e5a3739c192f6b6
|
7
|
+
data.tar.gz: c45737ecc9a1c1aa92b0bfb46594dbe5ce8be6dac55f4e9efb3e30655b65bec9ec1cccec1895a0805da86c9b8bd6193f5116b11798c3e2a1a24d2f6d3b159d24
|
data/README.md
CHANGED
@@ -20,14 +20,14 @@ synchronize the changes from the filesystem to Consul.
|
|
20
20
|
local:consul/config => consul:dc1:config/myapp
|
21
21
|
Keys scanned: 80
|
22
22
|
|
23
|
-
UPDATE config/myapp/prod/ip-
|
23
|
+
UPDATE config/myapp/prod/ip-allowlist.json
|
24
24
|
-------------------------------------------------------------------------------------
|
25
25
|
-["10.8.0.0/16"]
|
26
26
|
+["10.8.0.0/16","10.9.10.0/24"]
|
27
27
|
-------------------------------------------------------------------------------------
|
28
28
|
|
29
29
|
Keys to update: 1
|
30
|
-
~ config/myapp/prod/ip-
|
30
|
+
~ config/myapp/prod/ip-allowlist.json
|
31
31
|
|
32
32
|
You can also limit your command to specific synchronization targets by using
|
33
33
|
the `--target` flag:
|
@@ -38,19 +38,19 @@ the `--target` flag:
|
|
38
38
|
local:consul/config => consul:dc1:config/myapp
|
39
39
|
Keys scanned: 80
|
40
40
|
|
41
|
-
UPDATE config/myapp/prod/ip-
|
41
|
+
UPDATE config/myapp/prod/ip-allowlist.json
|
42
42
|
-------------------------------------------------------------------------------------
|
43
43
|
-["10.8.0.0/16"]
|
44
44
|
+["10.8.0.0/16","10.9.10.0/24"]
|
45
45
|
-------------------------------------------------------------------------------------
|
46
46
|
|
47
47
|
Keys to update: 1
|
48
|
-
~ config/myapp/prod/ip-
|
48
|
+
~ config/myapp/prod/ip-allowlist.json
|
49
49
|
|
50
50
|
Do you want to push these changes?
|
51
51
|
Enter 'yes' to continue: yes
|
52
52
|
|
53
|
-
UPDATE config/myapp/prod/ip-
|
53
|
+
UPDATE config/myapp/prod/ip-allowlist.json OK
|
54
54
|
|
55
55
|
Run `constancy --help` for additional options and commands.
|
56
56
|
|
@@ -127,6 +127,9 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
127
127
|
# 'none': expect no Consul token (although env vars will be used if they are set)
|
128
128
|
# 'env': expect Consul token to be set in CONSUL_TOKEN or CONSUL_HTTP_TOKEN
|
129
129
|
# 'vault': read Consul token from Vault based on settings in the 'vault' section
|
130
|
+
# 'vault.<label>': a named Vault token source, eg `vault.us-east-1` or `vault.dev`
|
131
|
+
# NOTE: labels must begin with a letter and may contain only (ASCII) letters,
|
132
|
+
# numbers, hyphens, and underscores
|
130
133
|
|
131
134
|
# the vault section is only necessary if consul.token_source is set to 'vault'
|
132
135
|
vault:
|
@@ -146,6 +149,15 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
146
149
|
# but can be set to something else for static values.
|
147
150
|
consul_token_field: token
|
148
151
|
|
152
|
+
# You can define one or more 'vault.<label>' sections to define alternative Vault
|
153
|
+
# token sources for use in individual sync targets.
|
154
|
+
vault.other:
|
155
|
+
url: https://your.vault.example
|
156
|
+
consul_token_path: consul/creds/my-other-role
|
157
|
+
vault.dev:
|
158
|
+
url: https://dev.vault.example
|
159
|
+
consul_token_path: consul/creds/my-dev-role
|
160
|
+
|
149
161
|
sync:
|
150
162
|
# sync is an array of hashes of sync target configurations
|
151
163
|
# Fields:
|
@@ -167,6 +179,10 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
167
179
|
# containing a hash of remote keys if this sync target has
|
168
180
|
# type=file. This path is calculated relative to the directory
|
169
181
|
# containing the configuration file.
|
182
|
+
# token_source - An alternative token source other than the
|
183
|
+
# default. Potential values are the same as for the
|
184
|
+
# consul.token_source config value: 'none', 'env', 'vault',
|
185
|
+
# or 'vault.<label>'.
|
170
186
|
# delete - Whether or not to delete remote keys that do not exist
|
171
187
|
# in the local filesystem. This inherits the setting from the
|
172
188
|
# `constancy` section, or if not specified, defaults to `false`.
|
@@ -195,6 +211,7 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
195
211
|
type: dir
|
196
212
|
datacenter: dc1
|
197
213
|
path: consul/private
|
214
|
+
token_source: vault.dev
|
198
215
|
delete: true
|
199
216
|
- name: yourapp-config
|
200
217
|
prefix: config/yourapp
|
@@ -354,10 +371,10 @@ Constancy may be partially configured using environment variables:
|
|
354
371
|
interacting with the API. Otherwise, by default the agent's `acl_token`
|
355
372
|
setting is used implicitly.
|
356
373
|
* `VAULT_ADDR` and `VAULT_TOKEN` - if `consul.token_source` is set to `vault`
|
357
|
-
these variables are used to authenticate to Vault. If
|
358
|
-
set, Constancy will attempt to read a token from
|
359
|
-
`url` field is set, it will take priority over the
|
360
|
-
variable, but one or the other must be set.
|
374
|
+
or `vault.<label>`, these variables are used to authenticate to Vault. If
|
375
|
+
`VAULT_TOKEN` is not set, Constancy will attempt to read a token from
|
376
|
+
`~/.vault-token`. If the `url` field is set, it will take priority over the
|
377
|
+
`VAULT_ADDR` environment variable, but one or the other must be set.
|
361
378
|
|
362
379
|
|
363
380
|
## Roadmap
|
data/lib/constancy.rb
CHANGED
@@ -4,10 +4,12 @@ require 'erb'
|
|
4
4
|
require 'imperium'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'ostruct'
|
7
|
+
require 'vault'
|
7
8
|
require 'yaml'
|
8
9
|
|
9
10
|
require_relative 'constancy/version'
|
10
11
|
require_relative 'constancy/config'
|
12
|
+
require_relative 'constancy/token_source'
|
11
13
|
require_relative 'constancy/diff'
|
12
14
|
require_relative 'constancy/sync_target'
|
13
15
|
|
@@ -8,18 +8,25 @@ class Constancy
|
|
8
8
|
Constancy::CLI.configure(call_external_apis: false)
|
9
9
|
|
10
10
|
puts " Config file: #{Constancy.config.config_file}"
|
11
|
-
puts " Consul URL: #{Constancy.config.
|
11
|
+
puts " Consul URL: #{Constancy.config.consul_url}"
|
12
12
|
puts " Verbose: #{Constancy.config.verbose?.to_s.bold}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
puts
|
14
|
+
puts " Defined Consul Token Sources:"
|
15
|
+
default_src_name = Constancy.config.default_consul_token_source.name
|
16
|
+
srcs = Constancy.config.consul_token_sources
|
17
|
+
( %w( none env ) + ( srcs.keys.sort - %w( none env ) ) ).each do |name|
|
18
18
|
puts
|
19
|
-
puts "
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
puts " #{name}:#{ default_src_name == name ? " (DEFAULT)".bold : ""}"
|
20
|
+
case name
|
21
|
+
when "none"
|
22
|
+
puts " uses CONSUL_HTTP_TOKEN or CONSUL_TOKEN env var if available"
|
23
|
+
when "env"
|
24
|
+
puts " requires CONSUL_HTTP_TOKEN or CONSUL_TOKEN env var"
|
25
|
+
when /^vault/
|
26
|
+
puts " address: #{srcs[name].vault_addr}"
|
27
|
+
puts " path: #{srcs[name].consul_token_path}"
|
28
|
+
puts " field: #{srcs[name].consul_token_field}"
|
29
|
+
end
|
23
30
|
end
|
24
31
|
puts
|
25
32
|
puts "Sync target defaults:"
|
@@ -35,16 +42,17 @@ class Constancy
|
|
35
42
|
else
|
36
43
|
print '*'
|
37
44
|
end
|
38
|
-
puts "
|
39
|
-
puts "
|
40
|
-
puts "
|
41
|
-
puts "
|
42
|
-
puts "
|
43
|
-
puts "
|
45
|
+
puts " Datacenter: #{target.datacenter}"
|
46
|
+
puts " Local type: #{target.type == :dir ? 'Directory' : 'Single file'}"
|
47
|
+
puts " #{target.type == :dir ? " Dir" : "File"} path: #{target.path}"
|
48
|
+
puts " Prefix: #{target.prefix}"
|
49
|
+
puts " Token Source: #{target.token_source.name}"
|
50
|
+
puts " Autochomp? #{target.chomp?}"
|
51
|
+
puts " Delete? #{target.delete?}"
|
44
52
|
if not target.exclude.empty?
|
45
|
-
puts "
|
53
|
+
puts " Exclusions:"
|
46
54
|
target.exclude.each do |exclusion|
|
47
|
-
puts "
|
55
|
+
puts " - #{exclusion}"
|
48
56
|
end
|
49
57
|
end
|
50
58
|
puts
|
data/lib/constancy/config.rb
CHANGED
@@ -11,6 +11,8 @@ class Constancy
|
|
11
11
|
class Config
|
12
12
|
CONFIG_FILENAMES = %w( constancy.yml )
|
13
13
|
VALID_CONFIG_KEYS = %w( sync consul vault constancy )
|
14
|
+
VALID_VAULT_KEY_PATTERNS = [ %r{^vault\.[A-Za-z][A-Za-z0-9_-]*$}, %r{^vault$} ]
|
15
|
+
VALID_CONFIG_KEY_PATTERNS = VALID_VAULT_KEY_PATTERNS
|
14
16
|
VALID_CONSUL_CONFIG_KEYS = %w( url datacenter token_source )
|
15
17
|
VALID_VAULT_CONFIG_KEYS = %w( url consul_token_path consul_token_field )
|
16
18
|
VALID_CONSTANCY_CONFIG_KEYS = %w( verbose chomp delete color )
|
@@ -18,7 +20,8 @@ class Constancy
|
|
18
20
|
DEFAULT_CONSUL_TOKEN_SOURCE = "none"
|
19
21
|
DEFAULT_VAULT_CONSUL_TOKEN_FIELD = "token"
|
20
22
|
|
21
|
-
attr_accessor :config_file, :base_dir, :
|
23
|
+
attr_accessor :config_file, :base_dir, :consul_url, :default_consul_token_source,
|
24
|
+
:sync_targets, :target_allowlist, :call_external_apis, :consul_token_sources
|
22
25
|
|
23
26
|
class << self
|
24
27
|
# discover the nearest config file
|
@@ -34,6 +37,15 @@ class Constancy
|
|
34
37
|
|
35
38
|
dir == "/" ? nil : self.discover(dir: File.dirname(dir))
|
36
39
|
end
|
40
|
+
|
41
|
+
def only_valid_config_keys!(keylist)
|
42
|
+
(keylist - VALID_CONFIG_KEYS).each do |key|
|
43
|
+
if not VALID_CONFIG_KEY_PATTERNS.find { |pattern| key =~ pattern }
|
44
|
+
raise Constancy::ConfigFileInvalid.new("'#{key}' is not a valid configuration key")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
true
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
51
|
def initialize(path: nil, targets: nil, call_external_apis: true)
|
@@ -51,7 +63,7 @@ class Constancy
|
|
51
63
|
|
52
64
|
self.config_file = File.expand_path(self.config_file)
|
53
65
|
self.base_dir = File.dirname(self.config_file)
|
54
|
-
self.
|
66
|
+
self.target_allowlist = targets
|
55
67
|
self.call_external_apis = call_external_apis
|
56
68
|
parse!
|
57
69
|
end
|
@@ -72,7 +84,11 @@ class Constancy
|
|
72
84
|
@use_color
|
73
85
|
end
|
74
86
|
|
75
|
-
|
87
|
+
def parse_vault_token_sources!(raw)
|
88
|
+
raw.keys.select { |key| VALID_VAULT_KEY_PATTERNS.find { |pattern| key =~ pattern } }.collect do |key|
|
89
|
+
[key, Constancy::VaultTokenSource.new(name: key, config: raw[key])]
|
90
|
+
end.to_h
|
91
|
+
end
|
76
92
|
|
77
93
|
def parse!
|
78
94
|
raw = {}
|
@@ -91,116 +107,40 @@ class Constancy
|
|
91
107
|
raise Constancy::ConfigFileInvalid.new("Config file must form a hash")
|
92
108
|
end
|
93
109
|
|
94
|
-
|
95
|
-
|
96
|
-
|
110
|
+
Constancy::Config.only_valid_config_keys!(raw.keys)
|
111
|
+
|
112
|
+
self.consul_token_sources = {
|
113
|
+
"none" => Constancy::PassiveTokenSource.new,
|
114
|
+
"env" => Constancy::EnvTokenSource.new,
|
115
|
+
}.merge(
|
116
|
+
self.parse_vault_token_sources!(raw),
|
117
|
+
)
|
97
118
|
|
98
119
|
raw['consul'] ||= {}
|
99
120
|
if not raw['consul'].is_a? Hash
|
100
121
|
raise Constancy::ConfigFileInvalid.new("'consul' must be a hash")
|
101
122
|
end
|
102
123
|
|
103
|
-
if (raw['consul'].keys -
|
104
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the consul config: #{
|
124
|
+
if (raw['consul'].keys - VALID_CONSUL_CONFIG_KEYS) != []
|
125
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the consul config: #{VALID_CONSUL_CONFIG_KEYS.join(", ")}")
|
105
126
|
end
|
106
127
|
|
107
|
-
consul_url = raw['consul']['url'] ||
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
case self.consul_token_source
|
114
|
-
when "none"
|
115
|
-
# nothing to do
|
116
|
-
|
117
|
-
when "env"
|
118
|
-
if consul_token.nil? or consul_token == ""
|
119
|
-
raise Constancy::ConsulTokenRequired.new("Consul token_source is set to 'env' but neither CONSUL_TOKEN nor CONSUL_HTTP_TOKEN is set")
|
120
|
-
end
|
121
|
-
|
122
|
-
when "vault"
|
123
|
-
require 'vault'
|
124
|
-
|
125
|
-
raw['vault'] ||= {}
|
126
|
-
if not raw['vault'].is_a? Hash
|
127
|
-
raise Constancy::ConfigFileInvalid.new("'vault' must be a hash")
|
128
|
-
end
|
129
|
-
|
130
|
-
if (raw['vault'].keys - Constancy::Config::VALID_VAULT_CONFIG_KEYS) != []
|
131
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the vault config: #{Constancy::Config::VALID_VAULT_CONFIG_KEYS.join(", ")}")
|
132
|
-
end
|
133
|
-
|
134
|
-
vault_path = raw['vault']['consul_token_path']
|
135
|
-
if vault_path.nil? or vault_path == ""
|
136
|
-
raise Constancy::ConfigFileInvalid.new("vault.consul_token_path must be specified to use vault as a token source")
|
137
|
-
end
|
138
|
-
|
139
|
-
# prioritize the config file over environment variables for vault address
|
140
|
-
vault_addr = raw['vault']['url'] || ENV['VAULT_ADDR']
|
141
|
-
if vault_addr.nil? or vault_addr == ""
|
142
|
-
raise Constancy::VaultConfigInvalid.new("Vault address must be set in vault.url or VAULT_ADDR")
|
143
|
-
end
|
144
|
-
|
145
|
-
vault_token = ENV['VAULT_TOKEN']
|
146
|
-
if vault_token.nil? or vault_token == ""
|
147
|
-
vault_token_file = File.expand_path("~/.vault-token")
|
148
|
-
if File.exist?(vault_token_file)
|
149
|
-
vault_token = File.read(vault_token_file)
|
150
|
-
else
|
151
|
-
raise Constancy::VaultConfigInvalid.new("Vault token must be set in ~/.vault-token or VAULT_TOKEN")
|
128
|
+
self.consul_url = raw['consul']['url'] || DEFAULT_CONSUL_URL
|
129
|
+
srcname = raw['consul']['token_source'] || DEFAULT_CONSUL_TOKEN_SOURCE
|
130
|
+
self.default_consul_token_source =
|
131
|
+
self.consul_token_sources[srcname].tap do |src|
|
132
|
+
if src.nil?
|
133
|
+
raise Constancy::ConfigFileInvalid.new("Consul token source '#{consul_token_source}' is not defined")
|
152
134
|
end
|
153
135
|
end
|
154
136
|
|
155
|
-
vault_field = raw['vault']['consul_token_field'] || Constancy::Config::DEFAULT_VAULT_CONSUL_TOKEN_FIELD
|
156
|
-
|
157
|
-
self.vault_config = OpenStruct.new(
|
158
|
-
url: vault_addr,
|
159
|
-
consul_token_path: vault_path,
|
160
|
-
consul_token_field: vault_field,
|
161
|
-
)
|
162
|
-
|
163
|
-
# don't waste time talking to Vault if this is just a config parsing run
|
164
|
-
if self.call_external_apis
|
165
|
-
ENV['VAULT_ADDR'] = vault_addr
|
166
|
-
ENV['VAULT_TOKEN'] = vault_token
|
167
|
-
|
168
|
-
begin
|
169
|
-
response = Vault.logical.read(vault_path)
|
170
|
-
consul_token = response.data[vault_field.to_sym]
|
171
|
-
|
172
|
-
if response.lease_id
|
173
|
-
at_exit {
|
174
|
-
begin
|
175
|
-
Vault.sys.revoke(response.lease_id)
|
176
|
-
rescue => e
|
177
|
-
# this is fine
|
178
|
-
end
|
179
|
-
}
|
180
|
-
end
|
181
|
-
|
182
|
-
rescue => e
|
183
|
-
raise Constancy::VaultConfigInvalid.new("Are you logged in to Vault?\n\n#{e}")
|
184
|
-
end
|
185
|
-
|
186
|
-
if consul_token.nil? or consul_token == ""
|
187
|
-
raise Constancy::VaultConfigInvalid.new("Could not acquire a Consul token from Vault")
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
else
|
192
|
-
raise Constancy::ConfigFileInvalid.new("Only the following values are valid for token_source: none, env, vault")
|
193
|
-
end
|
194
|
-
|
195
|
-
self.consul = Imperium::Configuration.new(url: consul_url, token: consul_token)
|
196
|
-
|
197
137
|
raw['constancy'] ||= {}
|
198
138
|
if not raw['constancy'].is_a? Hash
|
199
139
|
raise Constancy::ConfigFileInvalid.new("'constancy' must be a hash")
|
200
140
|
end
|
201
141
|
|
202
|
-
if (raw['constancy'].keys -
|
203
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the 'constancy' config block: #{
|
142
|
+
if (raw['constancy'].keys - VALID_CONSTANCY_CONFIG_KEYS) != []
|
143
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the 'constancy' config block: #{VALID_CONSTANCY_CONFIG_KEYS.join(", ")}")
|
204
144
|
end
|
205
145
|
|
206
146
|
# verbose: default false
|
@@ -233,6 +173,7 @@ class Constancy
|
|
233
173
|
|
234
174
|
self.sync_targets = []
|
235
175
|
raw['sync'].each do |target|
|
176
|
+
token_source = self.default_consul_token_source
|
236
177
|
if target.is_a? Hash
|
237
178
|
target['datacenter'] ||= raw['consul']['datacenter']
|
238
179
|
if target['chomp'].nil?
|
@@ -241,19 +182,31 @@ class Constancy
|
|
241
182
|
if target['delete'].nil?
|
242
183
|
target['delete'] = self.delete?
|
243
184
|
end
|
185
|
+
if not target['token_source'].nil?
|
186
|
+
token_source = self.consul_token_sources[target['token_source']]
|
187
|
+
if token_source.nil?
|
188
|
+
raise Constancy::ConfigFileInvalid.new("Consul token source '#{target['token_source']}' is not defined")
|
189
|
+
end
|
190
|
+
target.delete('token_source')
|
191
|
+
end
|
244
192
|
end
|
245
193
|
|
246
|
-
if not self.
|
247
|
-
# unnamed targets cannot be
|
194
|
+
if not self.target_allowlist.nil?
|
195
|
+
# unnamed targets cannot be allowlisted
|
248
196
|
next if target['name'].nil?
|
249
197
|
|
250
|
-
# named targets must be on the
|
251
|
-
next if not self.
|
198
|
+
# named targets must be on the allowlist
|
199
|
+
next if not self.target_allowlist.include?(target['name'])
|
252
200
|
end
|
253
201
|
|
254
|
-
|
202
|
+
# only try to fetch consul tokens if we are actually going to do work
|
203
|
+
consul_token = if self.call_external_apis
|
204
|
+
token_source.consul_token
|
205
|
+
else
|
206
|
+
""
|
207
|
+
end
|
208
|
+
self.sync_targets << Constancy::SyncTarget.new(config: target, consul_url: consul_url, token_source: token_source, base_dir: self.base_dir, call_external_apis: self.call_external_apis)
|
255
209
|
end
|
256
|
-
|
257
210
|
end
|
258
211
|
end
|
259
212
|
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
class Constancy
|
4
4
|
class SyncTarget
|
5
5
|
VALID_CONFIG_KEYS = %w( name type datacenter prefix path exclude chomp delete erb_enabled )
|
6
|
-
attr_accessor :name, :type, :datacenter, :prefix, :path, :exclude, :consul, :erb_enabled
|
6
|
+
attr_accessor :name, :type, :datacenter, :prefix, :path, :exclude, :consul, :erb_enabled, :consul_url, :token_source, :call_external_apis
|
7
7
|
|
8
8
|
REQUIRED_CONFIG_KEYS = %w( prefix )
|
9
9
|
VALID_TYPES = [ :dir, :file ]
|
10
10
|
DEFAULT_TYPE = :dir
|
11
11
|
|
12
|
-
def initialize(config:,
|
12
|
+
def initialize(config:, consul_url:, token_source:, base_dir:, call_external_apis: true)
|
13
13
|
if not config.is_a? Hash
|
14
14
|
raise Constancy::ConfigFileInvalid.new("Sync target entries must be specified as hashes")
|
15
15
|
end
|
@@ -46,7 +46,15 @@ class Constancy
|
|
46
46
|
@do_delete = false
|
47
47
|
end
|
48
48
|
|
49
|
-
self.
|
49
|
+
self.call_external_apis = call_external_apis
|
50
|
+
self.consul_url = consul_url
|
51
|
+
self.token_source = token_source
|
52
|
+
token = if self.call_external_apis
|
53
|
+
self.token_source.consul_token
|
54
|
+
else
|
55
|
+
""
|
56
|
+
end
|
57
|
+
self.consul = Imperium::KV.new(Imperium::Configuration.new(url: self.consul_url, token: token))
|
50
58
|
self.erb_enabled = config['erb_enabled']
|
51
59
|
end
|
52
60
|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# This software is public domain. No rights are reserved. See LICENSE for more information.
|
2
|
+
|
3
|
+
class Constancy
|
4
|
+
# use env vars if defined, but otherwise just return an empty string
|
5
|
+
class PassiveTokenSource
|
6
|
+
def name
|
7
|
+
"none"
|
8
|
+
end
|
9
|
+
|
10
|
+
def consul_token
|
11
|
+
ENV['CONSUL_HTTP_TOKEN'] || ENV['CONSUL_TOKEN'] || ""
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# use env vars and raise an error if none is found
|
16
|
+
class EnvTokenSource
|
17
|
+
def name
|
18
|
+
"env"
|
19
|
+
end
|
20
|
+
|
21
|
+
def consul_token
|
22
|
+
consul_token = ENV['CONSUL_HTTP_TOKEN'] || ENV['CONSUL_TOKEN']
|
23
|
+
if consul_token.nil? or consul_token == ""
|
24
|
+
raise Constancy::ConsulTokenRequired.new("Consul token_source was set to 'env' but neither CONSUL_TOKEN nor CONSUL_HTTP_TOKEN is set")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class VaultTokenSource
|
30
|
+
attr_accessor :name, :vault_addr, :vault_token, :consul_token_path, :consul_token_field
|
31
|
+
|
32
|
+
def initialize(name:, config:)
|
33
|
+
self.name = name
|
34
|
+
|
35
|
+
config ||= {}
|
36
|
+
if not config.is_a? Hash
|
37
|
+
raise Constancy::ConfigFileInvalid.new("'#{name}' must be a hash")
|
38
|
+
end
|
39
|
+
|
40
|
+
if (config.keys - Constancy::Config::VALID_VAULT_CONFIG_KEYS) != []
|
41
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in a vault config: #{Constancy::Config::VALID_VAULT_CONFIG_KEYS.join(", ")}")
|
42
|
+
end
|
43
|
+
|
44
|
+
self.consul_token_path = config['consul_token_path']
|
45
|
+
if self.consul_token_path.nil? or self.consul_token_path == ""
|
46
|
+
raise Constancy::ConfigFileInvalid.new("consul_token_path must be specified to use '#{name}' as a token source")
|
47
|
+
end
|
48
|
+
|
49
|
+
# prioritize the config file over environment variables for vault address
|
50
|
+
self.vault_addr = config['url'] || ENV['VAULT_ADDR']
|
51
|
+
if self.vault_addr.nil? or self.vault_addr == ""
|
52
|
+
raise Constancy::VaultConfigInvalid.new("Vault address must be set in #{name}.vault_addr or VAULT_ADDR")
|
53
|
+
end
|
54
|
+
|
55
|
+
self.vault_token = ENV['VAULT_TOKEN']
|
56
|
+
if self.vault_token.nil? or self.vault_token == ""
|
57
|
+
vault_token_file = File.expand_path("~/.vault-token")
|
58
|
+
if File.exist?(vault_token_file)
|
59
|
+
self.vault_token = File.read(vault_token_file)
|
60
|
+
else
|
61
|
+
raise Constancy::VaultConfigInvalid.new("Vault token must be set in ~/.vault-token or VAULT_TOKEN")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
self.consul_token_field = config['consul_token_field'] || Constancy::Config::DEFAULT_VAULT_CONSUL_TOKEN_FIELD
|
66
|
+
end
|
67
|
+
|
68
|
+
def consul_token
|
69
|
+
if @consul_token.nil?
|
70
|
+
begin
|
71
|
+
response = Vault::Client.new(address: self.vault_addr, token: self.vault_token).logical.read(self.consul_token_path)
|
72
|
+
@consul_token = response.data[self.consul_token_field.to_sym]
|
73
|
+
if response.lease_id
|
74
|
+
at_exit {
|
75
|
+
begin
|
76
|
+
Vault::Client.new(address: self.vault_addr, token: self.vault_token).sys.revoke(response.lease_id)
|
77
|
+
rescue => e
|
78
|
+
# this is fine
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
rescue => e
|
84
|
+
raise Constancy::VaultConfigInvalid.new("Are you logged in to Vault?\n\n#{e}")
|
85
|
+
end
|
86
|
+
|
87
|
+
if @consul_token.nil? or @consul_token == ""
|
88
|
+
raise Constancy::VaultConfigInvalid.new("Could not acquire a Consul token from Vault")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@consul_token
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/constancy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: constancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Adams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: imperium
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/constancy/config.rb
|
88
88
|
- lib/constancy/diff.rb
|
89
89
|
- lib/constancy/sync_target.rb
|
90
|
+
- lib/constancy/token_source.rb
|
90
91
|
- lib/constancy/version.rb
|
91
92
|
homepage: https://github.com/daveadams/constancy
|
92
93
|
licenses:
|