ravioli 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -29
- data/lib/ravioli/builder.rb +55 -25
- data/lib/ravioli/staging_inquirer.rb +26 -0
- data/lib/ravioli/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: 8f4bb7ba56c32614b3b2be61de6c94648e3ff2fbb54692f84c56bbacdbb1db4e
|
4
|
+
data.tar.gz: bafd93a46a25071127c09c19231af5545749ea18b63eaa94d7ac2e67e2642fdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b59aa2b29243d15c43a0262442576eeedb7c2b5d651341e184b771f277c598c62844dfc0b9f3d234ce9206a80ecd62286d352f31a4ea08db6439f64c5ccac8
|
7
|
+
data.tar.gz: a691095131f8babd604bf1a6c5a6aaacf4b137c46dc9adc1cd824ffabe2b2c1f75657e8b082defee4b911c5a37695d8955184ffe5d6c2f593cadc336ffdc92eb
|
data/README.md
CHANGED
@@ -216,7 +216,7 @@ Ravioli will then check for [encrypted credentials](https://guides.rubyonrails.o
|
|
216
216
|
2. Then, it loads and applies `config/credentials/RAILS_ENV.yml.enc` over top of what it has already loaded
|
217
217
|
3. Finally, IF `Rails.config.staging?` IS TRUE, it loads and applies `config/credentials/staging.yml.enc`
|
218
218
|
|
219
|
-
This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of
|
219
|
+
This allows you to use your secure credentials stores without duplicating information; you can simply layer environment-specific values over top of a "root" `config/credentials.yml.enc` file.
|
220
220
|
|
221
221
|
### All put together, it does this:
|
222
222
|
|
@@ -417,47 +417,47 @@ staging:
|
|
417
417
|
|
418
418
|
### Encryption keys in ENV
|
419
419
|
|
420
|
-
|
420
|
+
Here are a few facts about credentials in Rails and how they're deployed:
|
421
421
|
|
422
|
-
|
422
|
+
1. Rails assumes you want to use the file that matches your environment, if it exists (e.g. `RAILS_ENV=production` will look for `config/credentials/production.yml.enc`)
|
423
|
+
2. Rails does _not_ support environment-specfic keys, but it _does_ now aggressively loads credentials at boot time.
|
423
424
|
|
424
|
-
`
|
425
|
+
**This means `RAILS_MASTER_KEY` MUST be the decryption key for your environment-specific credential file, if one exists.**
|
425
426
|
|
426
|
-
|
427
|
+
But, because Ravioli merges environment-specific credentials over top of the root credentials file, you'll need to provide encryption keys for two (or, if you have a staging setup, three) different files in ENV vars. As such, Ravioli looks for decryption keys in a way that mirrors Rails' assumptions, but allows progressive layering of credentials.
|
427
428
|
|
428
|
-
|
429
|
+
Here are a few examples
|
429
430
|
|
430
|
-
</
|
431
|
+
<table><thead><tr><th>File</th><th>First it tries...</th><th>Then it tries...</th></tr></thead><tbody>
|
431
432
|
|
432
|
-
|
433
|
+
<tr>
|
434
|
+
<td><code>config/credentials.yml.enc</code></td>
|
435
|
+
<td><code>ENV["RAILS_MASTER_KEY"]</code></td>
|
436
|
+
<td><code>ENV["RAILS_ROOT_KEY"]</code></td>
|
437
|
+
</tr>
|
433
438
|
|
434
|
-
|
439
|
+
<tr>
|
440
|
+
<td><code>config/credentials/#{RAILS_ENV}.yml.enc</code></td>
|
441
|
+
<td><code>ENV["RAILS_MASTER_KEY"]</code></td>
|
442
|
+
<td><code>ENV["RAILS_#{RAILS_ENV}_KEY"]</code></td>
|
443
|
+
</tr>
|
435
444
|
|
436
|
-
`config/credentials/production.yml.enc`
|
437
445
|
|
438
|
-
|
446
|
+
<tr>
|
447
|
+
<td><code>config/credentials/staging.yml.enc</code></td>
|
448
|
+
<td><code>ENV["RAILS_MASTER_KEY"]</code></td>
|
449
|
+
<td><code>ENV["RAILS_STAGING_KEY"]</code></td>
|
450
|
+
</tr>
|
439
451
|
|
440
|
-
|
452
|
+
</tbody></table>
|
441
453
|
|
442
|
-
|
443
|
-
|
444
|
-
`ENV["RAILS_MASTER_KEY"]`
|
445
|
-
|
446
|
-
</td></tr><tr><td>
|
447
|
-
|
448
|
-
`config/credentials/staging.yml.enc` (only if running on staging)
|
449
|
-
|
450
|
-
</td><td>
|
451
|
-
|
452
|
-
`ENV["RAILS_STAGING_KEY"]`
|
453
|
-
|
454
|
-
</td><td>
|
455
|
-
|
456
|
-
`ENV["RAILS_MASTER_KEY"]`
|
454
|
+
Credentials are loaded in that order, too, so that you can have a base setup on `config/credentials.yml.enc`, overlay that with production-specific stuff from `config/credentials/production.yml.enc`, and then short-circuit or redirect some stuff in `config/credentials/staging.yml.enc` for staging environments.
|
457
455
|
|
458
|
-
|
456
|
+
#### TLDR:
|
459
457
|
|
460
|
-
|
458
|
+
1. Set `RAILS_MASTER_KEY` to the key for your specific environment
|
459
|
+
2. Set `RAILS_STAGING_KEY` to the key for your staging credentials (if deploying to staging AND you have staging-specific credentials)
|
460
|
+
3. Set `RAILS_ROOT_KEY` to the key for your root credentials (if you have anything in `config/credentials.yml.enc`)
|
461
461
|
|
462
462
|
## License
|
463
463
|
|
data/lib/ravioli/builder.rb
CHANGED
@@ -79,17 +79,10 @@ module Ravioli
|
|
79
79
|
#
|
80
80
|
# @param is_staging [boolean, #present?] whether or not the current environment is considered a staging environment
|
81
81
|
def add_staging_flag!(is_staging = Rails.env.production? && ENV["STAGING"].present?)
|
82
|
+
require_relative "./staging_inquirer"
|
82
83
|
is_staging = is_staging.present?
|
83
84
|
configuration.staging = is_staging
|
84
|
-
Rails.env.
|
85
|
-
def staging?
|
86
|
-
config = Rails.try(:config)
|
87
|
-
return false unless config&.is_a?(Ravioli::Configuration)
|
88
|
-
|
89
|
-
config.staging?
|
90
|
-
end
|
91
|
-
EOC
|
92
|
-
is_staging
|
85
|
+
Rails.env.class.prepend Ravioli::StagingInquirer
|
93
86
|
end
|
94
87
|
|
95
88
|
# Iterates through the config directory (including nested folders) and
|
@@ -104,14 +97,28 @@ module Ravioli
|
|
104
97
|
|
105
98
|
# Loads Rails encrypted credentials that it can. Checks for corresponding private key files, or ENV vars based on the {Ravioli::Builder credentials preadmlogic}
|
106
99
|
def auto_load_credentials!
|
107
|
-
# Load the
|
108
|
-
load_credentials(
|
109
|
-
|
110
|
-
|
111
|
-
|
100
|
+
# Load the root config (supports using the master key or `RAILS_ROOT_KEY`)
|
101
|
+
load_credentials(
|
102
|
+
key_path: "config/master.key",
|
103
|
+
env_names: %w[master root],
|
104
|
+
)
|
105
|
+
|
106
|
+
# Load any environment-specific configuration on top of it. Since Rails will try
|
107
|
+
# `RAILS_MASTER_KEY` from the environment, we assume the same
|
108
|
+
load_credentials(
|
109
|
+
"config/credentials/#{Rails.env}",
|
110
|
+
key_path: "config/credentials/#{Rails.env}.key",
|
111
|
+
env_names: ["master"],
|
112
|
+
)
|
112
113
|
|
113
114
|
# Apply staging configuration on top of THAT, if need be
|
114
|
-
|
115
|
+
if configuration.staging?
|
116
|
+
load_credentials(
|
117
|
+
"config/credentials/staging",
|
118
|
+
env_names: %w[staging master],
|
119
|
+
key_path: "config/credentials/staging.key",
|
120
|
+
)
|
121
|
+
end
|
115
122
|
end
|
116
123
|
|
117
124
|
# When the builder is done working, lock the configuration and return it
|
@@ -140,11 +147,30 @@ module Ravioli
|
|
140
147
|
end
|
141
148
|
|
142
149
|
# Load secure credentials using a key either from a file or the ENV
|
143
|
-
def load_credentials(path = "credentials", key_path: path,
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
150
|
+
def load_credentials(path = "credentials", key_path: path, env_names: path.split("/").last)
|
151
|
+
error = nil
|
152
|
+
env_names = Array(env_names).map { |env_name| parse_env_name(env_name) }
|
153
|
+
env_names.each do |env_name|
|
154
|
+
credentials = parse_credentials(path, env_name: env_name, key_path: key_path)
|
155
|
+
if credentials.present?
|
156
|
+
configuration.append(credentials)
|
157
|
+
return credentials
|
158
|
+
end
|
159
|
+
rescue => e
|
160
|
+
error = e
|
161
|
+
end
|
162
|
+
|
163
|
+
if error
|
164
|
+
attempted_names = ["key file `#{key_path}'"]
|
165
|
+
attempted_names.push(*env_names.map { |env_name| "`ENV[\"#{env_name}\"]'" })
|
166
|
+
attempted_names = attempted_names.to_sentence(two_words_connector: " or ", last_word_connector: ", or ")
|
167
|
+
warn(
|
168
|
+
"Could not decrypt `#{path}.yml.enc' with #{attempted_names}",
|
169
|
+
error,
|
170
|
+
critical: false,
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
148
174
|
{}
|
149
175
|
end
|
150
176
|
|
@@ -233,13 +259,17 @@ module Ravioli
|
|
233
259
|
{name => config}
|
234
260
|
end
|
235
261
|
|
262
|
+
def parse_env_name(env_name)
|
263
|
+
env_name = env_name.to_s
|
264
|
+
env_name.match?(/^RAILS_/) ? env_name : "RAILS_#{env_name.upcase}_KEY"
|
265
|
+
end
|
266
|
+
|
236
267
|
def parse_credentials(path, key_path: path, env_name: path.split("/").last)
|
237
268
|
@current_credentials = path
|
238
|
-
env_name = env_name
|
239
|
-
env_name = "RAILS_#{env_name.upcase}_KEY" unless env_name.upcase == env_name
|
269
|
+
env_name = parse_env_name(env_name)
|
240
270
|
key_path = path_to_config_file_path(key_path, extnames: "key", quiet: true)
|
241
|
-
options = {key_path: key_path}
|
242
|
-
options[:env_key] = ENV[env_name].present? ? env_name : SecureRandom.hex(6)
|
271
|
+
options = {key_path: key_path.to_s}
|
272
|
+
options[:env_key] = ENV[env_name].present? ? env_name : "__RAVIOLI__#{SecureRandom.hex(6)}"
|
243
273
|
|
244
274
|
path = path_to_config_file_path(path, extnames: "yml.enc")
|
245
275
|
(Rails.application.encrypted(path, **options)&.config || {}).tap do
|
@@ -288,7 +318,7 @@ module Ravioli
|
|
288
318
|
if @strict && critical
|
289
319
|
raise BuildError.new(message, error)
|
290
320
|
else
|
291
|
-
Rails.logger.warn
|
321
|
+
Rails.logger.try(:warn, message) if defined? Rails
|
292
322
|
$stderr.write message # rubocop:disable Rails/Output
|
293
323
|
end
|
294
324
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ravioli
|
4
|
+
# A module that we mix in to the `Rails.env` inquirer class to add some extra staging-related
|
5
|
+
# metadata
|
6
|
+
module StagingInquirer
|
7
|
+
# Add a `name` method to `Rails.env` that will return "staging" for staging environments, and
|
8
|
+
# otherwise the string's value
|
9
|
+
def name
|
10
|
+
staging? ? "staging" : to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
# Add a `strict:` keyword to reduce `Rails.env.production && !Rails.env.staging` calls
|
14
|
+
def production?(strict: false)
|
15
|
+
is_production = super()
|
16
|
+
return is_production unless strict && is_production
|
17
|
+
|
18
|
+
is_production && !staging?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Override staging inquiries to check against the current configuration
|
22
|
+
def staging?
|
23
|
+
Rails.try(:config)&.staging?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/ravioli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ravioli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flip Sasser
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -271,6 +271,7 @@ files:
|
|
271
271
|
- lib/ravioli/builder.rb
|
272
272
|
- lib/ravioli/configuration.rb
|
273
273
|
- lib/ravioli/engine.rb
|
274
|
+
- lib/ravioli/staging_inquirer.rb
|
274
275
|
- lib/ravioli/version.rb
|
275
276
|
homepage: https://github.com/flipsasser/ravioli
|
276
277
|
licenses:
|