From 828299e71c127d4406de3add93afa5b28bdb116a Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 17 Apr 2024 05:19:02 -0400
Subject: [PATCH] Enable AR Encryption (#29831)

---
 .env.development                              |  4 +++
 .env.test                                     |  5 ++++
 .github/workflows/test-ruby.yml               |  3 +++
 .gitignore                                    |  1 -
 Dockerfile                                    |  7 ++++-
 .../initializers/active_record_encryption.rb  | 26 +++++++++++++++++++
 lib/tasks/mastodon.rake                       |  9 +++++++
 7 files changed, 53 insertions(+), 2 deletions(-)
 create mode 100644 .env.development
 create mode 100644 config/initializers/active_record_encryption.rb

diff --git a/.env.development b/.env.development
new file mode 100644
index 000000000..0330da837
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,4 @@
+# Required by ActiveRecord encryption feature
+ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
+ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
+ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
diff --git a/.env.test b/.env.test
index 2f8c1afd6..9e6abea5c 100644
--- a/.env.test
+++ b/.env.test
@@ -3,3 +3,8 @@ NODE_ENV=production
 # Federation
 LOCAL_DOMAIN=cb6e6126.ngrok.io
 LOCAL_HTTPS=true
+
+# Required by ActiveRecord encryption feature
+ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
+ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
+ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 172b5271d..3a78f8b43 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -28,6 +28,9 @@ jobs:
     env:
       RAILS_ENV: ${{ matrix.mode }}
       BUNDLE_WITH: ${{ matrix.mode }}
+      ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: precompile_placeholder
+      ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: precompile_placeholder
+      ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: precompile_placeholder
       OTP_SECRET: precompile_placeholder
       SECRET_KEY_BASE: precompile_placeholder
 
diff --git a/.gitignore b/.gitignore
index c5af8eb67..2f94b751a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,6 @@
 /public/packs-test
 .env
 .env.production
-.env.development
 /node_modules/
 /build/
 
diff --git a/Dockerfile b/Dockerfile
index 1b007930e..43bc24295 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -205,7 +205,12 @@ ARG TARGETPLATFORM
 
 RUN \
 # Use Ruby on Rails to create Mastodon assets
-  OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile; \
+  ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=precompile_placeholder \
+  ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=precompile_placeholder \
+  ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=precompile_placeholder \
+  OTP_SECRET=precompile_placeholder \
+  SECRET_KEY_BASE=precompile_placeholder \
+  bundle exec rails assets:precompile; \
 # Cleanup temporary files
   rm -fr /opt/mastodon/tmp;
 
diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb
new file mode 100644
index 000000000..f99585b4a
--- /dev/null
+++ b/config/initializers/active_record_encryption.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+%w(
+  ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
+  ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
+  ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
+).each do |key|
+  ENV.fetch(key) do
+    raise <<~MESSAGE
+
+      The ActiveRecord encryption feature requires that these variables are set:
+
+        - ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
+        - ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
+        - ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
+
+      Run `bin/rails db:encryption:init` to generate values and then assign the environment variables.
+    MESSAGE
+  end
+end
+
+Rails.application.configure do
+  config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY')
+  config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT')
+  config.active_record.encryption.primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY')
+end
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index cb364b302..2822f2eeb 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -36,6 +36,15 @@ namespace :mastodon do
         env[key] = SecureRandom.hex(64)
       end
 
+      # Required by ActiveRecord encryption feature
+      %w(
+        ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
+        ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
+        ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
+      ).each do |key|
+        env[key] = SecureRandom.alphanumeric(32)
+      end
+
       vapid_key = Webpush.generate_key
 
       env['VAPID_PRIVATE_KEY'] = vapid_key.private_key