From 7d2d012e3c894f2df56425e2eefb09bc4acdf6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Sat, 24 Jan 2026 12:07:00 +0100 Subject: [PATCH] fix: Support encryption keys via environment variables in managed mode (#762) The encryption initializer previously only supported environment variables in self-hosted mode. In managed mode, it expected encryption credentials to exist in Rails.application.credentials, which would cause boot failures if they were missing. This change updates the encryption configuration to support environment variables in both managed and self-hosted modes: - Environment variables (ACTIVE_RECORD_ENCRYPTION_*) now work in both modes - Priority: env vars > auto-generation (self-hosted only) > credentials - Updated documentation in .env.example and Helm chart README This allows managed mode deployments to provide encryption keys via environment variables instead of requiring Rails credentials. Co-authored-by: Claude --- .env.example | 3 +- charts/sure/README.md | 6 +-- .../initializers/active_record_encryption.rb | 42 +++++++++++-------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index 56568fc88..548bd1971 100644 --- a/.env.example +++ b/.env.example @@ -103,7 +103,8 @@ POSTHOG_HOST= # Active Record Encryption Keys (Optional) # These keys are used to encrypt sensitive data like API keys in the database. -# If not provided, they will be automatically generated based on your SECRET_KEY_BASE. +# For managed mode: Set these environment variables to provide encryption keys. +# For self-hosted mode: If not provided, they will be automatically generated based on your SECRET_KEY_BASE. # You can generate your own keys by running: rails db:encryption:init # ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY= # ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY= diff --git a/charts/sure/README.md b/charts/sure/README.md index a1dfb8903..6a004d153 100644 --- a/charts/sure/README.md +++ b/charts/sure/README.md @@ -533,9 +533,9 @@ ingress: secretName: finance-tls ``` -## Boot-required secrets (self-hosted) +## Boot-required secrets -In self-hosted mode the Rails initializer for Active Record Encryption loads on boot. To prevent boot crashes, ensure the following environment variables are present for ALL workloads (web, worker, migrate job/initContainer, CronJobs, and the SimpleFin backfill job): +The Rails initializer for Active Record Encryption loads on boot. To prevent boot crashes, ensure the following environment variables are present for ALL workloads (web, worker, migrate job/initContainer, CronJobs, and the SimpleFin backfill job): - `SECRET_KEY_BASE` - `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY` @@ -552,7 +552,7 @@ rails: enabled: true # set to false to skip injecting the three AR encryption env vars ``` -Note: Even if `simplefin.encryption.enabled=false`, the app initializer expects these env vars to exist in self-hosted mode. +Note: In self-hosted mode, if these env vars are not provided, they will be automatically generated from `SECRET_KEY_BASE`. In managed mode, these env vars must be explicitly provided via environment variables or Rails credentials. ## Advanced environment variable injection diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb index 30d771531..0c6da99ef 100644 --- a/config/initializers/active_record_encryption.rb +++ b/config/initializers/active_record_encryption.rb @@ -1,25 +1,33 @@ -# Auto-generate Active Record encryption keys for self-hosted instances -# This ensures encryption works out of the box without manual setup -if Rails.application.config.app_mode.self_hosted? && !Rails.application.credentials.active_record_encryption.present? - # Check if keys are provided via environment variables - primary_key = ENV["ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"] - deterministic_key = ENV["ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"] - key_derivation_salt = ENV["ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"] +# Configure Active Record encryption keys +# Priority order: +# 1. Environment variables (works for both managed and self-hosted modes) +# 2. Auto-generation from SECRET_KEY_BASE (self-hosted only, if credentials not present) +# 3. Rails credentials (fallback, handled in application.rb) - # If any key is missing, generate all of them based on SECRET_KEY_BASE - if primary_key.blank? || deterministic_key.blank? || key_derivation_salt.blank? - # Use SECRET_KEY_BASE as the seed for deterministic key generation - # This ensures keys are consistent across container restarts - secret_base = Rails.application.secret_key_base +# Check if keys are provided via environment variables +primary_key = ENV["ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY"] +deterministic_key = ENV["ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY"] +key_derivation_salt = ENV["ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT"] - # Generate deterministic keys from the secret base - primary_key = Digest::SHA256.hexdigest("#{secret_base}:primary_key")[0..63] - deterministic_key = Digest::SHA256.hexdigest("#{secret_base}:deterministic_key")[0..63] - key_derivation_salt = Digest::SHA256.hexdigest("#{secret_base}:key_derivation_salt")[0..63] - end +# If all environment variables are present, use them (works for both managed and self-hosted) +if primary_key.present? && deterministic_key.present? && key_derivation_salt.present? + Rails.application.config.active_record.encryption.primary_key = primary_key + Rails.application.config.active_record.encryption.deterministic_key = deterministic_key + Rails.application.config.active_record.encryption.key_derivation_salt = key_derivation_salt +elsif Rails.application.config.app_mode.self_hosted? && !Rails.application.credentials.active_record_encryption.present? + # For self-hosted instances without credentials or env vars, auto-generate keys + # Use SECRET_KEY_BASE as the seed for deterministic key generation + # This ensures keys are consistent across container restarts + secret_base = Rails.application.secret_key_base + + # Generate deterministic keys from the secret base + primary_key = Digest::SHA256.hexdigest("#{secret_base}:primary_key")[0..63] + deterministic_key = Digest::SHA256.hexdigest("#{secret_base}:deterministic_key")[0..63] + key_derivation_salt = Digest::SHA256.hexdigest("#{secret_base}:key_derivation_salt")[0..63] # Configure Active Record encryption Rails.application.config.active_record.encryption.primary_key = primary_key Rails.application.config.active_record.encryption.deterministic_key = deterministic_key Rails.application.config.active_record.encryption.key_derivation_salt = key_derivation_salt end +# If none of the above conditions are met, credentials from application.rb will be used