secvault 1.0.4 → 2.1.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/CHANGELOG.md +60 -0
- data/README.md +29 -227
- data/lib/secvault/railtie.rb +2 -10
- data/lib/secvault/secrets.rb +13 -70
- data/lib/secvault/version.rb +1 -1
- data/lib/secvault.rb +8 -6
- data/secvault-2.0.0.gem +0 -0
- metadata +5 -5
- data/lib/secvault/tasks.rake +0 -135
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c2c9db7124c8ac60bf772bcb42c1367f9e5a25a72c977aecc717da47ec401e8
|
|
4
|
+
data.tar.gz: a8a46d584bf49e2ec7ca1bbfc9609c64b25642406a55efaf79f7f8e5eeeccbba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 52ffd8869e91255f41bbf5bcba424efa8ccfd2a7ec3227c97a8430cd2a21ecbf816558a966ad68afeff611889085b91af765012a9c1529c27917b56cab9aceab
|
|
7
|
+
data.tar.gz: 191341590209b60085ad54047813788e3bd809ad84791c7aacca546bbc9870a653ae6c6811bce083b8e187aae9d14f9d3bdb5b6c533b549dda356e917816828d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.1.0] - 2025-09-22
|
|
4
|
+
|
|
5
|
+
### Removed
|
|
6
|
+
|
|
7
|
+
- **Removed all rake tasks** - Ultimate simplicity! No more `rake secvault:setup`, `rake secvault:edit`, or `rake secvault:show`
|
|
8
|
+
- Removed `lib/secvault/tasks.rake` file entirely
|
|
9
|
+
- Removed rake task loading from railtie
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Ultra-simple setup**: Just create `config/secrets.yml` with any text editor
|
|
14
|
+
- Updated README to reflect manual file creation instead of rake tasks
|
|
15
|
+
- Updated module documentation to show simple 3-step process
|
|
16
|
+
- Cleaner railtie without task loading complexity
|
|
17
|
+
|
|
18
|
+
### Benefits
|
|
19
|
+
|
|
20
|
+
- **Zero dependencies on rake tasks** - works with just plain YAML files
|
|
21
|
+
- **Even simpler** - no commands to remember, just edit YAML files
|
|
22
|
+
- **More intuitive** - developers already know how to create and edit YAML files
|
|
23
|
+
- **Less code** - removed unnecessary complexity
|
|
24
|
+
|
|
25
|
+
### Tested
|
|
26
|
+
|
|
27
|
+
- ✅ Rails 7.1 integration works perfectly
|
|
28
|
+
- ✅ Rails 8.0 automatic setup works perfectly
|
|
29
|
+
- ✅ No rake task conflicts or errors
|
|
30
|
+
|
|
31
|
+
## [2.0.0] - 2025-09-22
|
|
32
|
+
|
|
33
|
+
### BREAKING CHANGES
|
|
34
|
+
|
|
35
|
+
- **Removed all encryption functionality** - Secvault now focuses purely on plain YAML secrets management
|
|
36
|
+
- Removed ActiveSupport::EncryptedFile dependencies
|
|
37
|
+
- Removed MissingKeyError and InvalidKeyError exceptions
|
|
38
|
+
- Removed `encrypted?`, `decrypt`, `decrypt_secrets` methods
|
|
39
|
+
- Simplified rake tasks to work with plain YAML only
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Simplified `rake secvault:setup` that creates plain YAML files with helpful comments
|
|
44
|
+
- Better error messages and user guidance in rake tasks
|
|
45
|
+
- Cleaner, more focused codebase without encryption complexity
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **Major simplification**: All secrets are now stored in plain YAML files
|
|
50
|
+
- Updated README to reflect plain YAML approach
|
|
51
|
+
- Updated module documentation and gemspec descriptions
|
|
52
|
+
- Rake tasks now use emojis and better user experience
|
|
53
|
+
- Production secrets should use ERB syntax with environment variables
|
|
54
|
+
|
|
55
|
+
### Benefits
|
|
56
|
+
|
|
57
|
+
- Much simpler gem with single focus: plain YAML secrets management
|
|
58
|
+
- No encryption keys to manage or lose
|
|
59
|
+
- Easy to understand, edit, and debug secrets files
|
|
60
|
+
- Perfect for development and test environments
|
|
61
|
+
- Production secrets via environment variables (recommended best practice)
|
|
62
|
+
|
|
3
63
|
## [1.0.4] - 2025-09-22
|
|
4
64
|
|
|
5
65
|
### Added
|
data/README.md
CHANGED
|
@@ -1,278 +1,80 @@
|
|
|
1
1
|
# Secvault
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Restores the classic Rails `secrets.yml` functionality that was removed in Rails 7.2. Uses simple, plain YAML files for environment-specific secrets management.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Rails 7.2
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- 🔐 **Encrypted secrets.yml** - Uses Rails' built-in encryption system
|
|
12
|
-
- 🔑 **Key management** - Secure key generation and management
|
|
13
|
-
- 🌍 **Environment-specific** - Different secrets for development, test, and production
|
|
14
|
-
- 📝 **ERB support** - Use ERB templates in your secrets files
|
|
15
|
-
- 🛠️ **Rake tasks** - Easy management with built-in rake tasks
|
|
16
|
-
- 🚀 **Rails 7.2+ compatible** - Works seamlessly with modern Rails
|
|
5
|
+
**Rails Version Support:**
|
|
6
|
+
- **Rails 7.1**: Manual setup (see Rails 7.1 Integration below)
|
|
7
|
+
- **Rails 7.2+**: Automatic setup
|
|
8
|
+
- **Rails 8.0+**: Full compatibility
|
|
17
9
|
|
|
18
10
|
## Installation
|
|
19
11
|
|
|
20
|
-
Add this line to your application's Gemfile:
|
|
21
|
-
|
|
22
12
|
```ruby
|
|
13
|
+
# Gemfile
|
|
23
14
|
gem 'secvault'
|
|
24
15
|
```
|
|
25
16
|
|
|
26
|
-
And then execute:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
$ bundle install
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Rails Version Compatibility
|
|
33
|
-
|
|
34
|
-
- **Rails 7.2+**: Automatic setup, drop-in replacement for removed secrets functionality
|
|
35
|
-
- **Rails 7.1**: Manual setup required (see Rails 7.1 Integration section below)
|
|
36
|
-
- **Rails 8.0+**: Full compatibility
|
|
37
|
-
|
|
38
|
-
## Setup
|
|
39
|
-
|
|
40
|
-
### 1. Generate secrets file
|
|
41
|
-
|
|
42
|
-
Run the setup task to create your encrypted `secrets.yml`:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
$ rake secvault:setup
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
This will:
|
|
49
|
-
- Generate `config/secrets.yml.key` (keep this secure!)
|
|
50
|
-
- Create an encrypted `config/secrets.yml` with default content
|
|
51
|
-
- Remind you to add the key file to `.gitignore`
|
|
52
|
-
|
|
53
|
-
Alternatively, use the Rails generator:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
$ rails generate secvault:secrets
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 2. Add key to .gitignore
|
|
60
|
-
|
|
61
|
-
Ensure your encryption key is not committed to version control:
|
|
62
|
-
|
|
63
17
|
```bash
|
|
64
|
-
|
|
18
|
+
bundle install
|
|
65
19
|
```
|
|
66
20
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
### Editing secrets
|
|
70
|
-
|
|
71
|
-
Edit your encrypted secrets file:
|
|
21
|
+
## Quick Start (Rails 7.2+)
|
|
72
22
|
|
|
73
23
|
```bash
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
This opens the decrypted file in your `$EDITOR`.
|
|
78
|
-
|
|
79
|
-
### Viewing secrets
|
|
24
|
+
# 1. Create secrets.yml
|
|
25
|
+
touch config/secrets.yml
|
|
80
26
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
$ rake secvault:show
|
|
27
|
+
# 2. Edit with your favorite editor
|
|
28
|
+
$EDITOR config/secrets.yml
|
|
85
29
|
```
|
|
86
30
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
Secrets are automatically loaded into `Rails.application.secrets`:
|
|
90
|
-
|
|
31
|
+
**Usage in your app:**
|
|
91
32
|
```ruby
|
|
92
|
-
# In your Rails application
|
|
93
|
-
Rails.application.secrets.secret_key_base
|
|
94
33
|
Rails.application.secrets.api_key
|
|
95
34
|
Rails.application.secrets.database_password
|
|
96
35
|
```
|
|
97
36
|
|
|
98
|
-
|
|
99
|
-
|
|
37
|
+
**Example secrets.yml:**
|
|
100
38
|
```yaml
|
|
101
|
-
# config/secrets.yml (encrypted)
|
|
102
39
|
development:
|
|
103
|
-
|
|
104
|
-
api_key: dev_api_key
|
|
40
|
+
api_key: dev_key
|
|
105
41
|
database_password: dev_password
|
|
106
42
|
|
|
107
|
-
test:
|
|
108
|
-
secret_key_base: your_test_secret
|
|
109
|
-
api_key: test_api_key
|
|
110
|
-
database_password: test_password
|
|
111
|
-
|
|
112
43
|
production:
|
|
113
|
-
secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
|
|
114
44
|
api_key: <%= ENV['API_KEY'] %>
|
|
115
45
|
database_password: <%= ENV['DATABASE_PASSWORD'] %>
|
|
116
46
|
```
|
|
117
47
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
export RAILS_SECRETS_KEY=your_encryption_key
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Production Deployment
|
|
127
|
-
|
|
128
|
-
### Option 1: Environment Variable
|
|
129
|
-
|
|
130
|
-
Set the encryption key as an environment variable:
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
export RAILS_SECRETS_KEY=your_encryption_key
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Option 2: Key File
|
|
137
|
-
|
|
138
|
-
Securely copy `config/secrets.yml.key` to your production server.
|
|
139
|
-
|
|
140
|
-
### Docker
|
|
141
|
-
|
|
142
|
-
For Docker deployments, you can pass the key as an environment variable:
|
|
143
|
-
|
|
144
|
-
```dockerfile
|
|
145
|
-
ENV RAILS_SECRETS_KEY=your_encryption_key
|
|
48
|
+
**Production:** Use environment variables in your YAML:
|
|
49
|
+
```yaml
|
|
50
|
+
production:
|
|
51
|
+
api_key: <%= ENV['API_KEY'] %>
|
|
146
52
|
```
|
|
147
53
|
|
|
148
|
-
## Rake Tasks
|
|
149
|
-
|
|
150
|
-
| Task | Description |
|
|
151
|
-
|------|-------------|
|
|
152
|
-
| `rake secvault:setup` | Create encrypted secrets.yml and key |
|
|
153
|
-
| `rake secvault:edit` | Edit the encrypted secrets file |
|
|
154
|
-
| `rake secvault:show` | Display decrypted secrets content |
|
|
155
|
-
|
|
156
54
|
## Rails 7.1 Integration
|
|
157
55
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### Quick Setup (Recommended)
|
|
161
|
-
|
|
162
|
-
Add this to `config/initializers/secvault.rb`:
|
|
56
|
+
Test Secvault in Rails 7.1 before upgrading to 7.2+:
|
|
163
57
|
|
|
164
58
|
```ruby
|
|
165
59
|
# config/initializers/secvault.rb
|
|
166
60
|
Secvault.setup_rails_71_integration!
|
|
167
61
|
```
|
|
168
62
|
|
|
169
|
-
|
|
170
|
-
- Override native Rails::Secrets with Secvault implementation
|
|
171
|
-
- Replace Rails.application.secrets with Secvault functionality
|
|
172
|
-
- Load secrets from config/secrets.yml automatically
|
|
173
|
-
|
|
174
|
-
### Manual Setup (Advanced)
|
|
175
|
-
|
|
176
|
-
If you prefer more control, you can set it up manually:
|
|
177
|
-
|
|
178
|
-
```ruby
|
|
179
|
-
# config/initializers/secvault.rb
|
|
180
|
-
module Rails
|
|
181
|
-
remove_const(:Secrets) if defined?(Secrets)
|
|
182
|
-
Secrets = Secvault::RailsSecrets
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
Rails.application.config.after_initialize do
|
|
186
|
-
secrets_path = Rails.root.join("config/secrets.yml")
|
|
187
|
-
|
|
188
|
-
if secrets_path.exist?
|
|
189
|
-
loaded_secrets = Rails::Secrets.parse([secrets_path], env: Rails.env)
|
|
190
|
-
secrets_object = ActiveSupport::OrderedOptions.new
|
|
191
|
-
secrets_object.merge!(loaded_secrets)
|
|
192
|
-
|
|
193
|
-
Rails.application.define_singleton_method(:secrets) do
|
|
194
|
-
secrets_object
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Rails 7.1 Benefits
|
|
201
|
-
|
|
202
|
-
✅ **Test before upgrading**: Validate Secvault works with your secrets
|
|
203
|
-
✅ **Zero code changes**: Existing Rails 7.1 code continues to work
|
|
204
|
-
✅ **Smooth migration**: Gradual transition to Rails 7.2+
|
|
205
|
-
✅ **Full compatibility**: All Rails.application.secrets functionality preserved
|
|
206
|
-
|
|
207
|
-
### Example Rails 7.1 Usage
|
|
63
|
+
This replaces Rails.application.secrets with Secvault functionality. Your existing Rails 7.1 code works unchanged:
|
|
208
64
|
|
|
209
65
|
```ruby
|
|
210
|
-
# Works
|
|
211
|
-
Rails.application.secrets.
|
|
212
|
-
Rails.
|
|
213
|
-
|
|
214
|
-
# Plus enhanced Secvault functionality
|
|
215
|
-
Rails::Secrets.parse_default(env: 'development')
|
|
216
|
-
Rails::Secrets.parse([custom_path], env: Rails.env)
|
|
66
|
+
Rails.application.secrets.api_key # ✅ Works
|
|
67
|
+
Rails.application.secrets.oauth_settings # ✅ Works
|
|
68
|
+
Rails::Secrets.parse_default # ✅ Enhanced functionality
|
|
217
69
|
```
|
|
218
70
|
|
|
219
|
-
## Migration from Rails < 7.2
|
|
220
|
-
|
|
221
|
-
If you're upgrading from an older Rails version that had `secrets.yml`:
|
|
222
|
-
|
|
223
|
-
1. Install secvault: `bundle add secvault`
|
|
224
|
-
2. Encrypt existing secrets: `rake secvault:setup`
|
|
225
|
-
3. Copy your existing secrets content using `rake secvault:edit`
|
|
226
|
-
4. Remove the old plain-text `config/secrets.yml`
|
|
227
71
|
|
|
228
|
-
## Security
|
|
72
|
+
## Security
|
|
229
73
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
- ✅ **Use strong, unique keys** for each environment
|
|
234
|
-
- ✅ **Limit access** to key files in production
|
|
235
|
-
|
|
236
|
-
## Troubleshooting
|
|
237
|
-
|
|
238
|
-
### Missing Key Error
|
|
239
|
-
|
|
240
|
-
```
|
|
241
|
-
Missing encryption key to decrypt secrets.yml
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**Solution**: Ensure `config/secrets.yml.key` exists or set `RAILS_SECRETS_KEY` environment variable.
|
|
245
|
-
|
|
246
|
-
### Invalid Key Error
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
Invalid encryption key for secrets.yml
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
**Solution**: The key doesn't match the encrypted file. Verify you're using the correct key.
|
|
253
|
-
|
|
254
|
-
### File Not Found
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
Secrets file doesn't exist
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
**Solution**: Run `rake secvault:setup` to create the secrets file.
|
|
261
|
-
|
|
262
|
-
## Development
|
|
263
|
-
|
|
264
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
265
|
-
|
|
266
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
267
|
-
|
|
268
|
-
## Contributing
|
|
269
|
-
|
|
270
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/unnitallman/secvault.
|
|
74
|
+
⚠️ Never commit production secrets to version control
|
|
75
|
+
✅ Use environment variables for production secrets with ERB syntax: `<%= ENV['SECRET'] %>`
|
|
76
|
+
✅ Add `config/secrets.yml` to `.gitignore` if it contains sensitive data
|
|
271
77
|
|
|
272
78
|
## License
|
|
273
79
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
## Code of Conduct
|
|
277
|
-
|
|
278
|
-
Everyone interacting in the Secvault project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/secvault/blob/main/CODE_OF_CONDUCT.md).
|
|
80
|
+
MIT License - see [LICENSE](https://opensource.org/licenses/MIT)
|
data/lib/secvault/railtie.rb
CHANGED
|
@@ -13,18 +13,17 @@ module Secvault
|
|
|
13
13
|
# Ensure initialization happens early in all environments
|
|
14
14
|
config.before_configuration do |app|
|
|
15
15
|
secrets_path = app.root.join("config/secrets.yml")
|
|
16
|
-
key_path = app.root.join("config/secrets.yml.key")
|
|
17
16
|
|
|
18
17
|
if secrets_path.exist? && !Rails.application.respond_to?(:secrets)
|
|
19
18
|
# Early initialization for test environment compatibility
|
|
20
19
|
current_env = ENV['RAILS_ENV'] || 'development'
|
|
21
|
-
secrets = Secvault::Secrets.read_secrets(secrets_path,
|
|
20
|
+
secrets = Secvault::Secrets.read_secrets(secrets_path, current_env)
|
|
22
21
|
|
|
23
22
|
if secrets
|
|
24
23
|
Rails.application.define_singleton_method(:secrets) do
|
|
25
24
|
@secrets ||= begin
|
|
26
25
|
current_secrets = ActiveSupport::OrderedOptions.new
|
|
27
|
-
env_secrets = Secvault::Secrets.read_secrets(secrets_path,
|
|
26
|
+
env_secrets = Secvault::Secrets.read_secrets(secrets_path, Rails.env)
|
|
28
27
|
current_secrets.merge!(env_secrets) if env_secrets
|
|
29
28
|
current_secrets
|
|
30
29
|
end
|
|
@@ -33,12 +32,5 @@ module Secvault
|
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
generators do
|
|
37
|
-
require "secvault/generators/secrets_generator"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
rake_tasks do
|
|
41
|
-
load "secvault/tasks.rake"
|
|
42
|
-
end
|
|
43
35
|
end
|
|
44
36
|
end
|
data/lib/secvault/secrets.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/encrypted_file"
|
|
4
3
|
require "active_support/core_ext/hash/keys"
|
|
5
|
-
require "active_support/core_ext/object/blank"
|
|
6
4
|
require "active_support/ordered_options"
|
|
7
5
|
require "pathname"
|
|
8
6
|
require "erb"
|
|
@@ -16,20 +14,19 @@ module Secvault
|
|
|
16
14
|
return unless rails_7_2_or_later?
|
|
17
15
|
|
|
18
16
|
secrets_path = app.root.join("config/secrets.yml")
|
|
19
|
-
key_path = app.root.join("config/secrets.yml.key")
|
|
20
17
|
|
|
21
18
|
if secrets_path.exist?
|
|
22
19
|
# Use a more reliable approach that works in all environments
|
|
23
20
|
app.config.before_configuration do
|
|
24
21
|
current_env = ENV['RAILS_ENV'] || Rails.env || 'development'
|
|
25
|
-
setup_secrets_immediately(app, secrets_path,
|
|
22
|
+
setup_secrets_immediately(app, secrets_path, current_env)
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
# Also try during to_prepare as a fallback
|
|
29
26
|
app.config.to_prepare do
|
|
30
27
|
current_env = Rails.env
|
|
31
28
|
unless Rails.application.respond_to?(:secrets) && !Rails.application.secrets.empty?
|
|
32
|
-
setup_secrets_immediately(app, secrets_path,
|
|
29
|
+
setup_secrets_immediately(app, secrets_path, current_env)
|
|
33
30
|
end
|
|
34
31
|
end
|
|
35
32
|
end
|
|
@@ -38,19 +35,18 @@ module Secvault
|
|
|
38
35
|
# Manual setup method for Rails 7.1 (opt-in)
|
|
39
36
|
def setup_for_rails_71!(app)
|
|
40
37
|
secrets_path = app.root.join("config/secrets.yml")
|
|
41
|
-
key_path = app.root.join("config/secrets.yml.key")
|
|
42
38
|
|
|
43
39
|
if secrets_path.exist?
|
|
44
40
|
app.config.before_configuration do
|
|
45
41
|
current_env = ENV['RAILS_ENV'] || Rails.env || 'development'
|
|
46
|
-
setup_secrets_immediately(app, secrets_path,
|
|
42
|
+
setup_secrets_immediately(app, secrets_path, current_env)
|
|
47
43
|
end
|
|
48
44
|
end
|
|
49
45
|
end
|
|
50
46
|
|
|
51
|
-
def setup_secrets_immediately(app, secrets_path,
|
|
47
|
+
def setup_secrets_immediately(app, secrets_path, env)
|
|
52
48
|
# Set up secrets if they exist
|
|
53
|
-
secrets = read_secrets(secrets_path,
|
|
49
|
+
secrets = read_secrets(secrets_path, env)
|
|
54
50
|
if secrets
|
|
55
51
|
# Rails 8.0+ compatibility: Add secrets accessor that initializes on first access
|
|
56
52
|
unless Rails.application.respond_to?(:secrets)
|
|
@@ -58,7 +54,7 @@ module Secvault
|
|
|
58
54
|
@secrets ||= begin
|
|
59
55
|
current_secrets = ActiveSupport::OrderedOptions.new
|
|
60
56
|
# Re-read secrets to ensure we have the right environment
|
|
61
|
-
env_secrets = Secvault::Secrets.read_secrets(secrets_path,
|
|
57
|
+
env_secrets = Secvault::Secrets.read_secrets(secrets_path, Rails.env)
|
|
62
58
|
current_secrets.merge!(env_secrets) if env_secrets
|
|
63
59
|
current_secrets
|
|
64
60
|
end
|
|
@@ -73,19 +69,15 @@ module Secvault
|
|
|
73
69
|
end
|
|
74
70
|
|
|
75
71
|
# Classic Rails::Secrets.parse implementation
|
|
76
|
-
# Parses secrets files and merges shared + environment-specific sections
|
|
72
|
+
# Parses plain YAML secrets files and merges shared + environment-specific sections
|
|
77
73
|
def parse(paths, env:)
|
|
78
74
|
paths.each_with_object(Hash.new) do |path, all_secrets|
|
|
79
75
|
# Handle string paths by converting to Pathname
|
|
80
76
|
path = Pathname.new(path) unless path.respond_to?(:exist?)
|
|
81
77
|
next unless path.exist?
|
|
82
78
|
|
|
83
|
-
# Read and process the file content
|
|
84
|
-
source =
|
|
85
|
-
decrypt(path)
|
|
86
|
-
else
|
|
87
|
-
preprocess(path)
|
|
88
|
-
end
|
|
79
|
+
# Read and process the plain YAML file content
|
|
80
|
+
source = path.read
|
|
89
81
|
|
|
90
82
|
# Process ERB and parse YAML
|
|
91
83
|
erb_result = ERB.new(source).result
|
|
@@ -102,22 +94,12 @@ module Secvault
|
|
|
102
94
|
all_secrets.merge!(secrets[env].deep_symbolize_keys) if secrets[env]
|
|
103
95
|
end
|
|
104
96
|
end
|
|
105
|
-
|
|
106
|
-
# Helper method to preprocess plain YAML files (for ERB)
|
|
107
|
-
def preprocess(path)
|
|
108
|
-
path.read
|
|
109
|
-
end
|
|
110
97
|
|
|
111
|
-
def read_secrets(secrets_path,
|
|
98
|
+
def read_secrets(secrets_path, env)
|
|
112
99
|
if secrets_path.exist?
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
else
|
|
117
|
-
# Handle plain YAML secrets.yml
|
|
118
|
-
YAML.safe_load(ERB.new(secrets_path.read).result, aliases: true)
|
|
119
|
-
end
|
|
120
|
-
|
|
100
|
+
# Handle plain YAML secrets.yml only
|
|
101
|
+
all_secrets = YAML.safe_load(ERB.new(secrets_path.read).result, aliases: true)
|
|
102
|
+
|
|
121
103
|
env_secrets = all_secrets[env.to_s]
|
|
122
104
|
return env_secrets.deep_symbolize_keys if env_secrets
|
|
123
105
|
end
|
|
@@ -125,46 +107,7 @@ module Secvault
|
|
|
125
107
|
{}
|
|
126
108
|
end
|
|
127
109
|
|
|
128
|
-
def encrypted?(path)
|
|
129
|
-
# Simple heuristic to detect if file is encrypted
|
|
130
|
-
content = path.read
|
|
131
|
-
# Encrypted files typically contain non-printable characters
|
|
132
|
-
!content.valid_encoding? || content.bytes.any? { |b| b < 32 && b != 10 && b != 13 }
|
|
133
|
-
rescue
|
|
134
|
-
false
|
|
135
|
-
end
|
|
136
|
-
|
|
137
110
|
private
|
|
138
|
-
|
|
139
|
-
def decrypt_secrets(secrets_path, key_path)
|
|
140
|
-
encrypted_file = ActiveSupport::EncryptedFile.new(
|
|
141
|
-
content_path: secrets_path,
|
|
142
|
-
key_path: key_path,
|
|
143
|
-
env_key: "RAILS_SECRETS_KEY",
|
|
144
|
-
raise_if_missing_key: true
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
content = encrypted_file.read
|
|
148
|
-
YAML.safe_load(ERB.new(content).result, aliases: true) if content.present?
|
|
149
|
-
rescue ActiveSupport::EncryptedFile::MissingKeyError
|
|
150
|
-
raise MissingKeyError,
|
|
151
|
-
"Missing encryption key to decrypt secrets.yml. " \
|
|
152
|
-
"Ask your team for your secrets key and put it in config/secrets.yml.key"
|
|
153
|
-
rescue ActiveSupport::EncryptedFile::InvalidMessage
|
|
154
|
-
raise InvalidKeyError,
|
|
155
|
-
"Invalid encryption key for secrets.yml."
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def decrypt(path)
|
|
159
|
-
key_path = Pathname.new("#{path}.key")
|
|
160
|
-
encrypted_file = ActiveSupport::EncryptedFile.new(
|
|
161
|
-
content_path: path,
|
|
162
|
-
key_path: key_path,
|
|
163
|
-
env_key: "RAILS_SECRETS_KEY",
|
|
164
|
-
raise_if_missing_key: true
|
|
165
|
-
)
|
|
166
|
-
encrypted_file.read
|
|
167
|
-
end
|
|
168
111
|
|
|
169
112
|
def rails_7_2_or_later?
|
|
170
113
|
rails_version = Rails.version
|
data/lib/secvault/version.rb
CHANGED
data/lib/secvault.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "rails"
|
|
4
4
|
require "yaml"
|
|
5
5
|
require "erb"
|
|
6
|
-
require "active_support/encrypted_file"
|
|
7
6
|
require "active_support/core_ext/hash/keys"
|
|
8
7
|
require "zeitwerk"
|
|
9
8
|
|
|
@@ -12,11 +11,11 @@ require_relative "secvault/version"
|
|
|
12
11
|
loader = Zeitwerk::Loader.for_gem
|
|
13
12
|
loader.setup
|
|
14
13
|
|
|
15
|
-
# Secvault -
|
|
14
|
+
# Secvault - Simple secrets management for Rails
|
|
16
15
|
#
|
|
17
16
|
# Secvault restores the classic Rails secrets.yml functionality that was removed
|
|
18
|
-
# in Rails 7.2,
|
|
19
|
-
#
|
|
17
|
+
# in Rails 7.2, using simple, plain YAML files for environment-specific secrets
|
|
18
|
+
# management.
|
|
20
19
|
#
|
|
21
20
|
# ## Rails Version Support:
|
|
22
21
|
# - Rails 7.1: Requires manual setup (see Rails 7.1 integration guide)
|
|
@@ -47,11 +46,14 @@ loader.setup
|
|
|
47
46
|
# Rails.application.secrets.oauth_settings[:google_client_id]
|
|
48
47
|
# Rails::Secrets.parse_default(env: 'development')
|
|
49
48
|
#
|
|
49
|
+
# ## Getting Started:
|
|
50
|
+
# 1. Create config/secrets.yml with your secrets
|
|
51
|
+
# 2. Use Rails.application.secrets.your_secret in your app
|
|
52
|
+
# 3. For production, use environment variables with ERB syntax
|
|
53
|
+
#
|
|
50
54
|
# @see https://github.com/unnitallman/secvault
|
|
51
55
|
module Secvault
|
|
52
56
|
class Error < StandardError; end
|
|
53
|
-
class MissingKeyError < Error; end
|
|
54
|
-
class InvalidKeyError < Error; end
|
|
55
57
|
|
|
56
58
|
extend self
|
|
57
59
|
|
data/secvault-2.0.0.gem
ADDED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secvault
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Unnikrishnan KP
|
|
@@ -39,8 +39,8 @@ dependencies:
|
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '2.6'
|
|
41
41
|
description: Secvault restores the classic Rails secrets.yml functionality that was
|
|
42
|
-
removed in Rails 7.2,
|
|
43
|
-
|
|
42
|
+
removed in Rails 7.2, using simple, plain YAML files for environment-specific secrets
|
|
43
|
+
management. Compatible with Rails 7.1+, 7.2+ and 8.0+.
|
|
44
44
|
email:
|
|
45
45
|
- unnikrishnan.kp@bigbinary.com
|
|
46
46
|
executables: []
|
|
@@ -60,8 +60,8 @@ files:
|
|
|
60
60
|
- lib/secvault/railtie.rb
|
|
61
61
|
- lib/secvault/secrets.rb
|
|
62
62
|
- lib/secvault/secrets_helper.rb
|
|
63
|
-
- lib/secvault/tasks.rake
|
|
64
63
|
- lib/secvault/version.rb
|
|
64
|
+
- secvault-2.0.0.gem
|
|
65
65
|
- sig/secvault.rbs
|
|
66
66
|
homepage: https://github.com/unnitallman/secvault
|
|
67
67
|
licenses:
|
|
@@ -89,5 +89,5 @@ requirements: []
|
|
|
89
89
|
rubygems_version: 3.5.10
|
|
90
90
|
signing_key:
|
|
91
91
|
specification_version: 4
|
|
92
|
-
summary: Rails secrets.yml functionality for Rails 7.1+, 7.2+ and Rails 8.0+
|
|
92
|
+
summary: Simple Rails secrets.yml functionality for Rails 7.1+, 7.2+ and Rails 8.0+
|
|
93
93
|
test_files: []
|
data/lib/secvault/tasks.rake
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "active_support/encrypted_file"
|
|
4
|
-
require "securerandom"
|
|
5
|
-
|
|
6
|
-
namespace :secvault do
|
|
7
|
-
desc "Setup secrets.yml file (plain YAML by default)"
|
|
8
|
-
task setup: :environment do
|
|
9
|
-
secrets_path = Rails.root.join("config/secrets.yml")
|
|
10
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
11
|
-
encrypted = ENV["ENCRYPTED"] == "true"
|
|
12
|
-
|
|
13
|
-
if secrets_path.exist?
|
|
14
|
-
puts "Secrets file already exists at #{secrets_path}"
|
|
15
|
-
else
|
|
16
|
-
default_content = <<~YAML
|
|
17
|
-
# Be sure to restart your server when you modify this file.
|
|
18
|
-
#
|
|
19
|
-
# Your secret key is used for verifying the integrity of signed cookies.
|
|
20
|
-
# If you change this key, all old signed cookies will become invalid!
|
|
21
|
-
#
|
|
22
|
-
# Make sure the secret is at least 30 characters and all random,
|
|
23
|
-
# no regular words or you'll be exposed to dictionary attacks.
|
|
24
|
-
# You can use `rails secret` to generate a secure secret key.
|
|
25
|
-
|
|
26
|
-
development:
|
|
27
|
-
secret_key_base: #{SecureRandom.hex(64)}
|
|
28
|
-
|
|
29
|
-
test:
|
|
30
|
-
secret_key_base: #{SecureRandom.hex(64)}
|
|
31
|
-
|
|
32
|
-
# Do not keep production secrets in the repository,
|
|
33
|
-
# instead read values from the environment.
|
|
34
|
-
production:
|
|
35
|
-
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
|
36
|
-
YAML
|
|
37
|
-
|
|
38
|
-
if encrypted
|
|
39
|
-
# Create encrypted file
|
|
40
|
-
unless key_path.exist?
|
|
41
|
-
key = ActiveSupport::EncryptedFile.generate_key
|
|
42
|
-
File.write(key_path, key)
|
|
43
|
-
puts "Generated encryption key in #{key_path}"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
encrypted_file = ActiveSupport::EncryptedFile.new(
|
|
47
|
-
content_path: secrets_path,
|
|
48
|
-
key_path: key_path,
|
|
49
|
-
env_key: "RAILS_SECRETS_KEY",
|
|
50
|
-
raise_if_missing_key: true
|
|
51
|
-
)
|
|
52
|
-
encrypted_file.write(default_content)
|
|
53
|
-
puts "Created encrypted secrets.yml file"
|
|
54
|
-
puts "Add #{key_path} to your .gitignore file"
|
|
55
|
-
else
|
|
56
|
-
# Create plain YAML file
|
|
57
|
-
File.write(secrets_path, default_content)
|
|
58
|
-
puts "Created plain secrets.yml file"
|
|
59
|
-
puts "Remember to add #{secrets_path} to your .gitignore if it contains sensitive data"
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
desc "Edit secrets.yml file"
|
|
65
|
-
task edit: :environment do
|
|
66
|
-
secrets_path = Rails.root.join("config/secrets.yml")
|
|
67
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
68
|
-
|
|
69
|
-
unless secrets_path.exist?
|
|
70
|
-
puts "Secrets file doesn't exist. Run 'rake secvault:setup' first."
|
|
71
|
-
exit 1
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Check if file is encrypted
|
|
75
|
-
is_encrypted = Secvault::Secrets.encrypted?(secrets_path)
|
|
76
|
-
|
|
77
|
-
if is_encrypted && key_path.exist?
|
|
78
|
-
# Handle encrypted file
|
|
79
|
-
encrypted_file = ActiveSupport::EncryptedFile.new(
|
|
80
|
-
content_path: secrets_path,
|
|
81
|
-
key_path: key_path,
|
|
82
|
-
env_key: "RAILS_SECRETS_KEY",
|
|
83
|
-
raise_if_missing_key: true
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
encrypted_file.change do |tmp_path|
|
|
87
|
-
system("#{ENV["EDITOR"] || "vi"} #{tmp_path}")
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
puts "Updated encrypted #{secrets_path}"
|
|
91
|
-
else
|
|
92
|
-
# Handle plain YAML file
|
|
93
|
-
system("#{ENV["EDITOR"] || "vi"} #{secrets_path}")
|
|
94
|
-
puts "Updated plain #{secrets_path}"
|
|
95
|
-
end
|
|
96
|
-
rescue ActiveSupport::EncryptedFile::MissingKeyError
|
|
97
|
-
puts "Missing encryption key to decrypt secrets.yml."
|
|
98
|
-
puts "Ask your team for your secrets key and put it in #{key_path}"
|
|
99
|
-
rescue ActiveSupport::EncryptedFile::InvalidMessage
|
|
100
|
-
puts "Invalid encryption key for secrets.yml."
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
desc "Show secrets.yml content"
|
|
104
|
-
task show: :environment do
|
|
105
|
-
secrets_path = Rails.root.join("config/secrets.yml")
|
|
106
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
107
|
-
|
|
108
|
-
unless secrets_path.exist?
|
|
109
|
-
puts "Secrets file doesn't exist. Run 'rake secvault:setup' first."
|
|
110
|
-
exit 1
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Check if file is encrypted
|
|
114
|
-
is_encrypted = Secvault::Secrets.encrypted?(secrets_path)
|
|
115
|
-
|
|
116
|
-
if is_encrypted && key_path.exist?
|
|
117
|
-
# Handle encrypted file
|
|
118
|
-
encrypted_file = ActiveSupport::EncryptedFile.new(
|
|
119
|
-
content_path: secrets_path,
|
|
120
|
-
key_path: key_path,
|
|
121
|
-
env_key: "RAILS_SECRETS_KEY",
|
|
122
|
-
raise_if_missing_key: true
|
|
123
|
-
)
|
|
124
|
-
puts encrypted_file.read
|
|
125
|
-
else
|
|
126
|
-
# Handle plain YAML file
|
|
127
|
-
puts File.read(secrets_path)
|
|
128
|
-
end
|
|
129
|
-
rescue ActiveSupport::EncryptedFile::MissingKeyError
|
|
130
|
-
puts "Missing encryption key to decrypt secrets.yml."
|
|
131
|
-
puts "Ask your team for your secrets key and put it in #{key_path}"
|
|
132
|
-
rescue ActiveSupport::EncryptedFile::InvalidMessage
|
|
133
|
-
puts "Invalid encryption key for secrets.yml."
|
|
134
|
-
end
|
|
135
|
-
end
|