diff --git a/Gemfile b/Gemfile index af5ad62..6504235 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,10 @@ gem "webauthn", "~> 3.0" # Public Suffix List for domain parsing 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 gem "tzinfo-data", platforms: %i[ windows jruby ] diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 866fd65..ceb6fbe 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -100,7 +100,10 @@ module Admin params.require(:application).permit( :name, :slug, :app_type, :active, :redirect_uris, :description, :metadata, :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 diff --git a/app/models/application.rb b/app/models/application.rb index d4d71e9..c7c1d60 100644 --- a/app/models/application.rb +++ b/app/models/application.rb @@ -13,7 +13,7 @@ class Application < ApplicationRecord validates :app_type, presence: true, inclusion: { in: %w[oidc forward_auth] } 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 :landing_url, format: { with: URI::regexp(%w[http https]), allow_nil: true, message: "must be a valid URL" } diff --git a/config/environments/development.rb b/config/environments/development.rb index b8f693a..9c960cc 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -83,4 +83,14 @@ Rails.application.configure do # Apply autocorrection by RuboCop to files generated by `bin/rails 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 diff --git a/config/environments/production.rb b/config/environments/production.rb index b3330b5..894ac25 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -133,4 +133,18 @@ Rails.application.configure do # Skip DNS rebinding protection for the default health check endpoint. 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 diff --git a/config/environments/test.rb b/config/environments/test.rb index c2095b1..74a1d2d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -50,4 +50,8 @@ Rails.application.configure do # Raise error when a before_action's only/except options reference missing actions. 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 diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 0fbf399..a81456b 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -53,6 +53,7 @@ Rails.application.configure do # Additional security headers for WebAuthn # Required for WebAuthn to work properly policy.require_trusted_types_for :none + policy.report_uri = "/api/csp-violation-report" end # Start with CSP in report-only mode for testing diff --git a/docs/oidc-key-setup.md b/docs/oidc-key-setup.md index 34af249..7805132 100644 --- a/docs/oidc-key-setup.md +++ b/docs/oidc-key-setup.md @@ -44,10 +44,7 @@ Then set it securely: # Generate key bin/generate_oidc_key > oidc_private_key.pem -# Option A: Using kamal env push (Kamal 2.0+) -kamal env push OIDC_PRIVATE_KEY="$(cat oidc_private_key.pem)" - -# Option B: Add to .kamal/secrets +# Add to .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 | Feature | ENV Variable | Rails Credentials | @@ -145,31 +91,7 @@ kamal env push RAILS_MASTER_KEY="$(cat config/master.key)" ## Key Rotation (Advanced) -If you need to rotate keys (security incident, etc.): - -### 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 -``` +Todo ---