nixos/gitlab: add activeRecord key files

GitLab 17.11 started using rails activeRecord encryption for some values.
Introduce new key files. This is breaking for unstable/25.05.

Also add a test to prevent this from happening unnoticed in the future.

For the future there should also be an option to set multiple activeRecord
keys for rotation.
This commit is contained in:
Leona Maroni 2025-05-07 17:29:50 +02:00
parent 2e2cab7847
commit a95a530883
No known key found for this signature in database
GPG key ID: D5B08ADFC75E3605
3 changed files with 76 additions and 2 deletions

View file

@ -333,6 +333,8 @@
- The behavior of the `networking.nat.externalIP` and `networking.nat.externalIPv6` options has been changed. `networking.nat.forwardPorts` now only forwards packets destined for the specified IP addresses.
- `services.gitlab` now requires the setting of `activeRecordPrimaryKeyFile`, `activeRecordDeterministicKeyFile`, `activeRecordSaltFile` as GitLab introduced Rails ActiveRecord encryption.
- `python3Packages.bpycv` has been removed due to being incompatible with Blender 4 and unmaintained.
- `python3Packages.jaeger-client` was removed because it was deprecated upstream. [OpenTelemetry](https://opentelemetry.io) is the recommended replacement.

View file

@ -907,6 +907,50 @@ in
'';
};
secrets.activeRecordPrimaryKeyFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
A file containing the secret used to encrypt some rails data
in the DB. This should not be the same as `services.gitlab.secrets.activeRecordDeterministicKeyFile`!
Make sure the secret is at ideally 32 characters and all random,
no regular words or you'll be exposed to dictionary attacks.
This should be a string, not a nix path, since nix paths are
copied into the world-readable nix store.
'';
};
secrets.activeRecordDeterministicKeyFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
A file containing the secret used to encrypt some rails data in a deterministic way
in the DB. This should not be the same as `services.gitlab.secrets.activeRecordPrimaryKeyFile`!
Make sure the secret is at ideally 32 characters and all random,
no regular words or you'll be exposed to dictionary attacks.
This should be a string, not a nix path, since nix paths are
copied into the world-readable nix store.
'';
};
secrets.activeRecordSaltFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
A file containing the salt for active record encryption in the DB.
Make sure the secret is at ideally 32 characters and all random,
no regular words or you'll be exposed to dictionary attacks.
This should be a string, not a nix path, since nix paths are
copied into the world-readable nix store.
'';
};
extraShellConfig = mkOption {
type = types.attrs;
default = { };
@ -1180,6 +1224,18 @@ in
assertion = cfg.secrets.jwsFile != null;
message = "services.gitlab.secrets.jwsFile must be set!";
}
{
assertion = cfg.secrets.activeRecordPrimaryKeyFile != null;
message = "services.gitlab.secrets.activeRecordPrimaryKeyFile must be set!";
}
{
assertion = cfg.secrets.activeRecordDeterministicKeyFile != null;
message = "services.gitlab.secrets.activeRecordDeterministicKeyFile must be set!";
}
{
assertion = cfg.secrets.activeRecordSaltFile != null;
message = "services.gitlab.secrets.activeRecordSaltFile must be set!";
}
{
assertion = versionAtLeast postgresqlPackage.version "14.9";
message = "PostgreSQL >= 14.9 is required to run GitLab 17. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading";
@ -1480,11 +1536,17 @@ in
db="$(<'${cfg.secrets.dbFile}')"
otp="$(<'${cfg.secrets.otpFile}')"
jws="$(<'${cfg.secrets.jwsFile}')"
export secret db otp jws
arprimary="$(<'${cfg.secrets.activeRecordPrimaryKeyFile}')"
ardeterministic="$(<'${cfg.secrets.activeRecordDeterministicKeyFile}')"
arsalt="$(<'${cfg.secrets.activeRecordSaltFile}')"
export secret db otp jws arprimary ardeterministic arsalt
jq -n '{production: {secret_key_base: $ENV.secret,
otp_key_base: $ENV.otp,
db_key_base: $ENV.db,
openid_connect_signing_key: $ENV.jws}}' \
openid_connect_signing_key: $ENV.jws,
active_record_encryption_primary_key: $ENV.arprimary,
active_record_encryption_deterministic_key: $ENV.ardeterministic,
active_record_encryption_key_derivation_salt: $ENV.arsalt}}' \
> '${cfg.statePath}/config/secrets.yml'
)

View file

@ -106,6 +106,9 @@ in
otpFile = pkgs.writeText "otpsecret" "Riew9mue";
dbFile = pkgs.writeText "dbsecret" "we2quaeZ";
jwsFile = pkgs.runCommand "oidcKeyBase" { } "${pkgs.openssl}/bin/openssl genrsa 2048 > $out";
activeRecordPrimaryKeyFile = pkgs.writeText "arprimary" "vsaYPZjTRxcbG7W6gNr95AwBmzFUd4Eu";
activeRecordDeterministicKeyFile = pkgs.writeText "ardeterministic" "kQarv9wb2JVP7XzLTh5f6DFcMHms4nEC";
activeRecordSaltFile = pkgs.writeText "arsalt" "QkgR9CfFU3MXEWGqa7LbP24AntK5ZeYw";
};
registry = {
@ -477,6 +480,9 @@ in
gitlab.start()
''
+ waitForServices
+ ''
gitlab.succeed("cp /var/gitlab/state/config/secrets.yml /root/gitlab-secrets.yml")
''
+ test true
+ ''
gitlab.systemctl("start gitlab-backup.service")
@ -496,5 +502,9 @@ in
gitlab.systemctl("start gitlab.target")
''
+ waitForServices
+ ''
with subtest("Check that no secrets were auto-generated as these would be non-persistent"):
gitlab.succeed("diff -u /root/gitlab-secrets.yml /var/gitlab/state/config/secrets.yml")
''
+ test false;
}