Add sentry, set csp reporting API
This commit is contained in:
4
Gemfile
4
Gemfile
@@ -37,6 +37,10 @@ gem "webauthn", "~> 3.0"
|
|||||||
# Public Suffix List for domain parsing
|
# Public Suffix List for domain parsing
|
||||||
gem "public_suffix", "~> 6.0"
|
gem "public_suffix", "~> 6.0"
|
||||||
|
|
||||||
|
# Error tracking and performance monitoring (optional, configured via SENTRY_DSN)
|
||||||
|
gem "sentry-ruby", "~> 5.18"
|
||||||
|
gem "sentry-rails", "~> 5.18"
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ module Admin
|
|||||||
params.require(:application).permit(
|
params.require(:application).permit(
|
||||||
:name, :slug, :app_type, :active, :redirect_uris, :description, :metadata,
|
:name, :slug, :app_type, :active, :redirect_uris, :description, :metadata,
|
||||||
:domain_pattern, :landing_url, headers_config: {}
|
:domain_pattern, :landing_url, headers_config: {}
|
||||||
)
|
).tap do |whitelisted|
|
||||||
|
# Remove client_secret from params if present (shouldn't be updated via form)
|
||||||
|
whitelisted.delete(:client_secret)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Application < ApplicationRecord
|
|||||||
validates :app_type, presence: true,
|
validates :app_type, presence: true,
|
||||||
inclusion: { in: %w[oidc forward_auth] }
|
inclusion: { in: %w[oidc forward_auth] }
|
||||||
validates :client_id, uniqueness: { allow_nil: true }
|
validates :client_id, uniqueness: { allow_nil: true }
|
||||||
validates :client_secret, presence: true, if: -> { oidc? && new_record? }
|
validates :client_secret, presence: true, on: :create, if: -> { oidc? }
|
||||||
validates :domain_pattern, presence: true, uniqueness: { case_sensitive: false }, if: :forward_auth?
|
validates :domain_pattern, presence: true, uniqueness: { case_sensitive: false }, if: :forward_auth?
|
||||||
validates :landing_url, format: { with: URI::regexp(%w[http https]), allow_nil: true, message: "must be a valid URL" }
|
validates :landing_url, format: { with: URI::regexp(%w[http https]), allow_nil: true, message: "must be a valid URL" }
|
||||||
|
|
||||||
|
|||||||
@@ -83,4 +83,14 @@ Rails.application.configure do
|
|||||||
|
|
||||||
# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
|
# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
|
||||||
# config.generators.apply_rubocop_autocorrect_after_generate!
|
# config.generators.apply_rubocop_autocorrect_after_generate!
|
||||||
|
|
||||||
|
# Sentry configuration for development
|
||||||
|
# Only enabled if SENTRY_DSN environment variable is set and explicitly enabled
|
||||||
|
if ENV["SENTRY_DSN"].present? && ENV["SENTRY_ENABLED_IN_DEVELOPMENT"] == "true"
|
||||||
|
config.sentry.enabled = true
|
||||||
|
|
||||||
|
# High sample rates for development debugging
|
||||||
|
config.sentry.traces_sample_rate = ENV.fetch("SENTRY_TRACES_SAMPLE_RATE", 0.5).to_f
|
||||||
|
config.sentry.profiles_sample_rate = ENV.fetch("SENTRY_PROFILES_SAMPLE_RATE", 0.2).to_f
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -133,4 +133,18 @@ Rails.application.configure do
|
|||||||
|
|
||||||
# Skip DNS rebinding protection for the default health check endpoint.
|
# Skip DNS rebinding protection for the default health check endpoint.
|
||||||
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
||||||
|
|
||||||
|
# Sentry configuration for production
|
||||||
|
# Only enabled if SENTRY_DSN environment variable is set
|
||||||
|
if ENV["SENTRY_DSN"].present?
|
||||||
|
config.sentry.enabled = true
|
||||||
|
|
||||||
|
# Performance monitoring: sample 20% of transactions for traces
|
||||||
|
# Adjust based on your traffic volume and Sentry plan limits
|
||||||
|
config.sentry.traces_sample_rate = ENV.fetch("SENTRY_TRACES_SAMPLE_RATE", 0.2).to_f
|
||||||
|
|
||||||
|
# Continuous profiling: disabled by default in production due to cost
|
||||||
|
# Enable temporarily for performance investigations if needed
|
||||||
|
config.sentry.profiles_sample_rate = ENV.fetch("SENTRY_PROFILES_SAMPLE_RATE", 0.0).to_f
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,4 +50,8 @@ Rails.application.configure do
|
|||||||
|
|
||||||
# Raise error when a before_action's only/except options reference missing actions.
|
# Raise error when a before_action's only/except options reference missing actions.
|
||||||
config.action_controller.raise_on_missing_callback_actions = true
|
config.action_controller.raise_on_missing_callback_actions = true
|
||||||
|
|
||||||
|
# Disable Sentry in test environment to avoid interference with tests
|
||||||
|
# Sentry can be explicitly enabled for integration testing if needed
|
||||||
|
config.sentry.enabled = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ Rails.application.configure do
|
|||||||
# Additional security headers for WebAuthn
|
# Additional security headers for WebAuthn
|
||||||
# Required for WebAuthn to work properly
|
# Required for WebAuthn to work properly
|
||||||
policy.require_trusted_types_for :none
|
policy.require_trusted_types_for :none
|
||||||
|
policy.report_uri = "/api/csp-violation-report"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start with CSP in report-only mode for testing
|
# Start with CSP in report-only mode for testing
|
||||||
|
|||||||
@@ -44,10 +44,7 @@ Then set it securely:
|
|||||||
# Generate key
|
# Generate key
|
||||||
bin/generate_oidc_key > oidc_private_key.pem
|
bin/generate_oidc_key > oidc_private_key.pem
|
||||||
|
|
||||||
# Option A: Using kamal env push (Kamal 2.0+)
|
# Add to .kamal/secrets
|
||||||
kamal env push OIDC_PRIVATE_KEY="$(cat oidc_private_key.pem)"
|
|
||||||
|
|
||||||
# Option B: Add to .kamal/secrets
|
|
||||||
echo "OIDC_PRIVATE_KEY=$(cat oidc_private_key.pem)" >> .kamal/secrets
|
echo "OIDC_PRIVATE_KEY=$(cat oidc_private_key.pem)" >> .kamal/secrets
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -60,57 +57,6 @@ bin/rails runner "puts OidcJwtService.send(:private_key).present? ? 'Key loaded'
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Option 2: Rails Credentials (Simpler but less flexible)
|
|
||||||
|
|
||||||
### 1. Generate the key
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openssl genrsa -out oidc_private_key.pem 2048
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Add to Rails credentials
|
|
||||||
|
|
||||||
```bash
|
|
||||||
EDITOR="nano" bin/rails credentials:edit
|
|
||||||
```
|
|
||||||
|
|
||||||
Add this section:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
oidc_private_key: |
|
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpAIBAAKCAQEAyZ0qaICMiLVWSFs+ef9Xok3fzy0p6k/7D5TQzmxf7C2vQG7s
|
|
||||||
2Odmi8iAHLoaUBaFj70qTbaconWyMr8s+ah+qZwrwolTLUe23VrceVXvInU57hBL
|
|
||||||
...
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important:** Use the `|` pipe character for multi-line, and indent the key content with 2 spaces.
|
|
||||||
|
|
||||||
### 3. Save and verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Verify credentials file
|
|
||||||
cat config/credentials.yml.enc # Should show encrypted data
|
|
||||||
|
|
||||||
# Test in console
|
|
||||||
bin/rails runner "puts OidcJwtService.send(:private_key).present? ? 'Key loaded' : 'Key missing'"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. For deployment
|
|
||||||
|
|
||||||
The `config/credentials.yml.enc` file is committed to git. You need to:
|
|
||||||
|
|
||||||
1. **Set RAILS_MASTER_KEY** env variable in production
|
|
||||||
2. Get the key from `config/master.key` (don't commit this!)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In Kamal
|
|
||||||
kamal env push RAILS_MASTER_KEY="$(cat config/master.key)"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Comparison
|
## Comparison
|
||||||
|
|
||||||
| Feature | ENV Variable | Rails Credentials |
|
| Feature | ENV Variable | Rails Credentials |
|
||||||
@@ -145,31 +91,7 @@ kamal env push RAILS_MASTER_KEY="$(cat config/master.key)"
|
|||||||
|
|
||||||
## Key Rotation (Advanced)
|
## Key Rotation (Advanced)
|
||||||
|
|
||||||
If you need to rotate keys (security incident, etc.):
|
Todo
|
||||||
|
|
||||||
### 1. Generate new key
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openssl genrsa -out oidc_private_key_new.pem 2048
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Add NEW key alongside old (dual-key setup)
|
|
||||||
|
|
||||||
This requires code changes to support multiple keys in JWKS. For now, rotation means:
|
|
||||||
|
|
||||||
**Warning:** Rotating the key will **invalidate all existing OIDC sessions**. Users will need to log in again.
|
|
||||||
|
|
||||||
### 3. Update OIDC_PRIVATE_KEY
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kamal env push OIDC_PRIVATE_KEY="$(cat oidc_private_key_new.pem)"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Restart application
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kamal deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user