Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rails credentials #34

Merged
merged 6 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.0
3.2.2
131 changes: 83 additions & 48 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,89 +1,124 @@
PATH
remote: .
specs:
eyaml (0.3.0)
eyaml (0.4.0)
rbnacl (~> 7.1)
thor (~> 1.1)

GEM
remote: https://rubygems.org/
specs:
actionpack (6.1.7.3)
actionview (= 6.1.7.3)
activesupport (= 6.1.7.3)
rack (~> 2.0, >= 2.0.9)
actionpack (7.1.3)
actionview (= 7.1.3)
activesupport (= 7.1.3)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (6.1.7.3)
activesupport (= 6.1.7.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
actionview (7.1.3)
activesupport (= 7.1.3)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activesupport (6.1.7.3)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activesupport (7.1.3)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
base64 (0.2.0)
bigdecimal (3.1.6)
builder (3.2.4)
coderay (1.1.3)
concurrent-ruby (1.1.9)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crass (1.0.6)
diff-lcs (1.5.0)
erubi (1.10.0)
drb (2.2.0)
ruby2_keywords
erubi (1.12.0)
fakefs (1.8.0)
ffi (1.15.5)
i18n (1.8.11)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
loofah (2.19.1)
io-console (0.7.2)
irb (1.11.1)
rdoc
reline (>= 0.4.2)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
nokogiri (>= 1.12.0)
method_source (1.0.0)
minitest (5.15.0)
nokogiri (1.12.5-arm64-darwin)
minitest (5.21.2)
mutex_m (0.2.0)
nokogiri (1.16.0-arm64-darwin)
racc (~> 1.4)
nokogiri (1.12.5-x86_64-linux)
nokogiri (1.16.0-x86_64-linux)
racc (~> 1.4)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
racc (1.6.0)
rack (2.2.6.4)
psych (5.1.2)
stringio
racc (1.7.3)
rack (3.0.8)
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
railties (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
method_source
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.1.3)
actionpack (= 7.1.3)
activesupport (= 7.1.3)
irb
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0)
rake (13.0.6)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rake (13.1.0)
rbnacl (7.1.1)
ffi
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.2)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.2)
rdoc (6.6.2)
psych (>= 4.0.0)
reline (0.4.2)
io-console (~> 0.5)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.3)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.6)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0)
rspec-support (3.10.3)
thor (1.2.1)
tzinfo (2.0.4)
rspec-support (~> 3.12.0)
rspec-support (3.12.1)
ruby2_keywords (0.0.5)
stringio (3.1.0)
thor (1.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
zeitwerk (2.6.7)
webrick (1.8.1)
zeitwerk (2.6.12)

PLATFORMS
arm64-darwin-20
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,15 @@ secret: password

#### `eyaml keygen`

Generates the keypair for the encryption flow to work. The public key must be placed into the file at `_public_key` and the private key must be saved in the default key directory (`/opt/ejson/keys`) with the filename being the public key and the contents, the private key, a key directory you'll provide later, or just pass the `--write` flag for `eyaml` to handle it for you.
Generates the keypair for the encryption flow to work. The public key must be placed into the file at `_public_key` like this:
e.g.
```shell
-> % cat config/credentials.development.eyaml
_public_key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
my_secret: 85d1fca99d98c4e7b83b868f75f809e1e33346317b0c354b593cdcdc8793ad4e
```

The private key must be saved in the default key directory (`/opt/ejson/keys`) with the filename being the public key and the contents, the private key, a key directory you'll provide later, or just pass the `--write` flag for `eyaml` to handle it for you.

```shell
-> % eyaml keygen
Expand All @@ -88,9 +96,14 @@ b01592942ba10f152bcf7c6b6734f6392554c578ff24cebcc62f9e3da6fcf302

### Rails

`eyaml` comes with baked in Rails support. It will search for a secrets file in `config/`, decrypt, and load the first valid one it finds.
`eyaml` comes with baked in Rails support. It will search for a secrets or credentials file in `config/`, decrypt, and load the first valid one it finds.
Credential files have priority over secrets before rails 7.2:
`credentials.{eyaml|eyml|ejson}` (e.g. `config/credentials.eyaml`) then `credentials.$env.{eyaml|eyml|ejson}` (e.g. `credentials.production.eyml`).
Then if no credentials are found it will look for a secrets file:
`secrets.{eyaml|eyml|ejson}` (e.g. `config/secrets.eyaml`) then `secrets.$env.{eyaml|eyml|ejson}` (e.g. `secrets.production.eyml`).

Note: From rails 7.2 onwards secrets are deprecated and eyaml will only look for credential files.

Instead of needing a private key locally, you can provide it to EYAML by setting `EJSON_PRIVATE_KEY` and it'll be automatically used for decrypting the secrets file.

### Apple M1 Support
Expand Down
27 changes: 20 additions & 7 deletions lib/eyaml/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@ class Railtie < Rails::Railtie
PRIVATE_KEY_ENV_VAR = "EJSON_PRIVATE_KEY"

config.before_configuration do
secrets_files.each do |file|
secret_files_present = Dir.glob(auth_files(:secrets)).any?
credential_files_present = Dir.glob(auth_files(:credentials)).any?

secrets_or_credentials = if Rails.version >= "7.2"
:credentials
else
if credential_files_present
:credentials
elsif secret_files_present
:secrets
end
end

auth_files(secrets_or_credentials).each do |file|
next unless valid?(file)

# If private_key is nil (i.e. when $EJSON_PRIVATE_KEY is not set), EYAML will search
Expand All @@ -19,7 +32,7 @@ class Railtie < Rails::Railtie
.deep_symbolize_keys
.except(:_public_key)

break Rails.application.secrets.deep_merge!(secrets)
break Rails.application.send(secrets_or_credentials).deep_merge!(secrets)
end
end

Expand All @@ -30,13 +43,13 @@ def valid?(pathname)
pathname.exist?
end

def secrets_files
EYAML::SUPPORTED_EXTENSIONS.map do |ext|
def auth_files(secrets_or_credentials)
EYAML::SUPPORTED_EXTENSIONS.flat_map do |ext|
[
Rails.root.join("config", "secrets.#{ext}"),
Rails.root.join("config", "secrets.#{Rails.env}.#{ext}")
Rails.root.join("config", "#{secrets_or_credentials}.#{ext}"),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe hint that these files will be read and merged from the least specific to the more specific one? also, should there be, say, credentials.production.eyaml and credentials.eyml the latter will overwrite everything introduced by credentials.production.eyaml even though it is more specific (specific to an environment). So to play this safe it would be better to do the product the other way around - first "envless" variants for all extensions, then "enved" variants for all the extensions. Still a bit magical but will be more logical IMO.

Rails.root.join("config", "#{secrets_or_credentials}.#{Rails.env}.#{ext}")
]
end.flatten
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/eyaml/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module EYAML
VERSION = "0.3.0"
VERSION = "0.4.0"
end
Loading
Loading