secvault 1.0.3 → 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 +83 -0
- data/README.md +39 -161
- 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 +86 -3
- metadata +4 -6
- data/secvault-1.0.1.gem +0 -0
- data/secvault-1.0.2.gem +0 -0
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,88 @@
|
|
|
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
|
+
|
|
35
|
+
## [1.0.4] - 2025-09-22
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Comprehensive Rails 7.1 integration support
|
|
40
|
+
- New `Secvault.setup_rails_71_integration!` helper method for easy Rails 7.1 setup
|
|
41
|
+
- Enhanced documentation with Rails 7.1 integration guide
|
|
42
|
+
- Module-level documentation with usage examples and version compatibility
|
|
43
|
+
|
|
44
|
+
### Improved
|
|
45
|
+
|
|
46
|
+
- Better Rails 7.1 compatibility with automatic detection and setup
|
|
47
|
+
- Enhanced README with Rails 7.1 integration section
|
|
48
|
+
- Improved error handling and logging for Rails 7.1 integration
|
|
49
|
+
- More comprehensive inline documentation
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Refined automatic setup logic to avoid conflicts with Rails 7.1 native functionality
|
|
54
|
+
- Updated gemspec description to include Rails 7.1+ support
|
|
55
|
+
|
|
56
|
+
## [1.0.3] - 2025-09-22
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Rails 7.1 compatibility issues with native Rails::Secrets conflicts
|
|
61
|
+
- String path handling in parse method
|
|
62
|
+
- Zeitwerk constant name mismatch resolution
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- Manual setup method for Rails 7.1 (opt-in)
|
|
67
|
+
- Rails version detection for automatic setup decisions
|
|
68
|
+
- Only create Rails::Secrets alias for Rails 7.2+ to avoid conflicts
|
|
69
|
+
|
|
70
|
+
## [1.0.2] - 2025-09-22
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- Updated Rails dependency from >= 7.2.0 to >= 7.1.0 for broader compatibility
|
|
75
|
+
- Updated gem description to include Rails 7.1+ support
|
|
76
|
+
|
|
77
|
+
## [1.0.1] - 2025-09-22
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- Zeitwerk constant name mismatch in rails_secrets.rb
|
|
82
|
+
- Changed module definition from Rails::Secrets to Secvault::RailsSecrets
|
|
83
|
+
- Added Rails::Secrets alias for backward compatibility
|
|
84
|
+
- Resolved Zeitwerk::NameError when loading Rails applications
|
|
85
|
+
|
|
3
86
|
## [1.0.0] - 2025-09-22
|
|
4
87
|
|
|
5
88
|
### Added
|
data/README.md
CHANGED
|
@@ -1,209 +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
|
-
## Setup
|
|
33
|
-
|
|
34
|
-
### 1. Generate secrets file
|
|
35
|
-
|
|
36
|
-
Run the setup task to create your encrypted `secrets.yml`:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
$ rake secvault:setup
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
This will:
|
|
43
|
-
- Generate `config/secrets.yml.key` (keep this secure!)
|
|
44
|
-
- Create an encrypted `config/secrets.yml` with default content
|
|
45
|
-
- Remind you to add the key file to `.gitignore`
|
|
46
|
-
|
|
47
|
-
Alternatively, use the Rails generator:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
$ rails generate secvault:secrets
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### 2. Add key to .gitignore
|
|
54
|
-
|
|
55
|
-
Ensure your encryption key is not committed to version control:
|
|
56
|
-
|
|
57
17
|
```bash
|
|
58
|
-
|
|
18
|
+
bundle install
|
|
59
19
|
```
|
|
60
20
|
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
### Editing secrets
|
|
64
|
-
|
|
65
|
-
Edit your encrypted secrets file:
|
|
21
|
+
## Quick Start (Rails 7.2+)
|
|
66
22
|
|
|
67
23
|
```bash
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
This opens the decrypted file in your `$EDITOR`.
|
|
72
|
-
|
|
73
|
-
### Viewing secrets
|
|
74
|
-
|
|
75
|
-
View the decrypted content:
|
|
24
|
+
# 1. Create secrets.yml with rake task
|
|
25
|
+
rake secvault:setup
|
|
76
26
|
|
|
77
|
-
|
|
78
|
-
|
|
27
|
+
# 2. Edit secrets
|
|
28
|
+
rake secvault:edit
|
|
79
29
|
```
|
|
80
30
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
Secrets are automatically loaded into `Rails.application.secrets`:
|
|
84
|
-
|
|
31
|
+
**Usage in your app:**
|
|
85
32
|
```ruby
|
|
86
|
-
# In your Rails application
|
|
87
|
-
Rails.application.secrets.secret_key_base
|
|
88
33
|
Rails.application.secrets.api_key
|
|
89
34
|
Rails.application.secrets.database_password
|
|
90
35
|
```
|
|
91
36
|
|
|
92
|
-
|
|
93
|
-
|
|
37
|
+
**Example secrets.yml:**
|
|
94
38
|
```yaml
|
|
95
|
-
# config/secrets.yml (encrypted)
|
|
96
39
|
development:
|
|
97
|
-
|
|
98
|
-
api_key: dev_api_key
|
|
40
|
+
api_key: dev_key
|
|
99
41
|
database_password: dev_password
|
|
100
42
|
|
|
101
|
-
test:
|
|
102
|
-
secret_key_base: your_test_secret
|
|
103
|
-
api_key: test_api_key
|
|
104
|
-
database_password: test_password
|
|
105
|
-
|
|
106
43
|
production:
|
|
107
|
-
secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>
|
|
108
44
|
api_key: <%= ENV['API_KEY'] %>
|
|
109
45
|
database_password: <%= ENV['DATABASE_PASSWORD'] %>
|
|
110
46
|
```
|
|
111
47
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
export RAILS_SECRETS_KEY=your_encryption_key
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Production Deployment
|
|
121
|
-
|
|
122
|
-
### Option 1: Environment Variable
|
|
123
|
-
|
|
124
|
-
Set the encryption key as an environment variable:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
export RAILS_SECRETS_KEY=your_encryption_key
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Option 2: Key File
|
|
131
|
-
|
|
132
|
-
Securely copy `config/secrets.yml.key` to your production server.
|
|
133
|
-
|
|
134
|
-
### Docker
|
|
135
|
-
|
|
136
|
-
For Docker deployments, you can pass the key as an environment variable:
|
|
137
|
-
|
|
138
|
-
```dockerfile
|
|
139
|
-
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'] %>
|
|
140
52
|
```
|
|
141
53
|
|
|
142
|
-
##
|
|
143
|
-
|
|
144
|
-
| Task | Description |
|
|
145
|
-
|------|-------------|
|
|
146
|
-
| `rake secvault:setup` | Create encrypted secrets.yml and key |
|
|
147
|
-
| `rake secvault:edit` | Edit the encrypted secrets file |
|
|
148
|
-
| `rake secvault:show` | Display decrypted secrets content |
|
|
149
|
-
|
|
150
|
-
## Migration from Rails < 7.2
|
|
151
|
-
|
|
152
|
-
If you're upgrading from an older Rails version that had `secrets.yml`:
|
|
153
|
-
|
|
154
|
-
1. Install secvault: `bundle add secvault`
|
|
155
|
-
2. Encrypt existing secrets: `rake secvault:setup`
|
|
156
|
-
3. Copy your existing secrets content using `rake secvault:edit`
|
|
157
|
-
4. Remove the old plain-text `config/secrets.yml`
|
|
54
|
+
## Rails 7.1 Integration
|
|
158
55
|
|
|
159
|
-
|
|
56
|
+
Test Secvault in Rails 7.1 before upgrading to 7.2+:
|
|
160
57
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- ✅ **Use strong, unique keys** for each environment
|
|
165
|
-
- ✅ **Limit access** to key files in production
|
|
166
|
-
|
|
167
|
-
## Troubleshooting
|
|
168
|
-
|
|
169
|
-
### Missing Key Error
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
Missing encryption key to decrypt secrets.yml
|
|
58
|
+
```ruby
|
|
59
|
+
# config/initializers/secvault.rb
|
|
60
|
+
Secvault.setup_rails_71_integration!
|
|
173
61
|
```
|
|
174
62
|
|
|
175
|
-
|
|
63
|
+
This replaces Rails.application.secrets with Secvault functionality. Your existing Rails 7.1 code works unchanged:
|
|
176
64
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
65
|
+
```ruby
|
|
66
|
+
Rails.application.secrets.api_key # ✅ Works
|
|
67
|
+
Rails.application.secrets.oauth_settings # ✅ Works
|
|
68
|
+
Rails::Secrets.parse_default # ✅ Enhanced functionality
|
|
181
69
|
```
|
|
182
70
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### File Not Found
|
|
71
|
+
## Available Commands
|
|
186
72
|
|
|
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
|
|
187
77
|
```
|
|
188
|
-
Secrets file doesn't exist
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
**Solution**: Run `rake secvault:setup` to create the secrets file.
|
|
192
|
-
|
|
193
|
-
## Development
|
|
194
78
|
|
|
195
|
-
|
|
79
|
+
## Security
|
|
196
80
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
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
|
|
202
84
|
|
|
203
85
|
## License
|
|
204
86
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
## Code of Conduct
|
|
208
|
-
|
|
209
|
-
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,10 +11,49 @@ require_relative "secvault/version"
|
|
|
12
11
|
loader = Zeitwerk::Loader.for_gem
|
|
13
12
|
loader.setup
|
|
14
13
|
|
|
14
|
+
# Secvault - Simple secrets management for Rails
|
|
15
|
+
#
|
|
16
|
+
# Secvault restores the classic Rails secrets.yml functionality that was removed
|
|
17
|
+
# in Rails 7.2, using simple, plain YAML files for environment-specific secrets
|
|
18
|
+
# management.
|
|
19
|
+
#
|
|
20
|
+
# ## Rails Version Support:
|
|
21
|
+
# - Rails 7.1: Requires manual setup (see Rails 7.1 integration guide)
|
|
22
|
+
# - Rails 7.2+: Automatic setup, drop-in replacement for removed functionality
|
|
23
|
+
# - Rails 8.0+: Full compatibility
|
|
24
|
+
#
|
|
25
|
+
# ## Rails 7.1 Integration:
|
|
26
|
+
# For Rails 7.1 apps, add this initializer to override native Rails::Secrets:
|
|
27
|
+
#
|
|
28
|
+
# # config/initializers/secvault.rb
|
|
29
|
+
# module Rails
|
|
30
|
+
# remove_const(:Secrets) if defined?(Secrets)
|
|
31
|
+
# Secrets = Secvault::RailsSecrets
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# Rails.application.config.after_initialize do
|
|
35
|
+
# secrets_path = Rails.root.join("config/secrets.yml")
|
|
36
|
+
# if secrets_path.exist?
|
|
37
|
+
# loaded_secrets = Rails::Secrets.parse([secrets_path], env: Rails.env)
|
|
38
|
+
# secrets_object = ActiveSupport::OrderedOptions.new
|
|
39
|
+
# secrets_object.merge!(loaded_secrets)
|
|
40
|
+
# Rails.application.define_singleton_method(:secrets) { secrets_object }
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# ## Usage:
|
|
45
|
+
# Rails.application.secrets.api_key
|
|
46
|
+
# Rails.application.secrets.oauth_settings[:google_client_id]
|
|
47
|
+
# Rails::Secrets.parse_default(env: 'development')
|
|
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
|
+
#
|
|
54
|
+
# @see https://github.com/unnitallman/secvault
|
|
15
55
|
module Secvault
|
|
16
56
|
class Error < StandardError; end
|
|
17
|
-
class MissingKeyError < Error; end
|
|
18
|
-
class InvalidKeyError < Error; end
|
|
19
57
|
|
|
20
58
|
extend self
|
|
21
59
|
|
|
@@ -25,6 +63,51 @@ module Secvault
|
|
|
25
63
|
require "secvault/railtie"
|
|
26
64
|
require "secvault/rails_secrets"
|
|
27
65
|
end
|
|
66
|
+
|
|
67
|
+
# Helper method to set up Secvault for Rails 7.1 applications
|
|
68
|
+
# This provides an easy way to integrate Secvault into Rails 7.1 apps
|
|
69
|
+
# that still have native Rails::Secrets functionality.
|
|
70
|
+
#
|
|
71
|
+
# Usage in an initializer:
|
|
72
|
+
# Secvault.setup_rails_71_integration!
|
|
73
|
+
#
|
|
74
|
+
# This will:
|
|
75
|
+
# 1. Override native Rails::Secrets with Secvault implementation
|
|
76
|
+
# 2. Replace Rails.application.secrets with Secvault-powered functionality
|
|
77
|
+
# 3. Load secrets from config/secrets.yml automatically
|
|
78
|
+
def setup_rails_71_integration!
|
|
79
|
+
# Override native Rails::Secrets
|
|
80
|
+
if defined?(Rails::Secrets)
|
|
81
|
+
Rails.send(:remove_const, :Secrets)
|
|
82
|
+
end
|
|
83
|
+
Rails.const_set(:Secrets, Secvault::RailsSecrets)
|
|
84
|
+
|
|
85
|
+
# Set up Rails.application.secrets replacement
|
|
86
|
+
Rails.application.config.after_initialize do
|
|
87
|
+
secrets_path = Rails.root.join("config/secrets.yml")
|
|
88
|
+
|
|
89
|
+
if secrets_path.exist?
|
|
90
|
+
# Load secrets using Secvault
|
|
91
|
+
loaded_secrets = Rails::Secrets.parse([secrets_path], env: Rails.env)
|
|
92
|
+
|
|
93
|
+
# Create ActiveSupport::OrderedOptions object for compatibility
|
|
94
|
+
secrets_object = ActiveSupport::OrderedOptions.new
|
|
95
|
+
secrets_object.merge!(loaded_secrets)
|
|
96
|
+
|
|
97
|
+
# Replace Rails.application.secrets
|
|
98
|
+
Rails.application.define_singleton_method(:secrets) do
|
|
99
|
+
secrets_object
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Log integration success (except in production)
|
|
103
|
+
unless Rails.env.production?
|
|
104
|
+
Rails.logger&.info "[Secvault] Rails 7.1 integration complete. Loaded #{loaded_secrets.keys.size} secret keys."
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
Rails.logger&.warn "[Secvault] No secrets.yml file found at #{secrets_path}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
28
111
|
end
|
|
29
112
|
|
|
30
113
|
Secvault.install! if defined?(Rails)
|
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: []
|
|
@@ -62,8 +62,6 @@ files:
|
|
|
62
62
|
- lib/secvault/secrets_helper.rb
|
|
63
63
|
- lib/secvault/tasks.rake
|
|
64
64
|
- lib/secvault/version.rb
|
|
65
|
-
- secvault-1.0.1.gem
|
|
66
|
-
- secvault-1.0.2.gem
|
|
67
65
|
- sig/secvault.rbs
|
|
68
66
|
homepage: https://github.com/unnitallman/secvault
|
|
69
67
|
licenses:
|
|
@@ -91,5 +89,5 @@ requirements: []
|
|
|
91
89
|
rubygems_version: 3.5.10
|
|
92
90
|
signing_key:
|
|
93
91
|
specification_version: 4
|
|
94
|
-
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+
|
|
95
93
|
test_files: []
|
data/secvault-1.0.1.gem
DELETED
|
Binary file
|
data/secvault-1.0.2.gem
DELETED
|
Binary file
|