diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix index 40f921e92047..b4588fa67d80 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.databasePasswordFile != null; - message = "services.gitlab.databasePasswordFile must be set!"; + assertion = (cfg.databaseHost != "") -> (cfg.databasePasswordFile != null); + message = "When services.gitlab.databaseHost is customized, 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;