secvault 1.0.4 → 2.0.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 +32 -0
- data/README.md +34 -225
- data/lib/secvault/secrets.rb +13 -70
- data/lib/secvault/tasks.rake +28 -88
- data/lib/secvault/version.rb +1 -1
- data/lib/secvault.rb +8 -6
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 99d7d1404f42246f62cbc8349ab689b43e8f24fa0ea52dd0e10910dee9615dcc
|
|
4
|
+
data.tar.gz: 4453d7ebce5a81e9e5fe0ed1dcd0df410a442ff669dd8dde02c15c3db3209a2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c11facd4f7abd26f8ecc9bbccabf0ede865210e220a944aee8e4640cd7851237db2c3bebeba2bc9ddee96a5fe52fa70c324f9b3f5aee4e9a09378ebf2703c91a
|
|
7
|
+
data.tar.gz: f01237875bb61dac4a8e145adeb05c36813937f72a9d4af85c3ae6e5069c02e0df9efd4df7e1f30dee9263fc69543eb04ebbb2a6998b29a30fbced84702ada90
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.0.0] - 2025-09-22
|
|
4
|
+
|
|
5
|
+
### BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
- **Removed all encryption functionality** - Secvault now focuses purely on plain YAML secrets management
|
|
8
|
+
- Removed ActiveSupport::EncryptedFile dependencies
|
|
9
|
+
- Removed MissingKeyError and InvalidKeyError exceptions
|
|
10
|
+
- Removed `encrypted?`, `decrypt`, `decrypt_secrets` methods
|
|
11
|
+
- Simplified rake tasks to work with plain YAML only
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Simplified `rake secvault:setup` that creates plain YAML files with helpful comments
|
|
16
|
+
- Better error messages and user guidance in rake tasks
|
|
17
|
+
- Cleaner, more focused codebase without encryption complexity
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Major simplification**: All secrets are now stored in plain YAML files
|
|
22
|
+
- Updated README to reflect plain YAML approach
|
|
23
|
+
- Updated module documentation and gemspec descriptions
|
|
24
|
+
- Rake tasks now use emojis and better user experience
|
|
25
|
+
- Production secrets should use ERB syntax with environment variables
|
|
26
|
+
|
|
27
|
+
### Benefits
|
|
28
|
+
|
|
29
|
+
- Much simpler gem with single focus: plain YAML secrets management
|
|
30
|
+
- No encryption keys to manage or lose
|
|
31
|
+
- Easy to understand, edit, and debug secrets files
|
|
32
|
+
- Perfect for development and test environments
|
|
33
|
+
- Production secrets via environment variables (recommended best practice)
|
|
34
|
+
|
|
3
35
|
## [1.0.4] - 2025-09-22
|
|
4
36
|
|
|
5
37
|
### Added
|
data/README.md
CHANGED
|
@@ -1,278 +1,87 @@
|
|
|
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
17
|
```bash
|
|
45
|
-
|
|
18
|
+
bundle install
|
|
46
19
|
```
|
|
47
20
|
|
|
48
|
-
|
|
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:
|
|
21
|
+
## Quick Start (Rails 7.2+)
|
|
54
22
|
|
|
55
23
|
```bash
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### 2. Add key to .gitignore
|
|
24
|
+
# 1. Create secrets.yml with rake task
|
|
25
|
+
rake secvault:setup
|
|
60
26
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
echo "/config/secrets.yml.key" >> .gitignore
|
|
27
|
+
# 2. Edit secrets
|
|
28
|
+
rake secvault:edit
|
|
65
29
|
```
|
|
66
30
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
### Editing secrets
|
|
70
|
-
|
|
71
|
-
Edit your encrypted secrets file:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
$ rake secvault:edit
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This opens the decrypted file in your `$EDITOR`.
|
|
78
|
-
|
|
79
|
-
### Viewing secrets
|
|
80
|
-
|
|
81
|
-
View the decrypted content:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
$ rake secvault:show
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Accessing secrets in your application
|
|
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
|
-
##
|
|
220
|
-
|
|
221
|
-
If you're upgrading from an older Rails version that had `secrets.yml`:
|
|
71
|
+
## Available Commands
|
|
222
72
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
## Security Best Practices
|
|
229
|
-
|
|
230
|
-
- ✅ **Never commit** `config/secrets.yml.key` to version control
|
|
231
|
-
- ✅ **Use environment variables** for production secrets when possible
|
|
232
|
-
- ✅ **Rotate keys** periodically
|
|
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
|
|
73
|
+
```bash
|
|
74
|
+
rake secvault:setup # Create plain secrets.yml file
|
|
75
|
+
rake secvault:edit # Edit secrets.yml file
|
|
76
|
+
rake secvault:show # Display secrets.yml content
|
|
258
77
|
```
|
|
259
78
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
## Development
|
|
79
|
+
## Security
|
|
263
80
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
## Contributing
|
|
269
|
-
|
|
270
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/unnitallman/secvault.
|
|
81
|
+
⚠️ Never commit production secrets to version control
|
|
82
|
+
✅ Use environment variables for production secrets with ERB syntax: `<%= ENV['SECRET'] %>`
|
|
83
|
+
✅ Add `config/secrets.yml` to `.gitignore` if it contains sensitive data
|
|
271
84
|
|
|
272
85
|
## License
|
|
273
86
|
|
|
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).
|
|
87
|
+
MIT License - see [LICENSE](https://opensource.org/licenses/MIT)
|
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/tasks.rake
CHANGED
|
@@ -1,135 +1,75 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/encrypted_file"
|
|
4
3
|
require "securerandom"
|
|
5
4
|
|
|
6
5
|
namespace :secvault do
|
|
7
|
-
desc "
|
|
6
|
+
desc "Create a plain YAML secrets.yml file"
|
|
8
7
|
task setup: :environment do
|
|
9
8
|
secrets_path = Rails.root.join("config/secrets.yml")
|
|
10
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
11
|
-
encrypted = ENV["ENCRYPTED"] == "true"
|
|
12
9
|
|
|
13
10
|
if secrets_path.exist?
|
|
14
11
|
puts "Secrets file already exists at #{secrets_path}"
|
|
15
12
|
else
|
|
16
13
|
default_content = <<~YAML
|
|
17
|
-
#
|
|
14
|
+
# Plain YAML secrets file
|
|
15
|
+
# Environment-specific secrets for your Rails application
|
|
18
16
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
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.
|
|
17
|
+
# For production, use environment variables with ERB syntax:
|
|
18
|
+
# production:
|
|
19
|
+
# api_key: <%= ENV['API_KEY'] %>
|
|
25
20
|
|
|
26
21
|
development:
|
|
27
22
|
secret_key_base: #{SecureRandom.hex(64)}
|
|
23
|
+
# Add your development secrets here
|
|
24
|
+
# api_key: dev_key
|
|
25
|
+
# database_password: dev_password
|
|
28
26
|
|
|
29
27
|
test:
|
|
30
28
|
secret_key_base: #{SecureRandom.hex(64)}
|
|
29
|
+
# Add your test secrets here
|
|
30
|
+
# api_key: test_key
|
|
31
31
|
|
|
32
|
-
# Do not keep production secrets in the repository,
|
|
33
|
-
# instead read values from the environment.
|
|
34
32
|
production:
|
|
35
33
|
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
|
34
|
+
# Use environment variables for production secrets
|
|
35
|
+
# api_key: <%= ENV["API_KEY"] %>
|
|
36
|
+
# database_password: <%= ENV["DATABASE_PASSWORD"] %>
|
|
36
37
|
YAML
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
39
|
+
File.write(secrets_path, default_content)
|
|
40
|
+
puts "✅ Created plain secrets.yml file at #{secrets_path}"
|
|
41
|
+
puts "⚠️ Remember to add production secrets as environment variables"
|
|
42
|
+
puts "⚠️ Never commit production secrets to version control"
|
|
61
43
|
end
|
|
62
44
|
end
|
|
63
45
|
|
|
64
|
-
desc "Edit secrets.yml file"
|
|
46
|
+
desc "Edit the plain YAML secrets.yml file"
|
|
65
47
|
task edit: :environment do
|
|
66
48
|
secrets_path = Rails.root.join("config/secrets.yml")
|
|
67
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
68
49
|
|
|
69
50
|
unless secrets_path.exist?
|
|
70
51
|
puts "Secrets file doesn't exist. Run 'rake secvault:setup' first."
|
|
71
52
|
exit 1
|
|
72
53
|
end
|
|
73
54
|
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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."
|
|
55
|
+
# Open the plain YAML file in editor
|
|
56
|
+
editor = ENV["EDITOR"] || "vi"
|
|
57
|
+
system("#{editor} #{secrets_path}")
|
|
58
|
+
puts "📝 Updated #{secrets_path}"
|
|
101
59
|
end
|
|
102
60
|
|
|
103
|
-
desc "Show secrets.yml content"
|
|
61
|
+
desc "Show the plain YAML secrets.yml content"
|
|
104
62
|
task show: :environment do
|
|
105
63
|
secrets_path = Rails.root.join("config/secrets.yml")
|
|
106
|
-
key_path = Rails.root.join("config/secrets.yml.key")
|
|
107
64
|
|
|
108
65
|
unless secrets_path.exist?
|
|
109
66
|
puts "Secrets file doesn't exist. Run 'rake secvault:setup' first."
|
|
110
67
|
exit 1
|
|
111
68
|
end
|
|
112
69
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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."
|
|
70
|
+
puts "📄 Contents of #{secrets_path}:"
|
|
71
|
+
puts "#{'=' * 50}"
|
|
72
|
+
puts File.read(secrets_path)
|
|
73
|
+
puts "#{'=' * 50}"
|
|
134
74
|
end
|
|
135
75
|
end
|
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
|
+
# ## Rake Tasks:
|
|
50
|
+
# rake secvault:setup # Create plain secrets.yml file
|
|
51
|
+
# rake secvault:edit # Edit secrets.yml file
|
|
52
|
+
# rake secvault:show # Display secrets.yml content
|
|
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
|
|
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:
|
|
4
|
+
version: 2.0.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: []
|
|
@@ -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: []
|