secret_config 0.9.1 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -817
- data/lib/secret_config.rb +1 -1
- data/lib/secret_config/cli.rb +7 -1
- data/lib/secret_config/parser.rb +1 -1
- data/lib/secret_config/setting_interpolator.rb +10 -10
- data/lib/secret_config/string_interpolator.rb +7 -7
- data/lib/secret_config/version.rb +1 -1
- data/test/config/application.yml +4 -4
- data/test/parser_test.rb +2 -2
- data/test/providers/file_test.rb +1 -1
- data/test/providers/ssm_test.rb +1 -1
- data/test/setting_interpolator_test.rb +26 -26
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3379ac282a4a14c4b577024fbd417e94aaa498c974b5371026d5142515050810
|
4
|
+
data.tar.gz: f70b58807cfa0bbe3d668d975b1dba7ca4ee67126f7fe0b380a2b3b6e66c4cae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 291fd4a292787ad879136b41511faebd7e9ad4e18caf071618d93c0b6ade144cfd4deab3e01313e81b130858d12037141a83bb99a9d516df316b57519beb12a9
|
7
|
+
data.tar.gz: 7eec020e42b794a3230dcff53bd6dde3ff67ae7bf4cc4dd8477d6ef13d7be4afead65cefaa337ac74abf8f85f1b9d30f1fb69c3821391c45e92aaa22b04c3a50
|
data/README.md
CHANGED
@@ -5,834 +5,35 @@ Centralized Configuration and Secrets Management for Ruby and Rails applications
|
|
5
5
|
|
6
6
|
Securely store configuration information centrally, supporting multiple tenants of the same application.
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
Note that the command line program name has changed from `secret_config` to `secret-config`.
|
11
|
-
Be careful that the arguments have also changed. The arguments are now consistent across operations.
|
12
|
-
The command line examples below have also been updated to reflect the changes.
|
13
|
-
|
14
|
-
Please run `secret-config --help` to see the new arguments and updated operations.
|
15
|
-
|
16
|
-
## Overview
|
17
|
-
|
18
|
-
Securely store centralized configuration information such as:
|
19
|
-
* Settings
|
20
|
-
* Passwords
|
21
|
-
* Encryption keys and certificates
|
22
|
-
|
23
|
-
## Features
|
24
|
-
|
25
|
-
Supports storing configuration information in:
|
26
|
-
* File
|
27
|
-
* Development and testing use.
|
28
|
-
* Environment Variables
|
29
|
-
* Environment Variables take precedence and can be used to override any setting.
|
30
|
-
* AWS System Manager Parameter Store
|
31
|
-
* Encrypt and securely store secrets such as passwords centrally.
|
32
|
-
|
33
|
-
Since all values are stored as strings in the central directory or config file, the following type conversions
|
34
|
-
are supported:
|
35
|
-
* integer
|
36
|
-
* float
|
37
|
-
* string
|
38
|
-
* boolean
|
39
|
-
* symbol
|
40
|
-
* json
|
41
|
-
|
42
|
-
Supported conversions:
|
43
|
-
* base64
|
44
|
-
|
45
|
-
Arrays are also supported when the value contains a known separator by which to break down the values.
|
46
|
-
|
47
|
-
## Benefits
|
48
|
-
|
49
|
-
Benefits of moving sensitive configuration information into AWS System Manager Parameter Store:
|
50
|
-
|
51
|
-
* Hierarchical structure is maintained.
|
52
|
-
* Environment variables force all config into a single level.
|
53
|
-
* Reduces the number of environment variables.
|
54
|
-
* In a large application the number of secrets can grow dramatically.
|
55
|
-
* Replaces sensitive data stored in local yaml or configuration files.
|
56
|
-
* Including securing and managing encryption keys.
|
57
|
-
* When encryption keys change, such as during a key rotation, config files don't have to be changed.
|
58
|
-
* Removes security concerns with placing passwords in the clear into environment variables.
|
59
|
-
* AWS System Manager Parameter Store does not charge for parameters.
|
60
|
-
* Still recommend using a custom KMS key that charges only $1 per month.
|
61
|
-
* Amounts as of 4/2019. Confirm what AWS charges you for these services.
|
62
|
-
* AWS Secrets Manager charges for every secret being managed, which can accumulate quickly with large projects.
|
63
|
-
* Configure multiple distinct application instances to support multiple tenants.
|
64
|
-
* For example, use separate databases with unique credentials for each tenant.
|
65
|
-
* Separation of responsibilities is achieved since operations can manage production configuration.
|
66
|
-
* Developers do not need to be involved with production configuration such as host names and passwords.
|
67
|
-
* All values are encrypted by default when stored in the AWS Parameter Store.
|
68
|
-
* Prevents accidentally not encrypting sensitive data.
|
69
|
-
|
70
|
-
## Introduction
|
71
|
-
|
72
|
-
When Secret Config starts up it reads all configuration entries into memory for all keys under the configured path.
|
73
|
-
This means that once Secret Config has initialized all calls to Secret Config are extremely fast.
|
74
|
-
|
75
|
-
The in-memory copy of the registry can be refreshed at any time by calling `SecretConfig.refresh!`. It can be refreshed
|
76
|
-
via a process signal, or by calling it through an event, or via a messaging system.
|
77
|
-
|
78
|
-
It is suggested that any programmatic lookup to values stored in Secret Config are called every time a value is
|
79
|
-
being used, rather than creating a local copy of the value. This ensures that a refresh of the registry will take effect
|
80
|
-
immediately for any code reading from Secret Config.
|
81
|
-
|
82
|
-
## API
|
83
|
-
|
84
|
-
When Secret Config starts up it reads all configuration entries immediately for all keys under the configured path.
|
85
|
-
This means that once Secret Config has initialized all calls to Secret Config are extremely fast.
|
86
|
-
|
87
|
-
Secret Config supports the following programmatic interface:
|
88
|
-
|
89
|
-
### Read values
|
90
|
-
|
91
|
-
Fetch the value for the supplied key, returning nil if not found:
|
92
|
-
|
93
|
-
~~~ruby
|
94
|
-
# Key is present:
|
95
|
-
SecretConfig["logger/level"]
|
96
|
-
# => "info"
|
97
|
-
|
98
|
-
# Key is missing:
|
99
|
-
SecretConfig["logger/blah"]
|
100
|
-
# => nil
|
101
|
-
~~~
|
102
|
-
|
103
|
-
Fetch the value for the supplied key, raising `SecretConfig::MissingMandatoryKey` if not found:
|
104
|
-
|
105
|
-
~~~ruby
|
106
|
-
# Key is present:
|
107
|
-
SecretConfig.fetch("logger/level")
|
108
|
-
# => "info"
|
109
|
-
|
110
|
-
# Key is missing:
|
111
|
-
SecretConfig.fetch("logger/blah")
|
112
|
-
# => SecretConfig::MissingMandatoryKey (Missing configuration value for /development/logger/blah)
|
113
|
-
~~~
|
114
|
-
|
115
|
-
A default value can be supplied when the key is not found in the registry:
|
116
|
-
|
117
|
-
~~~ruby
|
118
|
-
SecretConfig.fetch("logger/level", default: "info")
|
119
|
-
# => "info"
|
120
|
-
~~~
|
121
|
-
|
122
|
-
Since AWS SSM Parameter store and environment variables only support string values,
|
123
|
-
it is neccessary to convert the string back to the type required by the program.
|
124
|
-
|
125
|
-
The following types are supported:
|
126
|
-
`:integer`
|
127
|
-
`:float`
|
128
|
-
`:string`
|
129
|
-
`:boolean`
|
130
|
-
`:symbol`
|
131
|
-
`:json`
|
132
|
-
|
133
|
-
~~~ruby
|
134
|
-
# Without type conversion:
|
135
|
-
SecretConfig.fetch("symmetric_encryption/version")
|
136
|
-
# => "0"
|
137
|
-
|
138
|
-
# With type conversion:
|
139
|
-
SecretConfig.fetch("symmetric_encryption/version", type: :integer)
|
140
|
-
# => 0
|
141
|
-
~~~
|
142
|
-
|
143
|
-
Sometimes it is useful to store arrays of values as a single key.
|
144
|
-
|
145
|
-
~~~ruby
|
146
|
-
# Example: A list of host names could be stored as: "primary.example.net,secondary.example.net,backup.example.net"
|
147
|
-
# To extract it as an array of strings:
|
148
|
-
SecretConfig.fetch("address_services/hostnames", separator: ",")
|
149
|
-
# => ["primary.example.net", "secondary.example.net", "backup.example.net"]
|
150
|
-
|
151
|
-
# Example: A list of ports could be stored as: "12345,5343,26815"
|
152
|
-
# To extract it as an array of Integers:
|
153
|
-
SecretConfig.fetch("address_services/ports", type: :integer, separator: ",")
|
154
|
-
# => [12345, 5343, 26815]
|
155
|
-
~~~
|
156
|
-
|
157
|
-
When storing binary data, it should be encoded with strict base64 encoding. To automatically convert it back to binary
|
158
|
-
specify the encoding as `:base64`
|
159
|
-
|
160
|
-
~~~ruby
|
161
|
-
# Return a value that was stored in Base64 encoding format:
|
162
|
-
SecretConfig.fetch("symmetric_encryption/iv")
|
163
|
-
# => "FW+/wLubAYM+ZU0bWQj59Q=="
|
164
|
-
|
165
|
-
# Base64 decode a value that was stored in Base64 encoding format:
|
166
|
-
SecretConfig.fetch("symmetric_encryption/iv", encoding: :base64)
|
167
|
-
# => "\x15o\xBF\xC0\xBB\x9B\x01\x83>eM\eY\b\xF9\xF5"
|
168
|
-
~~~
|
169
|
-
|
170
|
-
### Key presence
|
171
|
-
|
172
|
-
Returns whether a key is present in the registry:
|
173
|
-
|
174
|
-
~~~ruby
|
175
|
-
SecretConfig.key?("logger/level")
|
176
|
-
# => true
|
177
|
-
~~~
|
178
|
-
|
179
|
-
### Write values
|
180
|
-
|
181
|
-
When Secret Config is configured to use the AWS SSM Parameter store, its values can be modified:
|
182
|
-
|
183
|
-
~~~ruby
|
184
|
-
SecretConfig["logger/level"] = "debug"
|
185
|
-
~~~
|
186
|
-
|
187
|
-
~~~ruby
|
188
|
-
SecretConfig.set("logger/level", "debug")
|
189
|
-
~~~
|
190
|
-
|
191
|
-
### Configuration
|
192
|
-
|
193
|
-
Returns a Hash copy of the configuration as a tree:
|
194
|
-
|
195
|
-
~~~ruby
|
196
|
-
SecretConfig.configuration
|
197
|
-
~~~
|
198
|
-
|
199
|
-
### Refresh Configuration
|
200
|
-
|
201
|
-
Tell Secret Config to refresh its in-memory copy of the configuration settings.
|
202
|
-
|
203
|
-
~~~ruby
|
204
|
-
SecretConfig.refresh!
|
205
|
-
~~~
|
206
|
-
|
207
|
-
Example, refresh the registry any time a SIGUSR2 is raised, add the following code on startup:
|
208
|
-
|
209
|
-
~~~ruby
|
210
|
-
Signal.trap('USR2') do
|
211
|
-
SecretConfig.refresh!
|
212
|
-
end
|
213
|
-
~~~
|
214
|
-
|
215
|
-
Then to make the process refresh it registry:
|
216
|
-
~~~shell
|
217
|
-
kill -SIGUSR2 1234
|
218
|
-
~~~
|
219
|
-
|
220
|
-
Where `1234` above is the process PID.
|
221
|
-
|
222
|
-
## Development and Test use
|
223
|
-
|
224
|
-
In the development environment create the file `config/application.yml` within which to store local development credentials.
|
225
|
-
Depending on your team setup you may want to use the same file for all developers so can check it into you change control system.
|
226
|
-
|
227
|
-
For example: `config/application.yml`
|
228
|
-
|
229
|
-
~~~yaml
|
230
|
-
development:
|
231
|
-
mysql:
|
232
|
-
database: secret_config_development
|
233
|
-
username: secret_config
|
234
|
-
password: secret_configrules
|
235
|
-
host: 127.0.0.1
|
236
|
-
|
237
|
-
mongo:
|
238
|
-
database: secret_config_development
|
239
|
-
primary: 127.0.0.1:27017
|
240
|
-
secondary: 127.0.0.1:27018
|
241
|
-
|
242
|
-
secrets:
|
243
|
-
secret_key_base: somereallylongstring
|
244
|
-
|
245
|
-
test:
|
246
|
-
mysql:
|
247
|
-
database: secret_config_test
|
248
|
-
username: secret_config
|
249
|
-
password: secret_configrules
|
250
|
-
host: 127.0.0.1
|
251
|
-
|
252
|
-
mongo:
|
253
|
-
database: secret_config_test
|
254
|
-
primary: 127.0.0.1:27017
|
255
|
-
secondary: 127.0.0.1:27018
|
256
|
-
|
257
|
-
secrets:
|
258
|
-
secret_key_base: somereallylongteststring
|
259
|
-
~~~
|
260
|
-
|
261
|
-
Note how the hierarchical nature of configuration values is maintained. Typical environment variable approaches have
|
262
|
-
to flatten everything into a single level.
|
263
|
-
|
264
|
-
Note: Do not put any production credentials into this file.
|
265
|
-
|
266
|
-
### Environment Variables
|
267
|
-
|
268
|
-
Any of the above values can be overridden with an environment variable, unless explicitly configured `SecretConfig.check_env_var = false`.
|
269
|
-
|
270
|
-
To overwrite any of these settings with an environment variable:
|
271
|
-
|
272
|
-
* Join the keys together with an '_'
|
273
|
-
* Convert to uppercase
|
274
|
-
|
275
|
-
For example, `mysql/host` can be overridden with the env var:
|
276
|
-
|
277
|
-
export MYSQL_HOST=test.server
|
278
|
-
|
279
|
-
### Applying to existing config files
|
280
|
-
|
281
|
-
Go through all the configuration files and look for sensitive data such as passwords:
|
282
|
-
|
283
|
-
Example, an unchanged common `database.yml`:
|
284
|
-
|
285
|
-
~~~yaml
|
286
|
-
defaults: &defaults
|
287
|
-
encoding: utf8
|
288
|
-
adapter: mysql2
|
289
|
-
|
290
|
-
development:
|
291
|
-
<<: *defaults
|
292
|
-
database: secure_config_development
|
293
|
-
username: jack
|
294
|
-
password: jackrules
|
295
|
-
host: localhost
|
296
|
-
|
297
|
-
test:
|
298
|
-
<<: *defaults
|
299
|
-
database: secure_config_test
|
300
|
-
username: tester
|
301
|
-
password: khjsdjhdsjhdsr32
|
302
|
-
host: test.server
|
303
|
-
|
304
|
-
production:
|
305
|
-
<<: *defaults
|
306
|
-
database: secure_config_production
|
307
|
-
username: product
|
308
|
-
password: donotexpose45
|
309
|
-
host: production.server
|
310
|
-
~~~
|
311
|
-
|
312
|
-
Replace the sensitive data with a `SecureConfig.fetch`:
|
313
|
-
|
314
|
-
Updated `database.yml`:
|
315
|
-
|
316
|
-
~~~yaml
|
317
|
-
configuration: &configuration
|
318
|
-
database: <%= SecretConfig.fetch("mysql/database") %>
|
319
|
-
username: <%= SecretConfig.fetch("mysql/username") %>
|
320
|
-
password: <%= SecretConfig.fetch("mysql/password") %>
|
321
|
-
host: <%= SecretConfig.fetch("mysql/host") %>
|
322
|
-
encoding: utf8
|
323
|
-
adapter: mysql2
|
324
|
-
|
325
|
-
development:
|
326
|
-
<<: *configuration
|
327
|
-
|
328
|
-
test:
|
329
|
-
<<: *configuration
|
330
|
-
|
331
|
-
production:
|
332
|
-
<<: *configuration
|
333
|
-
~~~
|
334
|
-
|
335
|
-
Since the secrets are externalized the configuration between environments is simpler.
|
336
|
-
|
337
|
-
### Replacing custom config files
|
338
|
-
|
339
|
-
When writing new components or gems, instead of requiring a proprietary config file, refer
|
340
|
-
to the settings programmatically:
|
341
|
-
|
342
|
-
For example, somewhere in your codebase you need a persistent http connection:
|
343
|
-
|
344
|
-
~~~ruby
|
345
|
-
def http_client
|
346
|
-
@http_client ||=
|
347
|
-
PersistentHTTP.new(
|
348
|
-
name: 'HTTPClient',
|
349
|
-
url: SecretConfig.fetch('http_client/url'),
|
350
|
-
logger: logger,
|
351
|
-
pool_size: SecretConfig.fetch('http_client/pool_size', type: :integer, default: 10),
|
352
|
-
warn_timeout: SecretConfig.fetch('http_client/warn_timeout', type: :float, default: 0.25),
|
353
|
-
open_timeout: SecretConfig.fetch('http_client/open_timeout', type: :float, default: 30),
|
354
|
-
read_timeout: SecretConfig.fetch('http_client/read_timeout', type: :float, default: 30),
|
355
|
-
force_retry: true
|
356
|
-
)
|
357
|
-
end
|
358
|
-
~~~
|
359
|
-
|
360
|
-
Then the application that uses the above library / gem just needs to add the relevant entries to their
|
361
|
-
`application.yml` file:
|
362
|
-
|
363
|
-
~~~yaml
|
364
|
-
http_client:
|
365
|
-
url: https://test.example.com
|
366
|
-
pool_size: 20
|
367
|
-
read_timeout: 300
|
368
|
-
~~~
|
369
|
-
|
370
|
-
This avoids a custom config file just for the above library.
|
371
|
-
|
372
|
-
Additionally the values can be overridden with environment variables at any time:
|
373
|
-
|
374
|
-
export HTTP_CLIENT_URL=https://production.example.com
|
375
|
-
|
376
|
-
## Configuration
|
377
|
-
|
378
|
-
Add the following line to Gemfile
|
379
|
-
|
380
|
-
gem "secret_config"
|
381
|
-
|
382
|
-
Out of the box Secret Config will look in the local file system for the file `config/application.yml`
|
383
|
-
as covered above. By default it will use env var `RAILS_ENV` to define the path to look under for settings.
|
384
|
-
|
385
|
-
The default settings are great for getting started in development and test, but should not be used in production.
|
8
|
+
Checkout https://config.rocketjob.io/
|
386
9
|
|
387
|
-
|
388
|
-
the following lines to the very top of `application.rb` under the line `class Application < Rails::Application`:
|
10
|
+
## Documentation
|
389
11
|
|
390
|
-
|
391
|
-
module MyApp
|
392
|
-
class Application < Rails::Application
|
12
|
+
* [Guide](https://config.rocketjob.io/)
|
393
13
|
|
394
|
-
|
395
|
-
if Rails.env.development? || Rails.env.test?
|
396
|
-
# Use 'config/application.yml'
|
397
|
-
config.secret_config.use :file
|
398
|
-
else
|
399
|
-
# Read configuration from AWS SSM Parameter Store
|
400
|
-
config.secret_config.use :ssm, path: "/#{Rails.env}/my_app"
|
401
|
-
end
|
14
|
+
## Support
|
402
15
|
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
~~~
|
16
|
+
* Questions? Join the chat room on Gitter for [rocketjob support](https://gitter.im/rocketjob/support)
|
17
|
+
* [Report bugs](https://github.com/rocketjob/secret_config/issues)
|
407
18
|
|
408
|
-
|
409
|
-
configuration for this instance of the application. In the example above it uses the rails env and application name
|
410
|
-
by default. This can be overridden using the `SECRET_CONFIG_PATH` environment variable when needed.
|
19
|
+
## v0.10 Upgrade Notes
|
411
20
|
|
412
|
-
|
413
|
-
|
21
|
+
String interpolation has been changed to use `$` instead of `%`. Please change
|
22
|
+
all interpolated strings to use `$` before upgrading.
|
414
23
|
|
415
|
-
|
416
|
-
For example:
|
417
|
-
`export SECRET_CONFIG_PROVIDER=ssm`
|
418
|
-
Or,
|
419
|
-
`export SECRET_CONFIG_PROVIDER=file`
|
24
|
+
Example: `%{date}` needs to be changed to `${date}`
|
420
25
|
|
421
|
-
|
422
|
-
multiple paths. For example:
|
423
|
-
|
424
|
-
/production1/my_application
|
425
|
-
/production2/my_application
|
426
|
-
|
427
|
-
/production/instance1/my_application
|
428
|
-
/production/instance2/my_application
|
429
|
-
|
430
|
-
The `path` is completely flexible, but must be unique for every AWS account under which the application will run.
|
431
|
-
The same `path` can be used in different AWS accounts though. It is also not replicated across regions.
|
432
|
-
|
433
|
-
When writing settings to the parameter store, it is recommended to use a custom KMS key to encrypt the values, if you don't specify a key ID, the system uses the default key associated with your AWS account `alias/aws/ssm`.
|
434
|
-
To supply the key to encrypt the values with, add the `key_id` parameter:
|
435
|
-
|
436
|
-
~~~ruby
|
437
|
-
module MyApp
|
438
|
-
class Application < Rails::Application
|
439
|
-
|
440
|
-
# Add the following lines to configure Secret Config:
|
441
|
-
if Rails.env.development? || Rails.env.test?
|
442
|
-
# Use 'config/application.yml'
|
443
|
-
config.secret_config.use :file
|
444
|
-
else
|
445
|
-
# Read configuration from AWS SSM Parameter Store
|
446
|
-
config.secret_config.use :ssm,
|
447
|
-
path: "/#{Rails.env}/my_app",
|
448
|
-
key_id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
449
|
-
end
|
450
|
-
|
451
|
-
# ....
|
452
|
-
end
|
453
|
-
end
|
454
|
-
~~~
|
455
|
-
|
456
|
-
Note: The relevant KMS key must be created first prior to using it here.
|
457
|
-
|
458
|
-
`ssm` provider supports various configuration parameters that can be provided as keyword arguments for `config.secret_config.use :ssm, path, **args`
|
459
|
-
|
460
|
-
Args hash:
|
461
|
-
* **:key_id** (String) - The `key_id` is only used when writing settings to the AWS Parameter store and can be left off when that instance will only read from the parameter store. Can be configred with environment variable `SECRET_CONFIG_KEY_ID`.
|
462
|
-
* **:retry_count** (Integer, default=10) - Max number of retries in case of execution failure.
|
463
|
-
* **:retry_max_ms** (Integer, default=3_000) - Interval in ms between retries, `sleep` is used to facilitate throttling.
|
464
|
-
* any options suported by [Aws::SSM::Client](https://docs.aws.amazon.com/sdkforruby/api/Aws/SSM/Client.html#initialize-instance_method) e.g. **:credentials**:
|
465
|
-
~~~ruby
|
466
|
-
config.secret_config.use :ssm,
|
467
|
-
path: "/#{Rails.env}/my_app",
|
468
|
-
key_id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
469
|
-
credentials: Aws::AssumeRoleCredentials.new(
|
470
|
-
role_arn: "arn:aws:iam::111111122222222:role/assume_role_name",
|
471
|
-
role_session_name: "session-name-to-identify-#{SecureRandom.uuid}"
|
472
|
-
))
|
473
|
-
~~~
|
474
|
-
|
475
|
-
### Secret Config Environment variables
|
476
|
-
|
477
|
-
Priority describes when environment variable is used as a default value, preceds configuration value or overrides.
|
478
|
-
|
479
|
-
Name | Desctiption | Priority
|
480
|
-
------------------------- | --------------------------------------------------------------- | --------
|
481
|
-
`SECRET_CONFIG_PATH` | path from which the configuration data will be read | precede
|
482
|
-
`SECRET_CONFIG_PROVIDER` | override the provider configured for `config.secret_config.use` | override
|
483
|
-
`SECRET_CONFIG_KEY_ID` | encryption `key_id` | default
|
484
|
-
`SECRET_CONFIG_ACCOUNT_ID`| used in `rspec` to configure AWS Account Id for role assuming | required
|
485
|
-
|
486
|
-
### Shared configuration for development and test
|
487
|
-
|
488
|
-
When running multiple engines or private "gems" inside the same code repository, the development and test
|
489
|
-
configuration file `application.yml` can be shared. Update the lines above to:
|
490
|
-
|
491
|
-
~~~ruby
|
492
|
-
module MyApp
|
493
|
-
class Application < Rails::Application
|
494
|
-
|
495
|
-
# Add the following lines:
|
496
|
-
if Rails.env.development? || Rails.env.test?
|
497
|
-
# Use 'config/application.yml'
|
498
|
-
config.secret_config.use :file
|
499
|
-
else
|
500
|
-
# Read configuration from AWS SSM Parameter Store
|
501
|
-
config.secret_config.use :ssm, path: "/#{Rails.env}/my_app"
|
502
|
-
end
|
503
|
-
|
504
|
-
# ....
|
505
|
-
end
|
506
|
-
end
|
507
|
-
~~~
|
508
|
-
|
509
|
-
Where `file_name` is the full path and filename for where `application.yml` is located.
|
510
|
-
|
511
|
-
### Authorization
|
512
|
-
|
513
|
-
The following policy needs to be added to the IAM Group under which the application will be running:
|
514
|
-
|
515
|
-
~~~json
|
516
|
-
{
|
517
|
-
"Version": "2012-10-17",
|
518
|
-
"Statement": [
|
519
|
-
{
|
520
|
-
"Sid": "VisualEditor0",
|
521
|
-
"Effect": "Allow",
|
522
|
-
"Action": [
|
523
|
-
"ssm:GetParametersByPath",
|
524
|
-
"ssm:PutParameter",
|
525
|
-
"ssm:DeleteParameter",
|
526
|
-
],
|
527
|
-
"Resource": "*"
|
528
|
-
}
|
529
|
-
]
|
530
|
-
}
|
531
|
-
~~~
|
532
|
-
|
533
|
-
The above policy restricts read and write access to just the Parameter Store capabilities of AWS System Manager.
|
534
|
-
|
535
|
-
These additional Actions are not used by Secret Config, but may be useful for anyone using the AWS Console directly
|
536
|
-
to view and modify parameters:
|
537
|
-
- `ssm:DescribeParameters`
|
538
|
-
- `ssm:GetParameterHistory`
|
539
|
-
- `ssm:GetParameters`
|
540
|
-
- `ssm:GetParameter`
|
541
|
-
|
542
|
-
## String Interpolation
|
543
|
-
|
544
|
-
Values supplied for config settings can be replaced inline with date, time, hostname, pid and random values.
|
545
|
-
|
546
|
-
For example to include the `hostname` in the log file name setting:
|
547
|
-
|
548
|
-
~~~yaml
|
549
|
-
development:
|
550
|
-
logger:
|
551
|
-
level: info
|
552
|
-
file_name: /var/log/my_application_%{hostname}.log
|
553
|
-
~~~
|
554
|
-
|
555
|
-
Available interpolations:
|
556
|
-
|
557
|
-
* %{date}
|
558
|
-
* Current date in the format of "%Y%m%d" (CCYYMMDD)
|
559
|
-
* %{date:format}
|
560
|
-
* Current date in the supplied format. See strftime
|
561
|
-
* %{time}
|
562
|
-
* Current date and time down to ms in the format of "%Y%m%d%Y%H%M%S%L" (CCYYMMDDHHMMSSmmm)
|
563
|
-
* %{time:format}
|
564
|
-
* Current date and time in the supplied format. See strftime
|
565
|
-
* %{env:name}
|
566
|
-
* Extract value from the named environment variable.
|
567
|
-
* %{hostname}
|
568
|
-
* Full name of this host.
|
569
|
-
* %{hostname:short}
|
570
|
-
* Short name of this host. Everything up to the first period.
|
571
|
-
* %{pid}
|
572
|
-
* Process Id for this process.
|
573
|
-
* %{random}
|
574
|
-
* URL safe Random 32 byte value.
|
575
|
-
* %{random:size}
|
576
|
-
* URL safe Random value of `size` bytes.
|
577
|
-
|
578
|
-
#### Notes:
|
579
|
-
|
580
|
-
* To prevent interpolation use %%{...}
|
581
|
-
* %% is not touched, only %{...} is searched for.
|
582
|
-
* Since these interpolations are only evaluated at load time and
|
583
|
-
every time the registry is refreshed there is no runtime overhead when keys are fetched.
|
584
|
-
|
585
|
-
## Command Line Interface
|
586
|
-
|
587
|
-
Secret Config has a command line interface for exporting, importing and copying between paths in the registry.
|
588
|
-
|
589
|
-
~~~
|
590
|
-
secret-config [options]
|
591
|
-
-e, --export SOURCE_PATH Export configuration. Use --file to specify the file name, otherwise stdout is used.
|
592
|
-
-i, --import TARGET_PATH Import configuration. Use --file to specify the file name, --path for the SOURCE_PATH, otherwise stdin is used.
|
593
|
-
--file FILE_NAME Import/Export/Diff to/from this file.
|
594
|
-
-p, --path PATH Import/Export/Diff to/from this path.
|
595
|
-
--diff TARGET_PATH Compare configuration to this path. Use --file to specify the source file name, --path for the SOURCE_PATH, otherwise stdin is used.
|
596
|
-
-s, --set KEY=VALUE Set one key to value. Example: --set mysql/database=localhost
|
597
|
-
-f, --fetch KEY Fetch the value for one setting. Example: --fetch mysql/database.
|
598
|
-
-d, --delete KEY Delete one specific key.
|
599
|
-
-r, --delete-tree PATH Recursively delete all keys under the specified path.
|
600
|
-
-c, --console Start interactive console.
|
601
|
-
--provider PROVIDER Provider to use. [ssm | file]. Default: ssm
|
602
|
-
--no-filter For --export only. Do not filter passwords and keys.
|
603
|
-
--interpolate For --export only. Evaluate string interpolation and __import__.
|
604
|
-
--prune For --import only. During import delete all existing keys for which there is no key in the import file. Only works with --import.
|
605
|
-
--force For --import only. Overwrite all values, not just the changed ones. Useful for changing the KMS key.
|
606
|
-
--key_id KEY_ID For --import only. Encrypt config settings with this AWS KMS key id. Default: AWS Default key.
|
607
|
-
--key_alias KEY_ALIAS For --import only. Encrypt config settings with this AWS KMS alias.
|
608
|
-
--random_size INTEGER For --import only. Size to use when generating random values when $(random) is encountered in the source. Default: 32
|
609
|
-
-v, --version Display Secret Config version.
|
610
|
-
-h, --help Prints this help.
|
611
|
-
~~~
|
612
|
-
|
613
|
-
### CLI Examples
|
614
|
-
|
615
|
-
#### Import from a file into SSM parameters
|
616
|
-
|
617
|
-
To get started it is useful to create a YAML file with all the relevant settings and then import
|
618
|
-
it into AWS SSM Parameter store. This file is the same as `applcation.yml` except that each file
|
619
|
-
is just for one environment. I.e. It does not contain the `test` or `development` root level entries.
|
620
|
-
|
621
|
-
For example: `production.yml`
|
622
|
-
|
623
|
-
~~~yaml
|
624
|
-
mysql:
|
625
|
-
database: secret_config_production
|
626
|
-
username: secret_config
|
627
|
-
password: secret_configrules
|
628
|
-
host: mysql_server.example.net
|
629
|
-
|
630
|
-
mongo:
|
631
|
-
database: secret_config_production
|
632
|
-
primary: mongo_primary.example.net:27017
|
633
|
-
secondary: mongo_secondary.example.net:27017
|
634
|
-
|
635
|
-
secrets:
|
636
|
-
secret_key_base: somereallylongproductionstring
|
637
|
-
~~~
|
638
|
-
|
639
|
-
Import a yaml file, into a path in AWS SSM Parameter Store:
|
640
|
-
|
641
|
-
secret-config --import /production/my_application --file production.yml
|
642
|
-
|
643
|
-
Import a yaml file, into a path in AWS SSM Parameter Store, using a custom KMS key to encrypt the values:
|
644
|
-
|
645
|
-
secret-config --import /production/my_application --file production.yml --key_id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
646
|
-
|
647
|
-
Import a yaml file, into a path in AWS SSM Parameter Store, using a custom KMS key alias to encrypt the values:
|
648
|
-
|
649
|
-
secret-config --import /production/my_application --file production.yml --key_alias my_key_alias
|
650
|
-
|
651
|
-
#### Diff
|
652
|
-
|
653
|
-
Before importing a new config file into the AWS SSM Parameter store, a diff can be performed to determine
|
654
|
-
what the differences are that will be applied when the import is run with the `--prune` option.
|
655
|
-
|
656
|
-
secret-config --diff /production/my_application --file production.yml
|
657
|
-
|
658
|
-
Key:
|
659
|
-
|
660
|
-
+ Adding a new key to the registry.
|
661
|
-
- The key will be removed from the registry during the import if --prune is specified.
|
662
|
-
* The value for that key will change during an import.
|
663
|
-
|
664
|
-
#### Export SSM parameters
|
665
|
-
|
666
|
-
In AWS SSM Parameter store it can be difficult to
|
667
|
-
Export the values from a specific path into a yaml or json file so that they are easier to read.
|
668
|
-
|
669
|
-
Export from a path in AWS SSM Parameter Store to a yaml file, where passwords are filtered:
|
670
|
-
|
671
|
-
secret-config --export /production/my_application --file production.yml
|
672
|
-
|
673
|
-
Export from a path in AWS SSM Parameter Store to a yaml file, _without_ filtering out passwords:
|
674
|
-
|
675
|
-
secret-config --export /production/my_application --file production.yml --no-filter
|
676
|
-
|
677
|
-
Export from a path in AWS SSM Parameter Store to a json file, where passwords are filtered:
|
678
|
-
|
679
|
-
secret-config --export /production/my_application --file production.json
|
680
|
-
|
681
|
-
#### Copy values between paths in AWS SSM parameter store
|
682
|
-
|
683
|
-
It can be useful to keep a "master" copy of the values for an environment or stack in a custom path
|
684
|
-
in AWS Parameter Store. Then for each stack or environment that is spun up, copy the "master" / "common" values
|
685
|
-
into the new path. Once copied the values specific to that path can be updated accordingly.
|
686
|
-
|
687
|
-
Import configuration from an existing path in AWS SSM Parameter Store into another:
|
688
|
-
|
689
|
-
secret-config --import /tenant73/my_application --path /production/my_application
|
690
|
-
|
691
|
-
#### Generating random passwords
|
692
|
-
|
693
|
-
In the multi-tenant example above, we may want to generate a secure random password for each tenant.
|
694
|
-
In the source file or registry, set the value to `$random`, this will ensure that during the `import`
|
695
|
-
that the destination will receive a secure random value.
|
696
|
-
|
697
|
-
By default the length of the randomized value is 32 bytes, use `--random_size` to adjust the length of
|
698
|
-
the randomized string.
|
699
|
-
|
700
|
-
## Docker
|
701
|
-
|
702
|
-
Secret Config is at its best when the application is containerized. By externalizing the configuration the same
|
703
|
-
docker container can be tested in one or more environments and then deployed directly to production without
|
704
|
-
any changes. The only difference being the path that container uses to read its configuration from.
|
705
|
-
|
706
|
-
Another important benefit is that the docker image does not contain any production or test credentials since
|
707
|
-
these are all stored in AWS SSM Parameter Store.
|
708
|
-
|
709
|
-
When a Ruby / Rails application is using Secret Config for its configuration settings, it only requires the
|
710
|
-
following environment variables when starting up the container in for example AWS ECS or AWS Fargate:
|
711
|
-
|
712
|
-
~~~shell
|
713
|
-
export SECRET_CONFIG_PATH=/production/my_application
|
714
|
-
~~~
|
715
|
-
|
716
|
-
For rails applications, typically the `RAILS_ENV` is also needed, but not required for Secret Config.
|
717
|
-
|
718
|
-
~~~shell
|
719
|
-
export RAILS_ENV=production
|
720
|
-
~~~
|
721
|
-
|
722
|
-
### Logging
|
723
|
-
|
724
|
-
When using Semantic Logger, the following code could be added to `application.rb` to facilitate configuration
|
725
|
-
of the logging output via Secret Config:
|
726
|
-
|
727
|
-
~~~ruby
|
728
|
-
# Logging
|
729
|
-
config.log_level = config.secret_config.fetch("logger/level", default: :info, type: :symbol)
|
730
|
-
config.semantic_logger.backtrace_level = config.secret_config.fetch("logger/backtrace_level", default: :error, type: :symbol)
|
731
|
-
config.semantic_logger.application = config.secret_config.fetch("logger/application", default: "my_app")
|
732
|
-
config.semantic_logger.environment = config.secret_config.fetch("logger/environment", default: Rails.env)
|
733
|
-
~~~
|
734
|
-
|
735
|
-
In any environment the log level can be changed, for example set `logger/level` to `debug`. And it can be changed
|
736
|
-
in the AWS SSM Parameter Store, or directly with the environment variable `export LOGGER_LEVEL=debug`
|
737
|
-
|
738
|
-
`logger/environment` can be used to identify which tenant the log messages are emanating from. By default it is just
|
739
|
-
the rails environment. For example set `logger/environment` to `tenant73`.
|
740
|
-
|
741
|
-
Additionally the following code can be used with containers to send log output to standard out:
|
742
|
-
|
743
|
-
~~~ruby
|
744
|
-
destination = config.secret_config.fetch("logger/destination", default: :file, type: :symbol)
|
745
|
-
if destination == :stdout
|
746
|
-
STDOUT.sync = true
|
747
|
-
config.rails_semantic_logger.add_file_appender = false
|
748
|
-
config.semantic_logger.add_appender(
|
749
|
-
io: STDOUT,
|
750
|
-
level: config.log_level,
|
751
|
-
formatter: config.secret_config.fetch("logger/formatter", default: :default, type: :symbol)
|
752
|
-
)
|
753
|
-
end
|
754
|
-
~~~
|
755
|
-
|
756
|
-
Specifically for docker containers it is necessary to turn off file logging and turn on logging to standard out
|
757
|
-
so that AWS Cloud Watch can pick up the log data.
|
758
|
-
|
759
|
-
To start with `logger/destination` of `stdout` will work with regular non-colorized output. When feeding the
|
760
|
-
log output into something that can process JSON, set `logger/formatter` to `json`.
|
761
|
-
|
762
|
-
The benefit with the above approach is that a developer can pull the exact same container image that is running
|
763
|
-
in production and configure it to run locally on their laptop. For example, set `logger/destination` to `file`.
|
764
|
-
|
765
|
-
The above code can be modified as necessary to add any Semantic Logger appender to write directly to external
|
766
|
-
centralized logging systems, instead of writing to standard out or local files.
|
767
|
-
|
768
|
-
### Email Server and Assets
|
769
|
-
|
770
|
-
An example of how to setup the email server and the assets for html emails. Add to `application.rb`:
|
771
|
-
|
772
|
-
~~~ruby
|
773
|
-
# Emails
|
774
|
-
application_url = config.secret_config.fetch("emails/asset_host")
|
775
|
-
uri = URI.parse(application_url)
|
776
|
-
|
777
|
-
config.action_mailer.default_url_options = {host: uri.host, protocol: uri.scheme}
|
778
|
-
config.action_mailer.asset_host = application_url
|
779
|
-
config.action_mailer.smtp_settings = {address: config.secret_config.fetch("emails/smtp/address", default: "localhost")}
|
780
|
-
config.action_mailer.raise_delivery_errors = config.secret_config.fetch("emails/raise_delivery_errors", default: true, type: :boolean)
|
781
|
-
~~~
|
782
|
-
|
783
|
-
### Symmetric Encryption
|
784
|
-
|
785
|
-
An example of how to setup Symmetric Encryption. Add to `application.rb`:
|
786
|
-
|
787
|
-
~~~ruby
|
788
|
-
# Encryption
|
789
|
-
config.symmetric_encryption.cipher =
|
790
|
-
SymmetricEncryption::Cipher.new(
|
791
|
-
key: config.secret_config.fetch('symmetric_encryption/key', encoding: :base64),
|
792
|
-
iv: config.secret_config.fetch('symmetric_encryption/iv', encoding: :base64),
|
793
|
-
version: config.secret_config.fetch('symmetric_encryption/version', type: :integer),
|
794
|
-
)
|
795
|
-
|
796
|
-
# Also support one prior encryption key version during key rotation
|
797
|
-
if config.secret_config.key?('symmetric_encryption/old/key')
|
798
|
-
SymmetricEncryption.secondary_ciphers = [
|
799
|
-
SymmetricEncryption::Cipher.new(
|
800
|
-
key: config.secret_config.fetch('symmetric_encryption/old/key', encoding: :base64),
|
801
|
-
iv: config.secret_config.fetch('symmetric_encryption/old/iv', encoding: :base64),
|
802
|
-
version: config.secret_config.fetch('symmetric_encryption/old/version', type: :integer),
|
803
|
-
),
|
804
|
-
]
|
805
|
-
end
|
806
|
-
~~~
|
807
|
-
|
808
|
-
Using this approach the file `config/symmetric-encryption.yml` can be removed once the keys have been moved to
|
809
|
-
the registry.
|
810
|
-
|
811
|
-
To extract existing keys from the config file so that they can be imported into the registry,
|
812
|
-
run the code below inside a console in each of the respective environments.
|
813
|
-
|
814
|
-
~~~ruby
|
815
|
-
require "yaml"
|
816
|
-
require "base64"
|
817
|
-
|
818
|
-
def se_config(cipher)
|
819
|
-
{
|
820
|
-
"key" => Base64.strict_encode64(cipher.send(:key)),
|
821
|
-
"iv" => Base64.strict_encode64(cipher.iv),
|
822
|
-
"version" => cipher.version
|
823
|
-
}
|
824
|
-
end
|
26
|
+
## v0.9 Upgrade Notes
|
825
27
|
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
~~~
|
28
|
+
Note that the command line program name has changed from `secret_config` to `secret-config`.
|
29
|
+
Be careful that the arguments have also changed. The arguments are now consistent across operations.
|
30
|
+
The command line examples below have also been updated to reflect the changes.
|
31
|
+
|
32
|
+
Please run `secret-config --help` to see the new arguments and updated operations.
|
832
33
|
|
833
34
|
## Versioning
|
834
35
|
|
835
|
-
This project adheres to [Semantic Versioning](
|
36
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
836
37
|
|
837
38
|
## Author
|
838
39
|
|
@@ -840,7 +41,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
840
41
|
|
841
42
|
## License
|
842
43
|
|
843
|
-
Copyright
|
44
|
+
Copyright 2020 Reid Morrison
|
844
45
|
|
845
46
|
Licensed under the Apache License, Version 2.0 (the "License");
|
846
47
|
you may not use this file except in compliance with the License.
|
data/lib/secret_config.rb
CHANGED
data/lib/secret_config/cli.rb
CHANGED
@@ -210,7 +210,13 @@ module SecretConfig
|
|
210
210
|
begin
|
211
211
|
case provider
|
212
212
|
when :ssm
|
213
|
-
|
213
|
+
if key_alias
|
214
|
+
Providers::Ssm.new(key_alias: key_alias)
|
215
|
+
elsif key_id
|
216
|
+
Providers::Ssm.new(key_id: key_id)
|
217
|
+
else
|
218
|
+
Providers::Ssm.new
|
219
|
+
end
|
214
220
|
else
|
215
221
|
raise ArgumentError, "Invalid provider: #{provider}"
|
216
222
|
end
|
data/lib/secret_config/parser.rb
CHANGED
@@ -15,7 +15,7 @@ module SecretConfig
|
|
15
15
|
# Keys are returned with path names relative to the supplied path.
|
16
16
|
def parse(key, value)
|
17
17
|
relative_key = relative_key?(key) ? key : key.sub("#{path}/", "")
|
18
|
-
value = interpolator.parse(value) if interpolator && value.is_a?(String) && value.include?("
|
18
|
+
value = interpolator.parse(value) if interpolator && value.is_a?(String) && value.include?("${")
|
19
19
|
tree[relative_key] = value
|
20
20
|
end
|
21
21
|
|
@@ -4,16 +4,16 @@ require "securerandom"
|
|
4
4
|
# * SecretConfig Interpolations
|
5
5
|
#
|
6
6
|
# Expanding values inline for date, time, hostname, pid and random values.
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
7
|
+
# ${date} # Current date in the format of "%Y%m%d" (CCYYMMDD)
|
8
|
+
# ${date:format} # Current date in the supplied format. See strftime
|
9
|
+
# ${time} # Current date and time down to ms in the format of "%Y%m%d%Y%H%M%S%L" (CCYYMMDDHHMMSSmmm)
|
10
|
+
# ${time:format} # Current date and time in the supplied format. See strftime
|
11
|
+
# ${env:name} # Extract value from the named environment value.
|
12
|
+
# ${hostname} # Full name of this host.
|
13
|
+
# ${hostname:short} # Short name of this host. Everything up to the first period.
|
14
|
+
# ${pid} # Process Id for this process.
|
15
|
+
# ${random} # URL safe Random 32 byte value.
|
16
|
+
# ${random:size} # URL safe Random value of `size` bytes.
|
17
17
|
module SecretConfig
|
18
18
|
class SettingInterpolator < StringInterpolator
|
19
19
|
def date(format = "%Y%m%d")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Parse strings containing
|
1
|
+
# Parse strings containing ${key:value1,value2,value3}
|
2
2
|
# Where `key` is a method implemented by a class inheriting from this class
|
3
3
|
#
|
4
4
|
# The following `key`s are reserved:
|
@@ -6,20 +6,20 @@
|
|
6
6
|
# * initialize
|
7
7
|
#
|
8
8
|
# Notes:
|
9
|
-
# * To prevent interpolation use
|
10
|
-
# *
|
9
|
+
# * To prevent interpolation use $${...}
|
10
|
+
# * $$ is not touched, only ${...} is identified.
|
11
11
|
module SecretConfig
|
12
12
|
class StringInterpolator
|
13
13
|
def initialize(pattern = nil)
|
14
|
-
@pattern = pattern ||
|
14
|
+
@pattern = pattern || /\${1,2}\{([^}]+)\}/
|
15
15
|
end
|
16
16
|
|
17
17
|
def parse(string)
|
18
|
-
string.gsub(
|
19
|
-
if match.start_with?("
|
18
|
+
string.gsub(/\${1,2}\{([^}]+)\}/) do |match|
|
19
|
+
if match.start_with?("$$")
|
20
20
|
match[1..-1]
|
21
21
|
else
|
22
|
-
expr = Regexp.last_match(1) || Regexp.last_match(2) || match.tr("
|
22
|
+
expr = Regexp.last_match(1) || Regexp.last_match(2) || match.tr("${}", "")
|
23
23
|
key, args_str = expr.split(":")
|
24
24
|
key = key.strip.to_sym
|
25
25
|
arguments = args_str&.split(",")&.map { |v| v.strip == "" ? nil : v.strip } || []
|
data/test/config/application.yml
CHANGED
@@ -45,7 +45,7 @@ test:
|
|
45
45
|
mongo:
|
46
46
|
database: secret_config_test
|
47
47
|
primary: 127.0.0.1:27017
|
48
|
-
secondary: "
|
48
|
+
secondary: "${hostname}:27018"
|
49
49
|
|
50
50
|
secrets:
|
51
51
|
secret_key_base: somereallylongteststring
|
@@ -59,15 +59,15 @@ test:
|
|
59
59
|
key: key0
|
60
60
|
|
61
61
|
mysql:
|
62
|
-
# database: "
|
62
|
+
# database: "${fetch: /test/my_application/mysql/database }"
|
63
63
|
username: other
|
64
64
|
password: otherrules
|
65
|
-
host: "
|
65
|
+
host: "${hostname}"
|
66
66
|
|
67
67
|
mongo:
|
68
68
|
database: secret_config_test
|
69
69
|
primary: localhost:27017
|
70
|
-
secondary: "
|
70
|
+
secondary: "${hostname}:27018"
|
71
71
|
|
72
72
|
mongo2:
|
73
73
|
__import__: mongo
|
data/test/parser_test.rb
CHANGED
@@ -26,7 +26,7 @@ class ParserTest < Minitest::Test
|
|
26
26
|
#
|
27
27
|
# Retrieve values elsewhere in the registry.
|
28
28
|
# Paths can be relative to the current root, or absolute paths outside the current root.
|
29
|
-
#
|
29
|
+
# ${fetch:key} # Fetches a single value from a relative or absolute path
|
30
30
|
# Return the value of the supplied key.
|
31
31
|
#
|
32
32
|
# With a relative key, look for the value in the current registry.
|
@@ -47,7 +47,7 @@ class ParserTest < Minitest::Test
|
|
47
47
|
# end
|
48
48
|
# end
|
49
49
|
|
50
|
-
#
|
50
|
+
# ${import:path} # Imports a path of keys and values into the current path
|
51
51
|
# Replace the current value with a tree of values with the supplied path.
|
52
52
|
#
|
53
53
|
describe "#import" do
|
data/test/providers/file_test.rb
CHANGED
@@ -15,7 +15,7 @@ module Providers
|
|
15
15
|
{
|
16
16
|
"/test/my_application/mongo/database" => "secret_config_test",
|
17
17
|
"/test/my_application/mongo/primary" => "127.0.0.1:27017",
|
18
|
-
"/test/my_application/mongo/secondary" => "
|
18
|
+
"/test/my_application/mongo/secondary" => "${hostname}:27018",
|
19
19
|
"/test/my_application/mysql/database" => "secret_config_test",
|
20
20
|
"/test/my_application/mysql/password" => "secret_configrules",
|
21
21
|
"/test/my_application/mysql/username" => "secret_config",
|
data/test/providers/ssm_test.rb
CHANGED
@@ -15,7 +15,7 @@ module Providers
|
|
15
15
|
{
|
16
16
|
"/test/my_application/mongo/database" => "secret_config_test",
|
17
17
|
"/test/my_application/mongo/primary" => "127.0.0.1:27017",
|
18
|
-
"/test/my_application/mongo/secondary" => "
|
18
|
+
"/test/my_application/mongo/secondary" => "${hostname}:27018",
|
19
19
|
"/test/my_application/mysql/database" => "secret_config_test",
|
20
20
|
"/test/my_application/mysql/password" => "secret_configrules",
|
21
21
|
"/test/my_application/mysql/username" => "secret_config",
|
@@ -6,23 +6,23 @@ module SecretConfig
|
|
6
6
|
|
7
7
|
describe "#parse" do
|
8
8
|
it "handles good key" do
|
9
|
-
string = "Set a date of
|
10
|
-
expected = string.gsub("
|
9
|
+
string = "Set a date of ${date} here."
|
10
|
+
expected = string.gsub("${date}", Date.today.strftime("%Y%m%d"))
|
11
11
|
actual = interpolator.parse(string)
|
12
12
|
assert_equal expected, actual, string
|
13
13
|
end
|
14
14
|
|
15
15
|
it "handles multiple keys" do
|
16
|
-
string = "
|
17
|
-
expected = string.gsub("
|
18
|
-
expected = expected.gsub("
|
19
|
-
expected = expected.gsub("
|
16
|
+
string = "${pid}: Set a date of ${date} here and a ${time:%H%M} here and for luck ${pid}"
|
17
|
+
expected = string.gsub("${date}", Date.today.strftime("%Y%m%d"))
|
18
|
+
expected = expected.gsub("${time:%H%M}", Time.now.strftime("%H%M"))
|
19
|
+
expected = expected.gsub("${pid}", $$.to_s)
|
20
20
|
actual = interpolator.parse(string)
|
21
21
|
assert_equal expected, actual, string
|
22
22
|
end
|
23
23
|
|
24
24
|
it "handles bad key" do
|
25
|
-
string = "Set a date of
|
25
|
+
string = "Set a date of ${blah} here."
|
26
26
|
assert_raises InvalidInterpolation do
|
27
27
|
interpolator.parse(string)
|
28
28
|
end
|
@@ -31,22 +31,22 @@ module SecretConfig
|
|
31
31
|
|
32
32
|
describe "#date" do
|
33
33
|
it "interpolates date only" do
|
34
|
-
string = "
|
34
|
+
string = "${date}"
|
35
35
|
expected = Date.today.strftime("%Y%m%d")
|
36
36
|
actual = interpolator.parse(string)
|
37
37
|
assert_equal expected, actual, string
|
38
38
|
end
|
39
39
|
|
40
40
|
it "interpolates date" do
|
41
|
-
string = "Set a date of
|
42
|
-
expected = string.gsub("
|
41
|
+
string = "Set a date of ${date} here."
|
42
|
+
expected = string.gsub("${date}", Date.today.strftime("%Y%m%d"))
|
43
43
|
actual = interpolator.parse(string)
|
44
44
|
assert_equal expected, actual, string
|
45
45
|
end
|
46
46
|
|
47
47
|
it "interpolates date with custom format" do
|
48
|
-
string = "Set a custom
|
49
|
-
expected = string.gsub("
|
48
|
+
string = "Set a custom ${date:%m%d%Y} here."
|
49
|
+
expected = string.gsub("${date:%m%d%Y}", Date.today.strftime("%m%d%Y"))
|
50
50
|
actual = interpolator.parse(string)
|
51
51
|
assert_equal expected, actual, string
|
52
52
|
end
|
@@ -54,7 +54,7 @@ module SecretConfig
|
|
54
54
|
|
55
55
|
describe "#time" do
|
56
56
|
it "interpolates time only" do
|
57
|
-
string = "
|
57
|
+
string = "${time}"
|
58
58
|
time = Time.now
|
59
59
|
Time.stub(:now, time) do
|
60
60
|
expected = Time.now.strftime("%Y%m%d%H%M%S%L")
|
@@ -64,18 +64,18 @@ module SecretConfig
|
|
64
64
|
end
|
65
65
|
|
66
66
|
it "interpolates time" do
|
67
|
-
string = "Set a time of
|
67
|
+
string = "Set a time of ${time} here."
|
68
68
|
time = Time.now
|
69
69
|
Time.stub(:now, time) do
|
70
|
-
expected = string.gsub("
|
70
|
+
expected = string.gsub("${time}", Time.now.strftime("%Y%m%d%H%M%S%L"))
|
71
71
|
actual = interpolator.parse(string)
|
72
72
|
assert_equal expected, actual, string
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
it "interpolates time with custom format" do
|
77
|
-
string = "Set a custom time of
|
78
|
-
expected = string.gsub("
|
77
|
+
string = "Set a custom time of ${time:%H%M} here."
|
78
|
+
expected = string.gsub("${time:%H%M}", Time.now.strftime("%H%M"))
|
79
79
|
actual = interpolator.parse(string)
|
80
80
|
assert_equal expected, actual, string
|
81
81
|
end
|
@@ -87,20 +87,20 @@ module SecretConfig
|
|
87
87
|
end
|
88
88
|
|
89
89
|
it "fetches existing ENV var" do
|
90
|
-
string = "
|
90
|
+
string = "${env:TEST_SETTING}"
|
91
91
|
actual = interpolator.parse(string)
|
92
92
|
assert_equal "Secret", actual, string
|
93
93
|
end
|
94
94
|
|
95
95
|
it "fetches existing ENV var into a larger string" do
|
96
|
-
string = "Hello
|
96
|
+
string = "Hello ${env:TEST_SETTING}. How are you?"
|
97
97
|
actual = interpolator.parse(string)
|
98
|
-
expected = string.gsub("
|
98
|
+
expected = string.gsub("${env:TEST_SETTING}", "Secret")
|
99
99
|
assert_equal expected, actual, string
|
100
100
|
end
|
101
101
|
|
102
102
|
it "handles missing ENV var" do
|
103
|
-
string = "
|
103
|
+
string = "${env:OTHER_TEST_SETTING}"
|
104
104
|
actual = interpolator.parse(string)
|
105
105
|
assert_equal "", actual, string
|
106
106
|
end
|
@@ -108,13 +108,13 @@ module SecretConfig
|
|
108
108
|
|
109
109
|
describe "#hostname" do
|
110
110
|
it "returns hostname" do
|
111
|
-
string = "
|
111
|
+
string = "${hostname}"
|
112
112
|
actual = interpolator.parse(string)
|
113
113
|
assert_equal Socket.gethostname, actual, string
|
114
114
|
end
|
115
115
|
|
116
116
|
it "returns short hostname" do
|
117
|
-
string = "
|
117
|
+
string = "${hostname:short}"
|
118
118
|
actual = interpolator.parse(string)
|
119
119
|
assert_equal Socket.gethostname.split(".")[0], actual, string
|
120
120
|
end
|
@@ -122,7 +122,7 @@ module SecretConfig
|
|
122
122
|
|
123
123
|
describe "#pid" do
|
124
124
|
it "returns process id" do
|
125
|
-
string = "
|
125
|
+
string = "${pid}"
|
126
126
|
actual = interpolator.parse(string)
|
127
127
|
assert_equal $$.to_s, actual, string
|
128
128
|
end
|
@@ -130,7 +130,7 @@ module SecretConfig
|
|
130
130
|
|
131
131
|
describe "#random" do
|
132
132
|
it "interpolates random 32 byte string" do
|
133
|
-
string = "
|
133
|
+
string = "${random}"
|
134
134
|
random = SecureRandom.urlsafe_base64(32)
|
135
135
|
SecureRandom.stub(:urlsafe_base64, random) do
|
136
136
|
actual = interpolator.parse(string)
|
@@ -139,7 +139,7 @@ module SecretConfig
|
|
139
139
|
end
|
140
140
|
|
141
141
|
it "interpolates custom length random string" do
|
142
|
-
string = "
|
142
|
+
string = "${random:64}"
|
143
143
|
random = SecureRandom.urlsafe_base64(64)
|
144
144
|
SecureRandom.stub(:urlsafe_base64, random) do
|
145
145
|
actual = interpolator.parse(string)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secret_config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|