ravioli 0.1.3 → 0.1.4
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 +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:
|