From 7648b4f8ba8020fda347ab7a18a24997a8cd6d39 Mon Sep 17 00:00:00 2001 From: talyz Date: Wed, 31 Jul 2019 17:14:52 +0200 Subject: nixos/gitlab: Fix missing ca_file for SMTP Work around upstream issue #790 by explicitly referencing the ca-certificates.crt file. --- nixos/modules/services/misc/gitlab.nix | 1 + 1 file changed, 1 insertion(+) (limited to 'nixos/modules/services/misc/gitlab.nix') diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 09c3a89d6a6..bd7bb6214bf 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -184,6 +184,7 @@ let domain: "${cfg.smtp.domain}", ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"} enable_starttls_auto: ${toString cfg.smtp.enableStartTLSAuto}, + ca_file: "/etc/ssl/certs/ca-certificates.crt", openssl_verify_mode: '${cfg.smtp.opensslVerifyMode}' } end -- cgit 1.4.1 From cbdf94c0f3ff3edba7452f30c3185e4a5b7965f6 Mon Sep 17 00:00:00 2001 From: talyz Date: Wed, 31 Jul 2019 18:09:27 +0200 Subject: nixos/gitlab: Add support for storing secrets in files Add support for storing secrets in files outside the nix store, since files in the nix store are world-readable and secrets therefore can't be stored safely there. The old string options are kept, since they can potentially be handy for testing purposes, but their descriptions now state that they shouldn't be used in production. The manual section is updated to use the file options rather than the string options and the tests now test both. --- nixos/modules/services/misc/gitlab.nix | 185 ++++++++++++++++++++++++--------- nixos/modules/services/misc/gitlab.xml | 56 +++------- nixos/tests/gitlab.nix | 42 ++------ 3 files changed, 159 insertions(+), 124 deletions(-) (limited to 'nixos/modules/services/misc/gitlab.nix') diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index bd7bb6214bf..40f921e9204 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -19,7 +19,6 @@ let adapter = "postgresql"; database = cfg.databaseName; host = cfg.databaseHost; - password = cfg.databasePassword; username = cfg.databaseUsername; encoding = "utf8"; pool = cfg.databasePool; @@ -66,13 +65,6 @@ let redisConfig.production.url = "redis://localhost:6379/"; - secretsConfig.production = { - secret_key_base = cfg.secrets.secret; - otp_key_base = cfg.secrets.otp; - db_key_base = cfg.secrets.db; - openid_connect_signing_key = cfg.secrets.jws; - }; - gitlabConfig = { # These are the default settings from config/gitlab.example.yml production = flip recursiveUpdate cfg.extraConfig { @@ -180,7 +172,7 @@ let address: "${cfg.smtp.address}", port: ${toString cfg.smtp.port}, ${optionalString (cfg.smtp.username != null) ''user_name: "${cfg.smtp.username}",''} - ${optionalString (cfg.smtp.password != null) ''password: "${cfg.smtp.password}",''} + ${optionalString (cfg.smtp.passwordFile != null) ''password: "@smtpPassword@",''} domain: "${cfg.smtp.domain}", ${optionalString (cfg.smtp.authentication != null) "authentication: :${cfg.smtp.authentication},"} enable_starttls_auto: ${toString cfg.smtp.enableStartTLSAuto}, @@ -249,9 +241,15 @@ in { description = "Gitlab database hostname."; }; - databasePassword = mkOption { - type = types.str; - description = "Gitlab database user password."; + databasePasswordFile = mkOption { + type = with types; nullOr path; + default = null; + description = '' + File containing the Gitlab database user password. + + This should be a string, not a nix path, since nix paths are + copied into the world-readable nix store. + ''; }; databaseName = mkOption { @@ -339,10 +337,15 @@ in { ''; }; - initialRootPassword = mkOption { - type = types.str; + initialRootPasswordFile = mkOption { + type = with types; nullOr path; + default = null; description = '' - Initial password of the root account if this is a new install. + File containing the initial password of the root account if + this is a new install. + + This should be a string, not a nix path, since nix paths are + copied into the world-readable nix store. ''; }; @@ -366,15 +369,20 @@ in { }; username = mkOption { - type = types.nullOr types.str; + type = with types; nullOr str; default = null; description = "Username of the SMTP server for Gitlab."; }; - password = mkOption { - type = types.nullOr types.str; + passwordFile = mkOption { + type = types.nullOr types.path; default = null; - description = "Password of the SMTP server for Gitlab."; + description = '' + File containing the password of the SMTP server for Gitlab. + + This should be a string, not a nix path, since nix paths + are copied into the world-readable nix store. + ''; }; domain = mkOption { @@ -384,7 +392,7 @@ in { }; authentication = mkOption { - type = types.nullOr types.str; + type = with types; nullOr str; default = null; description = "Authentitcation type to use, see http://api.rubyonrails.org/classes/ActionMailer/Base.html"; }; @@ -402,52 +410,69 @@ in { }; }; - secrets.secret = mkOption { - type = types.str; + secrets.secretFile = mkOption { + type = with types; nullOr path; + default = null; description = '' - The secret is used to encrypt variables in the DB. If - you change or lose this key you will be unable to access variables - stored in database. + A file containing the secret used to encrypt variables in + the DB. If you change or lose this key you will be unable to + access variables stored in database. Make sure the secret is at least 30 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.db = mkOption { - type = types.str; + secrets.dbFile = mkOption { + type = with types; nullOr path; + default = null; description = '' - The secret is used to encrypt variables in the DB. If - you change or lose this key you will be unable to access variables - stored in database. + A file containing the secret used to encrypt variables in + the DB. If you change or lose this key you will be unable to + access variables stored in database. Make sure the secret is at least 30 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.otp = mkOption { - type = types.str; + secrets.otpFile = mkOption { + type = with types; nullOr path; + default = null; description = '' - The secret is used to encrypt secrets for OTP tokens. If - you change or lose this key, users which have 2FA enabled for login - won't be able to login anymore. + A file containing the secret used to encrypt secrets for OTP + tokens. If you change or lose this key, users which have 2FA + enabled for login won't be able to login anymore. Make sure the secret is at least 30 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.jws = mkOption { - type = types.str; + secrets.jwsFile = mkOption { + type = with types; nullOr path; + default = null; description = '' - The secret is used to encrypt session keys. If you change or lose - this key, users will be disconnected. + A file containing the secret used to encrypt session + keys. If you change or lose this key, users will be + disconnected. Make sure the secret is an RSA private key in PEM format. You can generate one with openssl genrsa 2048 + + This should be a string, not a nix path, since nix paths are + copied into the world-readable nix store. ''; }; @@ -471,6 +496,33 @@ in { config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.initialRootPasswordFile != null; + message = "services.gitlab.initialRootPasswordFile must be set!"; + } + { + assertion = cfg.databasePasswordFile != null; + message = "services.gitlab.databasePasswordFile must be set!"; + } + { + assertion = cfg.secrets.secretFile != null; + message = "services.gitlab.secrets.secretFile must be set!"; + } + { + assertion = cfg.secrets.dbFile != null; + message = "services.gitlab.secrets.dbFile must be set!"; + } + { + assertion = cfg.secrets.otpFile != null; + message = "services.gitlab.secrets.otpFile must be set!"; + } + { + assertion = cfg.secrets.jwsFile != null; + message = "services.gitlab.secrets.jwsFile must be set!"; + } + ]; + environment.systemPackages = [ pkgs.git gitlab-rake gitlab-rails cfg.packages.gitlab-shell ]; # Redis is required for the sidekiq queue runner. @@ -529,13 +581,9 @@ in { "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" "L+ ${cfg.statePath}/config/gitlab.yml - - - - ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)}" - "L+ ${cfg.statePath}/config/database.yml - - - - ${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)}" - "L+ ${cfg.statePath}/config/secrets.yml - - - - ${pkgs.writeText "secrets.yml" (builtins.toJSON secretsConfig)}" "L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}" - "L+ ${cfg.statePath}/config/initializers/extra-gitlab.rb - - - - ${extraGitlabRb}" - ] ++ optional cfg.smtp.enable - "L+ ${cfg.statePath}/config/initializers/smtp_settings.rb - - - - ${smtpSettings}" ; + ]; systemd.services.gitlab-sidekiq = { after = [ "network.target" "redis.service" "gitlab.service" ]; @@ -632,13 +680,53 @@ in { ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db - ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret - ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${cfg.packages.gitlab-shell}/bin/install + ${optionalString cfg.smtp.enable '' + install -o ${cfg.user} -g ${cfg.group} -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb + ${optionalString (cfg.smtp.passwordFile != null) '' + smtp_password=$(<'${cfg.smtp.passwordFile}') + ${pkgs.replace}/bin/replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb' + ''} + ''} + + ( + umask u=rwx,g=,o= + + ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret + + if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then + rm '${cfg.statePath}/config/secrets.yml' + fi + + export secret="$(<'${cfg.secrets.secretFile}')" + export db="$(<'${cfg.secrets.dbFile}')" + export otp="$(<'${cfg.secrets.otpFile}')" + export jws="$(<'${cfg.secrets.jwsFile}')" + ${pkgs.jq}/bin/jq -n '{production: {secret_key_base: $ENV.secret, + otp_key_base: $ENV.db, + db_key_base: $ENV.otp, + openid_connect_signing_key: $ENV.jws}}' \ + > '${cfg.statePath}/config/secrets.yml' + + chown ${cfg.user}:${cfg.group} '${cfg.statePath}/config/secrets.yml' '${cfg.statePath}/gitlab_shell_secret' + ) + + export db_password="$(<'${cfg.databasePasswordFile}')" + + if [[ -z "$db_password" ]]; then + >&2 echo "Database password was an empty string!" + exit 1 + fi + + ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + '.production.password = $ENV.db_password' \ + >'${cfg.statePath}/config/database.yml' + chown ${cfg.user}:${cfg.group} '${cfg.statePath}/config/database.yml' + if ! test -e "${cfg.statePath}/db-created"; then if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.databasePassword}'" + ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '$db_password'" ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName} # enable required pg_trgm extension for gitlab @@ -654,8 +742,9 @@ in { ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:migrate if ! test -e "${cfg.statePath}/db-seeded"; then + initial_root_password="$(<'${cfg.initialRootPasswordFile}')" ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \ - GITLAB_ROOT_PASSWORD='${cfg.initialRootPassword}' GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' + GITLAB_ROOT_PASSWORD="$initial_root_password" GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded" fi diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml index 5ff570a442f..b6171a9a194 100644 --- a/nixos/modules/services/misc/gitlab.xml +++ b/nixos/modules/services/misc/gitlab.xml @@ -54,8 +54,8 @@ services.gitlab = { enable = true; - databasePassword = "eXaMpl3"; - initialRootPassword = "UseNixOS!"; + databasePasswordFile = "/var/keys/gitlab/db_password"; + initialRootPasswordFile = "/var/keys/gitlab/root_password"; https = true; host = "git.example.com"; port = 443; @@ -67,38 +67,10 @@ services.gitlab = { port = 25; }; secrets = { - db = "uPgq1gtwwHiatiuE0YHqbGa5lEIXH7fMsvuTNgdzJi8P0Dg12gibTzBQbq5LT7PNzcc3BP9P1snHVnduqtGF43PgrQtU7XL93ts6gqe9CBNhjtaqUwutQUDkygP5NrV6"; - secret = "devzJ0Tz0POiDBlrpWmcsjjrLaltyiAdS8TtgT9YNBOoUcDsfppiY3IXZjMVtKgXrFImIennFGOpPN8IkP8ATXpRgDD5rxVnKuTTwYQaci2NtaV1XxOQGjdIE50VGsR3"; - otp = "e1GATJVuS2sUh7jxiPzZPre4qtzGGaS22FR50Xs1TerRVdgI3CBVUi5XYtQ38W4xFeS4mDqi5cQjExE838iViSzCdcG19XSL6qNsfokQP9JugwiftmhmCadtsnHErBMI"; - jws = '' - -----BEGIN RSA PRIVATE KEY----- - MIIEpAIBAAKCAQEArrtx4oHKwXoqUbMNqnHgAklnnuDon3XG5LJB35yPsXKv/8GK - ke92wkI+s1Xkvsp8tg9BIY/7c6YK4SR07EWL+dB5qwctsWR2Q8z+/BKmTx9D99pm - hnsjuNIXTF7BXrx3RX6BxZpH5Vzzh9nCwWKT/JCFqtwH7afNGGL7aMf+hdaiUg/Q - SD05yRObioiO4iXDolsJOhrnbZvlzVHl1ZYxFJv0H6/Snc0BBA9Fl/3uj6ANpbjP - eXF1SnJCqT87bj46r5NdVauzaRxAsIfqHroHK4UZ98X5LjGQFGvSqTvyjPBS4I1i - s7VJU28ObuutHxIxSlH0ibn4HZqWmKWlTS652wIDAQABAoIBAGtPcUTTw2sJlR3x - 4k2wfAvLexkHNbZhBdKEa5JiO5mWPuLKwUiZEY2CU7Gd6csG3oqNWcm7/IjtC7dz - xV8p4yp8T4yq7vQIJ93B80NqTLtBD2QTvG2RCMJEPMzJUObWxkVmyVpLQyZo7KOd - KE/OM+aj94OUeEYLjRkSCScz1Gvq/qFG/nAy7KPCmN9JDHuhX26WHo2Rr1OnPNT/ - 7diph0bB9F3b8gjjNTqXDrpdAqVOgR/PsjEBz6DMY+bdyMIn87q2yfmMexxRofN6 - LulpzSaa6Yup8N8H6PzVO6KAkQuf1aQRj0sMwGk1IZEnj6I0KbuHIZkw21Nc6sf2 - ESFySDECgYEA1PnCNn5tmLnwe62Ttmrzl20zIS3Me1gUVJ1NTfr6+ai0I9iMYU21 - 5czuAjJPm9JKQF2vY8UAaCj2ZoObtHa/anb3xsCd8NXoM3iJq5JDoXI1ldz3Y+ad - U/bZUg1DLRvAniTuXmw9iOTwTwPxlDIGq5k+wG2Xmi1lk7zH8ezr9BMCgYEA0gfk - EhgcmPH8Z5cU3YYwOdt6HSJOM0OyN4k/5gnkv+HYVoJTj02gkrJmLr+mi1ugKj46 - 7huYO9TVnrKP21tmbaSv1dp5hS3letVRIxSloEtVGXmmdvJvBRzDWos+G+KcvADi - fFCz6w8v9NmO40CB7y/3SxTmSiSxDQeoi9LhDBkCgYEAsPgMWm25sfOnkY2NNUIv - wT8bAlHlHQT2d8zx5H9NttBpR3P0ShJhuF8N0sNthSQ7ULrIN5YGHYcUH+DyLAWU - TuomP3/kfa+xL7vUYb269tdJEYs4AkoppxBySoz8qenqpz422D0G8M6TpIS5Y5Qi - GMrQ6uLl21YnlpiCaFOfSQMCgYEAmZxj1kgEQmhZrnn1LL/D7czz1vMMNrpAUhXz - wg9iWmSXkU3oR1sDIceQrIhHCo2M6thwyU0tXjUft93pEQocM/zLDaGoVxtmRxxV - J08mg8IVD3jFoyFUyWxsBIDqgAKRl38eJsXvkO+ep3mm49Z+Ma3nM+apN3j2dQ0w - 3HLzXaECgYBFLMEAboVFwi5+MZjGvqtpg2PVTisfuJy2eYnPwHs+AXUgi/xRNFjI - YHEa7UBPb5TEPSzWImQpETi2P5ywcUYL1EbN/nqPWmjFnat8wVmJtV4sUpJhubF4 - Vqm9LxIWc1uQ1q1HDCejRIxIN3aSH+wgRS3Kcj8kCTIoXd1aERb04g== - -----END RSA PRIVATE KEY----- - ''; + dbFile = "/var/keys/gitlab/db"; + secretFile = "/var/keys/gitlab/secret"; + otpFile = "/var/keys/gitlab/otp"; + jwsFile = "/var/keys/gitlab/jws"; }; extraConfig = { gitlab = { @@ -113,12 +85,16 @@ services.gitlab = { - If you're setting up a new Gitlab instance, generate new secrets. You for - instance use tr -dc A-Za-z0-9 < /dev/urandom | head -c - 128 to generate a new secret. Gitlab encrypts sensitive data - stored in the database. If you're restoring an existing Gitlab instance, you - must specify the secrets secret from config/secrets.yml - located in your Gitlab state folder. + If you're setting up a new Gitlab instance, generate new + secrets. You for instance use tr -dc A-Za-z0-9 < + /dev/urandom | head -c 128 > /var/keys/gitlab/db to + generate a new db secret. Make sure the files can be read by, and + only by, the user specified by services.gitlab.user. Gitlab + encrypts sensitive data stored in the database. If you're restoring + an existing Gitlab instance, you must specify the secrets secret + from config/secrets.yml located in your Gitlab + state folder. diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix index ac733461932..5768e5081ca 100644 --- a/nixos/tests/gitlab.nix +++ b/nixos/tests/gitlab.nix @@ -29,44 +29,14 @@ import ./make-test.nix ({ pkgs, lib, ...} : with lib; { services.gitlab = { enable = true; - databasePassword = "dbPassword"; - inherit initialRootPassword; + databasePasswordFile = pkgs.writeText "dbPassword" "xo0daiF4"; + initialRootPasswordFile = pkgs.writeText "rootPassword" initialRootPassword; smtp.enable = true; secrets = { - secret = "secret"; - otp = "otpsecret"; - db = "dbsecret"; - - # nix-shell -p openssl --run "openssl genrsa 2048" - jws = '' - -----BEGIN RSA PRIVATE KEY----- - MIIEpAIBAAKCAQEA13/qEio76OWUtWO0WIz9lWnsTWOU8Esv4sQHDq9PCEFsLt21 - PAXrlWhLjjWcxGfsrDwnh7YErGHYL62BMSxMdFJolaknlQK/O/V8UETDe45VoHM+ - Znk270RfUcfYFgiihnXUZXVmL0om9TsQSk646wCcjCY9LxtxUyKNhvT7KjgYw2aX - z34aw7M+Js3T2p1TjZPSC82GtmtKkJEKFMi5EjprLTDE7EdcUzr9Xuw+kQ+gRm9k - 7FE+JQqSoprwE3Q0v2OAn3UhLMgg0gNFRnsc5l6IAshDzV+H22RPqKKlJjVjjfPY - 0TQSvYLVApigHbDPH0BoCXfjFfQazbbP3OUHrwIDAQABAoIBAQCMU+tkcMQaYIV5 - qLdjgkwO467QpivyXcOM8wF1eosIYTHFQvIlZ+WEoSmyLQ8shlADyBgls01Pw1c3 - lNAv6RzQEmmwKzpvOh61OKH+0whIiOMRXHoh2IUBQZCgfHYlwvGyhUAN4WjtGmhM - AG4XNTQNM5S9Xpkw97nP3Qwz+YskbbkrfqtCEVy9ro+4nhbjqPsuO3adbnkva4zR - cyurRhrHgHU6LPjn5NHnHH4qw2faY2oAsL8pmpkTbO5IqWDvOcbjNfjVPgVoq26O - bbaa1qs4nmc80qQgMjRPJef535xyf3eLsSlDvpf6O8sPrJzVR1zaqEqixpQCZDac - +kRiSBrhAoGBAOwHiq0PuyJh6VzBu7ybqX6+gF/wA4Jkwzx6mbfaBgurvU1aospp - kisIonAkxSbxllZMnjbkShZEdATYKeT9o5NEhnU4YnHfc5bJZbiWOZAzYGLcY7g8 - vDQ31pBItyY4pFgPbSpNlbUvUsoPVJ45RasRADDTNCzMzdjFQQXst2V9AoGBAOm7 - sSpzYfFPLEAhieAkuhtbsX58Boo46djiKVfzGftfp6F9aHTOfzGORU5jrZ16mSbS - qkkC6BEFrATX2051dzzXC89fWoJYALrsffE5I3KlKXsCAWSnCP1MMxOfH+Ls61Mr - 7pK/LKfvJt53mUH4jIdbmmFUDwbg18oBEH+x9PmbAoGAS/+JqXu9N67rIxDGUE6W - 3tacI0f2+U9Uhe67/DTZaXyc8YFTlXU0uWKIWy+bw5RaYeM9tlL/f/f+m2i25KK+ - vrZ7zNag7CWU5GJovGyykDnauTpZaYM03mN0VPT08/uc/zXIYqyknbhlIeaZynCK - fDB3LUF0NVCknz20WCIGU0kCgYEAkxY0ZXx61Dp4pFr2wwEZxQGs7uXpz64FKyEX - 12r6nMATY4Lh6y/Px0W6w5vis8lk+5Ny6cNUevHQ0LNuJS+yu6ywl+1vrbrnqroM - f3LvpcPeGLSoX8jl1VDQi7aFgG6LoKly1xJLbdsH4NPutB9PgBbbTghx9GgmI88L - rPA2M6UCgYBOmkYJocNgxg6B1/n4Tb9fN1Q/XuJrFDE6NxVUoke+IIyMPRH7FC3m - VMYzu+b7zTVJjaBb1cmJemxl/xajziWDofJYPefhdbOVU7HXtmJFY0IG3pVxU1zW - 3bmDj5QAtCUDpuuNa6GEIT0YR4+D/V7o3DmlZ0tVIwKJmVJoQ2f5dw== - -----END RSA PRIVATE KEY----- - ''; + secretFile = pkgs.writeText "secret" "Aig5zaic"; + otpFile = pkgs.writeText "otpsecret" "Riew9mue"; + dbFile = pkgs.writeText "dbsecret" "we2quaeZ"; + jwsFile = pkgs.runCommand "oidcKeyBase" {} "${pkgs.openssl}/bin/openssl genrsa 2048 > $out"; }; }; }; -- cgit 1.4.1 From b351454cac1eca1dc76e54e880e86d7c2bb21ffa Mon Sep 17 00:00:00 2001 From: talyz Date: Thu, 1 Aug 2019 16:59:24 +0200 Subject: nixos/gitlab: Use postgresql module options to provision local db Use the postgresql module to provision a local db (if databaseCreateLocally is true) instead of doing this locally. Switch to using the local unix socket for db connections by default; this is needed since dbs created by the postgresql module only support peer authentication. Instead of running the rake tasks db:schema:load, db:migrate and db:seed_fu, run gitlab:db:configure, which in turn runs these tasks when needed. Solves issue #53852 for gitlab. --- nixos/modules/services/misc/gitlab.nix | 129 +++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 53 deletions(-) (limited to 'nixos/modules/services/misc/gitlab.nix') diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 40f921e9204..b4588fa67d8 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -12,7 +12,6 @@ let gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket"; gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket"; pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url; - pgSuperUser = config.services.postgresql.superUser; databaseConfig = { production = { @@ -237,8 +236,11 @@ in { databaseHost = mkOption { type = types.str; - default = "127.0.0.1"; - description = "Gitlab database hostname."; + default = ""; + description = '' + Gitlab database hostname. An empty string means use + local unix socket connection. + ''; }; databasePasswordFile = mkOption { @@ -252,6 +254,17 @@ in { ''; }; + databaseCreateLocally = mkOption { + type = types.bool; + default = true; + description = '' + Whether a database should be automatically created on the + local host. Set this to false if you plan + on provisioning a local database yourself or use an external + one. + ''; + }; + databaseName = mkOption { type = types.str; default = "gitlab"; @@ -498,12 +511,16 @@ in { assertions = [ { - assertion = cfg.initialRootPasswordFile != null; - message = "services.gitlab.initialRootPasswordFile must be set!"; + assertion = cfg.databaseCreateLocally -> (cfg.user == cfg.databaseUsername); + message = "For local automatic database provisioning services.gitlab.user and services.gitlab.databaseUsername should be identical."; + } + { + assertion = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null); + message = "When services.gitlab.databaseHost is customized, services.gitlab.databasePasswordFile must be set!"; } { - assertion = cfg.databasePasswordFile != null; - message = "services.gitlab.databasePasswordFile must be set!"; + assertion = cfg.initialRootPasswordFile != null; + message = "services.gitlab.initialRootPasswordFile must be set!"; } { assertion = cfg.secrets.secretFile != null; @@ -527,8 +544,31 @@ in { # Redis is required for the sidekiq queue runner. services.redis.enable = mkDefault true; + # We use postgres as the main data store. - services.postgresql.enable = mkDefault true; + services.postgresql = optionalAttrs cfg.databaseCreateLocally { + enable = true; + ensureUsers = singleton { name = cfg.databaseUsername; }; + }; + # The postgresql module doesn't currently support concepts like + # objects owners and extensions; for now we tack on what's needed + # here. + systemd.services.postgresql.postStart = mkAfter (optionalString cfg.databaseCreateLocally '' + $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.databaseName}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${cfg.databaseName}" OWNER "${cfg.databaseUsername}"' + current_owner=$($PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.databaseName}'") + if [[ "$current_owner" != "${cfg.databaseUsername}" ]]; then + $PSQL -tAc 'ALTER DATABASE "${cfg.databaseName}" OWNER TO "${cfg.databaseUsername}"' + if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" ]]; then + echo "Reassigning ownership of database ${cfg.databaseName} to user ${cfg.databaseUsername} failed on last boot. Failing..." + exit 1 + fi + touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" + $PSQL "${cfg.databaseName}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.databaseUsername}\"" + rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.databaseName}" + fi + $PSQL '${cfg.databaseName}' -tAc "CREATE EXTENSION IF NOT EXISTS pg_trgm" + ''); + # Use postfix to send out mails. services.postfix.enable = mkDefault true; @@ -675,15 +715,15 @@ in { gnupg ]; preStart = '' - ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION - ${pkgs.sudo}/bin/sudo -u ${cfg.user} rm -rf ${cfg.statePath}/db/* - ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config - ${pkgs.sudo}/bin/sudo -u ${cfg.user} cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db + cp -f ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION + rm -rf ${cfg.statePath}/db/* + cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config + cp -rf --no-preserve=mode ${cfg.packages.gitlab}/share/gitlab/db/* ${cfg.statePath}/db - ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${cfg.packages.gitlab-shell}/bin/install + ${cfg.packages.gitlab-shell}/bin/install ${optionalString cfg.smtp.enable '' - install -o ${cfg.user} -g ${cfg.group} -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb + install -m u=rw ${smtpSettings} ${cfg.statePath}/config/initializers/smtp_settings.rb ${optionalString (cfg.smtp.passwordFile != null) '' smtp_password=$(<'${cfg.smtp.passwordFile}') ${pkgs.replace}/bin/replace-literal -e '@smtpPassword@' "$smtp_password" '${cfg.statePath}/config/initializers/smtp_settings.rb' @@ -695,6 +735,24 @@ in { ${pkgs.openssl}/bin/openssl rand -hex 32 > ${cfg.statePath}/gitlab_shell_secret + ${if cfg.databasePasswordFile != null then '' + export db_password="$(<'${cfg.databasePasswordFile}')" + + if [[ -z "$db_password" ]]; then + >&2 echo "Database password was an empty string!" + exit 1 + fi + + ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + '.production.password = $ENV.db_password' \ + >'${cfg.statePath}/config/database.yml' + '' + else '' + ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ + >'${cfg.statePath}/config/database.yml' + '' + } + if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then rm '${cfg.statePath}/config/secrets.yml' fi @@ -708,54 +766,19 @@ in { db_key_base: $ENV.otp, openid_connect_signing_key: $ENV.jws}}' \ > '${cfg.statePath}/config/secrets.yml' - - chown ${cfg.user}:${cfg.group} '${cfg.statePath}/config/secrets.yml' '${cfg.statePath}/gitlab_shell_secret' ) - export db_password="$(<'${cfg.databasePasswordFile}')" - - if [[ -z "$db_password" ]]; then - >&2 echo "Database password was an empty string!" - exit 1 - fi - - ${pkgs.jq}/bin/jq <${pkgs.writeText "database.yml" (builtins.toJSON databaseConfig)} \ - '.production.password = $ENV.db_password' \ - >'${cfg.statePath}/config/database.yml' - chown ${cfg.user}:${cfg.group} '${cfg.statePath}/config/database.yml' - - if ! test -e "${cfg.statePath}/db-created"; then - if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "CREATE ROLE ${cfg.databaseUsername} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '$db_password'" - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} ${config.services.postgresql.package}/bin/createdb --owner ${cfg.databaseUsername} ${cfg.databaseName} - - # enable required pg_trgm extension for gitlab - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql ${cfg.databaseName} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm" - fi - - ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:schema:load - - ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-created" - fi - - # Always do the db migrations just to be sure the database is up-to-date - ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${gitlab-rake}/bin/gitlab-rake db:migrate - - if ! test -e "${cfg.statePath}/db-seeded"; then - initial_root_password="$(<'${cfg.initialRootPasswordFile}')" - ${pkgs.sudo}/bin/sudo -u ${cfg.user} ${gitlab-rake}/bin/gitlab-rake db:seed_fu \ - GITLAB_ROOT_PASSWORD="$initial_root_password" GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' - ${pkgs.sudo}/bin/sudo -u ${cfg.user} touch "${cfg.statePath}/db-seeded" - fi + initial_root_password="$(<'${cfg.initialRootPasswordFile}')" + ${gitlab-rake}/bin/gitlab-rake gitlab:db:configure GITLAB_ROOT_PASSWORD="$initial_root_password" \ + GITLAB_ROOT_EMAIL='${cfg.initialRootEmail}' # We remove potentially broken links to old gitlab-shell versions rm -Rf ${cfg.statePath}/repositories/**/*.git/hooks - ${pkgs.sudo}/bin/sudo -u ${cfg.user} -H ${pkgs.git}/bin/git config --global core.autocrlf "input" + ${pkgs.git}/bin/git config --global core.autocrlf "input" ''; serviceConfig = { - PermissionsStartOnly = true; # preStart must be run as root Type = "simple"; User = cfg.user; Group = cfg.group; -- cgit 1.4.1 From 240649a5100fcb5ea6f18bc5bc87acc3ce216646 Mon Sep 17 00:00:00 2001 From: talyz Date: Wed, 7 Aug 2019 11:26:08 +0200 Subject: nixos/gitlab: Extract arbitrary secrets from extraConfig Adds the ability to make any parameter specified in extraConfig secret by defining it an attrset containing the attr _secret, which in turn is a path to a file containing the actual secret. --- nixos/modules/services/misc/gitlab.nix | 62 +++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) (limited to 'nixos/modules/services/misc/gitlab.nix') diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index b4588fa67d8..9cb4b0d6884 100644 --- a/nixos/modules/services/misc/gitlab.nix +++ b/nixos/modules/services/misc/gitlab.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, utils, ... }: # TODO: support non-postgresql @@ -492,16 +492,56 @@ in { extraConfig = mkOption { type = types.attrs; default = {}; - example = { - gitlab = { - default_projects_features = { - builds = false; + example = literalExample '' + { + gitlab = { + default_projects_features = { + builds = false; + }; + }; + omniauth = { + enabled = true; + auto_sign_in_with_provider = "openid_connect"; + allow_single_sign_on = ["openid_connect"]; + block_auto_created_users = false; + providers = [ + { + name = "openid_connect"; + label = "OpenID Connect"; + args = { + name = "openid_connect"; + scope = ["openid" "profile"]; + response_type = "code"; + issuer = "https://keycloak.example.com/auth/realms/My%20Realm"; + discovery = true; + client_auth_method = "query"; + uid_field = "preferred_username"; + client_options = { + identifier = "gitlab"; + secret = { _secret = "/var/keys/gitlab_oidc_secret"; }; + redirect_uri = "https://git.example.com/users/auth/openid_connect/callback"; + }; + }; + } + ]; }; }; - }; + ''; description = '' - Extra options to be merged into config/gitlab.yml as nix - attribute set. + Extra options to be added under + production in + config/gitlab.yml, as a nix attribute + set. + + Options containing secret data should be set to an attribute + set containing the attribute _secret - a + string pointing to a file containing the value the option + should be set to. See the example to get a better picture of + this: in the resulting + config/gitlab.yml file, the + production.omniauth.providers[0].args.client_options.secret + key will be set to the contents of the + /var/keys/gitlab_oidc_secret file. ''; }; }; @@ -620,7 +660,6 @@ in { "L+ /run/gitlab/shell-config.yml - - - - ${pkgs.writeText "config.yml" (builtins.toJSON gitlabShellConfig)}" - "L+ ${cfg.statePath}/config/gitlab.yml - - - - ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)}" "L+ ${cfg.statePath}/config/unicorn.rb - - - - ${./defaultUnicornConfig.rb}" "L+ ${cfg.statePath}/config/initializers/extra-gitlab.rb - - - - ${extraGitlabRb}" ]; @@ -753,6 +792,11 @@ in { '' } + ${utils.genJqSecretsReplacementSnippet + gitlabConfig + "${cfg.statePath}/config/gitlab.yml" + } + if [[ -h '${cfg.statePath}/config/secrets.yml' ]]; then rm '${cfg.statePath}/config/secrets.yml' fi -- cgit 1.4.1