nixpkgs/nixos/modules/services/misc/gitea.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

727 lines
26 KiB
Nix
Raw Normal View History

2017-10-18 04:16:46 +00:00
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.gitea;
opt = options.services.gitea;
exe = lib.getExe cfg.package;
pg = config.services.postgresql;
useMysql = cfg.database.type == "mysql";
usePostgresql = cfg.database.type == "postgres";
useSqlite = cfg.database.type == "sqlite3";
format = pkgs.formats.ini { };
2017-10-18 04:16:46 +00:00
configFile = pkgs.writeText "app.ini" ''
APP_NAME = ${cfg.appName}
RUN_USER = ${cfg.user}
RUN_MODE = prod
WORK_PATH = ${cfg.stateDir}
2017-10-18 04:16:46 +00:00
2020-04-23 21:53:18 +00:00
${generators.toINI {} cfg.settings}
${optionalString (cfg.extraConfig != null) cfg.extraConfig}
2017-10-18 04:16:46 +00:00
'';
in
{
imports = [
(mkRenamedOptionModule [ "services" "gitea" "cookieSecure" ] [ "services" "gitea" "settings" "session" "COOKIE_SECURE" ])
(mkRenamedOptionModule [ "services" "gitea" "disableRegistration" ] [ "services" "gitea" "settings" "service" "DISABLE_REGISTRATION" ])
(mkRenamedOptionModule [ "services" "gitea" "domain" ] [ "services" "gitea" "settings" "server" "DOMAIN" ])
(mkRenamedOptionModule [ "services" "gitea" "httpAddress" ] [ "services" "gitea" "settings" "server" "HTTP_ADDR" ])
(mkRenamedOptionModule [ "services" "gitea" "httpPort" ] [ "services" "gitea" "settings" "server" "HTTP_PORT" ])
(mkRenamedOptionModule [ "services" "gitea" "log" "level" ] [ "services" "gitea" "settings" "log" "LEVEL" ])
(mkRenamedOptionModule [ "services" "gitea" "log" "rootPath" ] [ "services" "gitea" "settings" "log" "ROOT_PATH" ])
(mkRenamedOptionModule [ "services" "gitea" "rootUrl" ] [ "services" "gitea" "settings" "server" "ROOT_URL" ])
(mkRenamedOptionModule [ "services" "gitea" "ssh" "clonePort" ] [ "services" "gitea" "settings" "server" "SSH_PORT" ])
(mkRenamedOptionModule [ "services" "gitea" "staticRootPath" ] [ "services" "gitea" "settings" "server" "STATIC_ROOT_PATH" ])
(mkChangedOptionModule [ "services" "gitea" "enableUnixSocket" ] [ "services" "gitea" "settings" "server" "PROTOCOL" ] (
config: if config.services.gitea.enableUnixSocket then "http+unix" else "http"
))
(mkRemovedOptionModule [ "services" "gitea" "ssh" "enable" ] "services.gitea.ssh.enable has been migrated into freeform setting services.gitea.settings.server.DISABLE_SSH. Keep in mind that the setting is inverted")
];
2017-10-18 04:16:46 +00:00
options = {
services.gitea = {
enable = mkOption {
default = false;
type = types.bool;
description = "Enable Gitea Service.";
2017-10-18 04:16:46 +00:00
};
package = mkPackageOption pkgs "gitea" { };
2017-10-18 04:16:46 +00:00
useWizard = mkOption {
default = false;
type = types.bool;
description = "Do not generate a configuration and use gitea' installation wizard instead. The first registered user will be administrator.";
2017-10-18 04:16:46 +00:00
};
stateDir = mkOption {
default = "/var/lib/gitea";
type = types.str;
description = "Gitea data directory.";
};
customDir = mkOption {
default = "${cfg.stateDir}/custom";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/custom"'';
type = types.str;
description = "Gitea custom directory. Used for config, custom templates and other options.";
2017-10-18 04:16:46 +00:00
};
user = mkOption {
type = types.str;
default = "gitea";
description = "User account under which gitea runs.";
2017-10-18 04:16:46 +00:00
};
group = mkOption {
type = types.str;
default = "gitea";
description = "Group under which gitea runs.";
};
2017-10-18 04:16:46 +00:00
database = {
type = mkOption {
type = types.enum [ "sqlite3" "mysql" "postgres" ];
example = "mysql";
default = "sqlite3";
description = "Database engine to use.";
2017-10-18 04:16:46 +00:00
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Database host address.";
2017-10-18 04:16:46 +00:00
};
port = mkOption {
2021-06-18 15:27:06 +00:00
type = types.port;
default = if usePostgresql then pg.settings.port else 3306;
defaultText = literalExpression ''
if config.${opt.database.type} != "postgresql"
then 3306
else 5432
'';
description = "Database host port.";
2017-10-18 04:16:46 +00:00
};
name = mkOption {
type = types.str;
default = "gitea";
description = "Database name.";
2017-10-18 04:16:46 +00:00
};
user = mkOption {
type = types.str;
default = "gitea";
description = "Database user.";
2017-10-18 04:16:46 +00:00
};
password = mkOption {
type = types.str;
default = "";
description = ''
2017-10-18 04:16:46 +00:00
The password corresponding to {option}`database.user`.
Warning: this is stored in cleartext in the Nix store!
Use {option}`database.passwordFile` instead.
'';
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/gitea-dbpassword";
description = ''
2017-10-18 04:16:46 +00:00
A file containing the password corresponding to
{option}`database.user`.
'';
};
socket = mkOption {
type = types.nullOr types.path;
default = if (cfg.database.createDatabase && usePostgresql) then "/run/postgresql" else if (cfg.database.createDatabase && useMysql) then "/run/mysqld/mysqld.sock" else null;
defaultText = literalExpression "null";
example = "/run/mysqld/mysqld.sock";
description = "Path to the unix socket file to use for authentication.";
};
2017-10-18 04:16:46 +00:00
path = mkOption {
type = types.str;
default = "${cfg.stateDir}/data/gitea.db";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/gitea.db"'';
description = "Path to the sqlite3 database file.";
2017-10-18 04:16:46 +00:00
};
createDatabase = mkOption {
type = types.bool;
default = true;
description = "Whether to create a local database automatically.";
};
2017-10-18 04:16:46 +00:00
};
dump = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable a timer that runs gitea dump to generate backup-files of the
current gitea database and repositories.
'';
};
interval = mkOption {
type = types.str;
default = "04:31";
example = "hourly";
description = ''
Run a gitea dump at this interval. Runs by default at 04:31 every day.
The format is described in
{manpage}`systemd.time(7)`.
'';
};
2020-07-30 21:04:23 +00:00
backupDir = mkOption {
type = types.str;
default = "${cfg.stateDir}/dump";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/dump"'';
description = "Path to the dump files.";
2020-07-30 21:04:23 +00:00
};
type = mkOption {
type = types.enum [ "zip" "rar" "tar" "sz" "tar.gz" "tar.xz" "tar.bz2" "tar.br" "tar.lz4" "tar.zst" ];
default = "zip";
description = "Archive format used to store the dump file.";
};
file = mkOption {
type = types.nullOr types.str;
default = null;
description = "Filename to be used for the dump. If `null` a default name is chosen by gitea.";
example = "gitea-dump";
};
};
2020-08-02 17:32:17 +00:00
lfs = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enables git-lfs support.";
2020-08-02 17:32:17 +00:00
};
contentDir = mkOption {
type = types.str;
default = "${cfg.stateDir}/data/lfs";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/lfs"'';
description = "Where to store LFS files.";
2020-08-02 17:32:17 +00:00
};
};
2017-10-18 04:16:46 +00:00
appName = mkOption {
type = types.str;
default = "gitea: Gitea Service";
description = "Application name.";
2017-10-18 04:16:46 +00:00
};
repositoryRoot = mkOption {
type = types.str;
default = "${cfg.stateDir}/repositories";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/repositories"'';
description = "Path to the git repositories.";
2017-10-18 04:16:46 +00:00
};
2022-05-24 12:03:35 +00:00
camoHmacKeyFile = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/secrets/gitea/camoHmacKey";
description = "Path to a file containing the camo HMAC key.";
2022-05-24 12:03:35 +00:00
};
mailerPasswordFile = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/secrets/gitea/mailpw";
description = "Path to a file containing the SMTP password.";
};
metricsTokenFile = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/secrets/gitea/metrics_token";
description = "Path to a file containing the metrics authentication token.";
};
2020-04-23 21:53:18 +00:00
settings = mkOption {
default = {};
description = ''
2020-04-23 21:53:18 +00:00
Gitea configuration. Refer to <https://docs.gitea.io/en-us/config-cheat-sheet/>
for details on supported values.
'';
example = literalExpression ''
2020-04-23 21:53:18 +00:00
{
"cron.sync_external_users" = {
RUN_AT_START = true;
SCHEDULE = "@every 24h";
UPDATE_EXISTING = true;
};
mailer = {
ENABLED = true;
MAILER_TYPE = "sendmail";
FROM = "do-not-reply@example.org";
SENDMAIL_PATH = "''${pkgs.system-sendmail}/bin/sendmail";
2020-04-23 21:53:18 +00:00
};
other = {
SHOW_FOOTER_VERSION = false;
};
}
'';
type = types.submodule {
freeformType = format.type;
options = {
log = {
ROOT_PATH = mkOption {
default = "${cfg.stateDir}/log";
defaultText = literalExpression ''"''${config.${opt.stateDir}}/log"'';
type = types.str;
description = "Root path for log files.";
};
LEVEL = mkOption {
default = "Info";
type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ];
description = "General log level.";
};
};
server = {
PROTOCOL = mkOption {
type = types.enum [ "http" "https" "fcgi" "http+unix" "fcgi+unix" ];
default = "http";
description = ''Listen protocol. `+unix` means "over unix", not "in addition to."'';
};
HTTP_ADDR = mkOption {
type = types.either types.str types.path;
default = if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/gitea/gitea.sock" else "0.0.0.0";
defaultText = literalExpression ''if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/gitea/gitea.sock" else "0.0.0.0"'';
description = "Listen address. Must be a path when using a unix socket.";
};
HTTP_PORT = mkOption {
type = types.port;
default = 3000;
description = "Listen port. Ignored when using a unix socket.";
};
DOMAIN = mkOption {
type = types.str;
default = "localhost";
description = "Domain name of your server.";
};
ROOT_URL = mkOption {
type = types.str;
default = "http://${cfg.settings.server.DOMAIN}:${toString cfg.settings.server.HTTP_PORT}/";
defaultText = literalExpression ''"http://''${config.services.gitea.settings.server.DOMAIN}:''${toString config.services.gitea.settings.server.HTTP_PORT}/"'';
description = "Full public URL of gitea server.";
};
STATIC_ROOT_PATH = mkOption {
type = types.either types.str types.path;
default = cfg.package.data;
defaultText = literalExpression "config.${opt.package}.data";
example = "/var/lib/gitea/data";
description = "Upper level of template and static files path.";
};
DISABLE_SSH = mkOption {
type = types.bool;
default = false;
description = "Disable external SSH feature.";
};
SSH_PORT = mkOption {
type = types.port;
default = 22;
example = 2222;
description = ''
SSH port displayed in clone URL.
The option is required to configure a service when the external visible port
differs from the local listening port i.e. if port forwarding is used.
'';
};
};
service = {
DISABLE_REGISTRATION = mkEnableOption "the registration lock" // {
description = ''
By default any user can create an account on this `gitea` instance.
This can be disabled by using this option.
*Note:* please keep in mind that this should be added after the initial
deploy unless [](#opt-services.gitea.useWizard)
is `true` as the first registered user will be the administrator if
no install wizard is used.
'';
};
};
session = {
COOKIE_SECURE = mkOption {
type = types.bool;
default = false;
description = ''
Marks session cookies as "secure" as a hint for browsers to only send
them via HTTPS. This option is recommend, if gitea is being served over HTTPS.
'';
};
};
};
};
2020-04-23 21:53:18 +00:00
};
2017-10-18 04:16:46 +00:00
extraConfig = mkOption {
2020-04-23 21:53:18 +00:00
type = with types; nullOr str;
default = null;
description = "Configuration lines appended to the generated gitea configuration file.";
2017-10-18 04:16:46 +00:00
};
};
};
config = mkIf cfg.enable {
assertions = [
{ assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user;
message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned";
}
nixos/postgresql: drop ensurePermissions, fix ensureUsers for postgresql15 Closes #216989 First of all, a bit of context: in PostgreSQL, newly created users don't have the CREATE privilege on the public schema of a database even with `ALL PRIVILEGES` granted via `ensurePermissions` which is how most of the DB users are currently set up "declaratively"[1]. This means e.g. a freshly deployed Nextcloud service will break early because Nextcloud itself cannot CREATE any tables in the public schema anymore. The other issue here is that `ensurePermissions` is a mere hack. It's effectively a mixture of SQL code (e.g. `DATABASE foo` is relying on how a value is substituted in a query. You'd have to parse a subset of SQL to actually know which object are permissions granted to for a user). After analyzing the existing modules I realized that in every case with a single exception[2] the UNIX system user is equal to the db user is equal to the db name and I don't see a compelling reason why people would change that in 99% of the cases. In fact, some modules would even break if you'd change that because the declarations of the system user & the db user are mixed up[3]. So I decided to go with something new which restricts the ways to use `ensure*` options rather than expanding those[4]. Effectively this means that * The DB user _must_ be equal to the DB name. * Permissions are granted via `ensureDBOwnerhip` for an attribute-set in `ensureUsers`. That way, the user is actually the owner and can perform `CREATE`. * For such a postgres user, a database must be declared in `ensureDatabases`. For anything else, a custom state management should be implemented. This can either be `initialScript`, doing it manual, outside of the module or by implementing proper state management for postgresql[5], but the current state of `ensure*` isn't even declarative, but a convergent tool which is what Nix actually claims to _not_ do. Regarding existing setups: there are effectively two options: * Leave everything as-is (assuming that system user == db user == db name): then the DB user will automatically become the DB owner and everything else stays the same. * Drop the `createDatabase = true;` declarations: nothing will change because a removal of `ensure*` statements is ignored, so it doesn't matter at all whether this option is kept after the first deploy (and later on you'd usually restore from backups anyways). The DB user isn't the owner of the DB then, but for an existing setup this is irrelevant because CREATE on the public schema isn't revoked from existing users (only not granted for new users). [1] not really declarative though because removals of these statements are simply ignored for instance: https://github.com/NixOS/nixpkgs/issues/206467 [2] `services.invidious`: I removed the `ensure*` part temporarily because it IMHO falls into the category "manage the state on your own" (see the commit message). See also https://github.com/NixOS/nixpkgs/pull/265857 [3] e.g. roundcube had `"DATABASE ${cfg.database.username}" = "ALL PRIVILEGES";` [4] As opposed to other changes that are considered a potential fix, but also add more things like collation for DBs or passwords that are _never_ touched again when changing those. [5] As suggested in e.g. https://github.com/NixOS/nixpkgs/issues/206467
2023-11-08 11:50:09 +00:00
{ assertion = cfg.database.createDatabase && usePostgresql -> cfg.database.user == cfg.database.name;
message = ''
When creating a database via NixOS, the db user and db name must be equal!
If you already have an existing DB+user and this assertion is new, you can safely set
`services.gitea.createDatabase` to `false` because removal of `ensureUsers`
and `ensureDatabases` doesn't have any effect.
'';
}
];
2020-04-23 21:53:18 +00:00
services.gitea.settings = {
"cron.update_checker".ENABLED = lib.mkDefault false;
2020-04-23 21:53:18 +00:00
database = mkMerge [
{
DB_TYPE = cfg.database.type;
}
(mkIf (useMysql || usePostgresql) {
HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port;
NAME = cfg.database.name;
USER = cfg.database.user;
PASSWD = "#dbpass#";
})
(mkIf useSqlite {
PATH = cfg.database.path;
})
(mkIf usePostgresql {
SSL_MODE = "disable";
})
];
repository = {
ROOT = cfg.repositoryRoot;
};
server = mkIf cfg.lfs.enable {
LFS_START_SERVER = true;
LFS_JWT_SECRET = "#lfsjwtsecret#";
};
2020-04-23 21:53:18 +00:00
2022-05-24 12:03:35 +00:00
camo = mkIf (cfg.camoHmacKeyFile != null) {
HMAC_KEY = "#hmackey#";
};
2020-04-23 21:53:18 +00:00
session = {
COOKIE_NAME = lib.mkDefault "session";
2020-04-23 21:53:18 +00:00
};
security = {
SECRET_KEY = "#secretkey#";
INTERNAL_TOKEN = "#internaltoken#";
2020-04-23 21:53:18 +00:00
INSTALL_LOCK = true;
};
mailer = mkIf (cfg.mailerPasswordFile != null) {
PASSWD = "#mailerpass#";
};
metrics = mkIf (cfg.metricsTokenFile != null) {
TOKEN = "#metricstoken#";
};
oauth2 = {
JWT_SECRET = "#oauth2jwtsecret#";
};
lfs = mkIf cfg.lfs.enable {
PATH = cfg.lfs.contentDir;
};
packages.CHUNKED_UPLOAD_PATH = "${cfg.stateDir}/tmp/package-upload";
2020-04-23 21:53:18 +00:00
};
services.postgresql = optionalAttrs (usePostgresql && cfg.database.createDatabase) {
enable = mkDefault true;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
nixos/postgresql: drop ensurePermissions, fix ensureUsers for postgresql15 Closes #216989 First of all, a bit of context: in PostgreSQL, newly created users don't have the CREATE privilege on the public schema of a database even with `ALL PRIVILEGES` granted via `ensurePermissions` which is how most of the DB users are currently set up "declaratively"[1]. This means e.g. a freshly deployed Nextcloud service will break early because Nextcloud itself cannot CREATE any tables in the public schema anymore. The other issue here is that `ensurePermissions` is a mere hack. It's effectively a mixture of SQL code (e.g. `DATABASE foo` is relying on how a value is substituted in a query. You'd have to parse a subset of SQL to actually know which object are permissions granted to for a user). After analyzing the existing modules I realized that in every case with a single exception[2] the UNIX system user is equal to the db user is equal to the db name and I don't see a compelling reason why people would change that in 99% of the cases. In fact, some modules would even break if you'd change that because the declarations of the system user & the db user are mixed up[3]. So I decided to go with something new which restricts the ways to use `ensure*` options rather than expanding those[4]. Effectively this means that * The DB user _must_ be equal to the DB name. * Permissions are granted via `ensureDBOwnerhip` for an attribute-set in `ensureUsers`. That way, the user is actually the owner and can perform `CREATE`. * For such a postgres user, a database must be declared in `ensureDatabases`. For anything else, a custom state management should be implemented. This can either be `initialScript`, doing it manual, outside of the module or by implementing proper state management for postgresql[5], but the current state of `ensure*` isn't even declarative, but a convergent tool which is what Nix actually claims to _not_ do. Regarding existing setups: there are effectively two options: * Leave everything as-is (assuming that system user == db user == db name): then the DB user will automatically become the DB owner and everything else stays the same. * Drop the `createDatabase = true;` declarations: nothing will change because a removal of `ensure*` statements is ignored, so it doesn't matter at all whether this option is kept after the first deploy (and later on you'd usually restore from backups anyways). The DB user isn't the owner of the DB then, but for an existing setup this is irrelevant because CREATE on the public schema isn't revoked from existing users (only not granted for new users). [1] not really declarative though because removals of these statements are simply ignored for instance: https://github.com/NixOS/nixpkgs/issues/206467 [2] `services.invidious`: I removed the `ensure*` part temporarily because it IMHO falls into the category "manage the state on your own" (see the commit message). See also https://github.com/NixOS/nixpkgs/pull/265857 [3] e.g. roundcube had `"DATABASE ${cfg.database.username}" = "ALL PRIVILEGES";` [4] As opposed to other changes that are considered a potential fix, but also add more things like collation for DBs or passwords that are _never_ touched again when changing those. [5] As suggested in e.g. https://github.com/NixOS/nixpkgs/issues/206467
2023-11-08 11:50:09 +00:00
ensureDBOwnership = true;
}
];
};
services.mysql = optionalAttrs (useMysql && cfg.database.createDatabase) {
enable = mkDefault true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
ensureUsers = [
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
];
};
2017-10-18 04:16:46 +00:00
systemd.tmpfiles.rules = [
"d '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -"
"d '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.stateDir}/.ssh' 0700 ${cfg.user} ${cfg.group} - -"
"z '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -"
# If we have a folder or symlink with gitea locales, remove it
# And symlink the current gitea locales in place
"L+ '${cfg.stateDir}/conf/locale' - - - - ${cfg.package.out}/locale"
] ++ lib.optionals cfg.lfs.enable [
"d '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
"z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -"
];
2017-10-18 04:16:46 +00:00
systemd.services.gitea = {
description = "gitea";
2023-06-02 19:15:25 +00:00
after = [ "network.target" ] ++ optional usePostgresql "postgresql.service" ++ optional useMysql "mysql.service";
requires = optional (cfg.database.createDatabase && usePostgresql) "postgresql.service" ++ optional (cfg.database.createDatabase && useMysql) "mysql.service";
2017-10-18 04:16:46 +00:00
wantedBy = [ "multi-user.target" ];
path = [ cfg.package pkgs.git pkgs.gnupg ];
2017-10-18 04:16:46 +00:00
# In older versions the secret naming for JWT was kind of confusing.
# The file jwt_secret hold the value for LFS_JWT_SECRET and JWT_SECRET
2022-12-18 00:31:14 +00:00
# wasn't persistent at all.
# To fix that, there is now the file oauth2_jwt_secret containing the
# values for JWT_SECRET and the file jwt_secret gets renamed to
# lfs_jwt_secret.
# We have to consider this to stay compatible with older installations.
2017-10-18 04:16:46 +00:00
preStart = let
runConfig = "${cfg.customDir}/conf/app.ini";
secretKey = "${cfg.customDir}/conf/secret_key";
oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret";
oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET
lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET
internalToken = "${cfg.customDir}/conf/internal_token";
replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
2017-10-18 04:16:46 +00:00
in ''
# copy custom configuration and generate random secrets if needed
2022-05-19 20:16:44 +00:00
${optionalString (!cfg.useWizard) ''
function gitea_setup {
cp -f '${configFile}' '${runConfig}'
if [ ! -s '${secretKey}' ]; then
${exe} generate secret SECRET_KEY > '${secretKey}'
fi
# Migrate LFS_JWT_SECRET filename
if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then
mv '${oldLfsJwtSecret}' '${lfsJwtSecret}'
fi
if [ ! -s '${oauth2JwtSecret}' ]; then
${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}'
fi
${lib.optionalString cfg.lfs.enable ''
if [ ! -s '${lfsJwtSecret}' ]; then
${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}'
fi
''}
if [ ! -s '${internalToken}' ]; then
${exe} generate secret INTERNAL_TOKEN > '${internalToken}'
fi
chmod u+w '${runConfig}'
${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}'
${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}'
${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}'
${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}'
${lib.optionalString cfg.lfs.enable ''
${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}'
''}
2022-05-24 12:03:35 +00:00
${lib.optionalString (cfg.camoHmacKeyFile != null) ''
${replaceSecretBin} '#hmackey#' '${cfg.camoHmacKeyFile}' '${runConfig}'
''}
${lib.optionalString (cfg.mailerPasswordFile != null) ''
${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}'
''}
${lib.optionalString (cfg.metricsTokenFile != null) ''
${replaceSecretBin} '#metricstoken#' '${cfg.metricsTokenFile}' '${runConfig}'
''}
chmod u-w '${runConfig}'
}
(umask 027; gitea_setup)
2017-10-18 04:16:46 +00:00
''}
# run migrations/init the database
${exe} migrate
2017-10-18 04:16:46 +00:00
# update all hooks' binary paths
${exe} admin regenerate hooks
# update command option in authorized_keys
if [ -r ${cfg.stateDir}/.ssh/authorized_keys ]
then
${exe} admin regenerate keys
fi
2017-10-18 04:16:46 +00:00
'';
serviceConfig = {
Type = "simple";
2017-10-18 04:16:46 +00:00
User = cfg.user;
Group = cfg.group;
2017-10-18 04:16:46 +00:00
WorkingDirectory = cfg.stateDir;
ExecStart = "${exe} web --pid /run/gitea/gitea.pid";
2017-10-18 04:16:46 +00:00
Restart = "always";
2020-07-30 21:20:27 +00:00
# Runtime directory and mode
RuntimeDirectory = "gitea";
RuntimeDirectoryMode = "0755";
2023-01-01 11:07:09 +00:00
# Proc filesystem
ProcSubset = "pid";
ProtectProc = "invisible";
2020-07-31 12:53:48 +00:00
# Access write directories
ReadWritePaths = [ cfg.customDir cfg.dump.backupDir cfg.repositoryRoot cfg.stateDir cfg.lfs.contentDir ];
2020-07-31 12:53:48 +00:00
UMask = "0027";
# Capabilities
CapabilityBoundingSet = "";
# Security
NoNewPrivileges = true;
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
2020-07-31 12:53:48 +00:00
PrivateTmp = true;
PrivateDevices = true;
2020-07-31 12:53:48 +00:00
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
2020-07-31 12:53:48 +00:00
ProtectKernelLogs = true;
ProtectControlGroups = true;
2023-01-01 11:07:09 +00:00
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictNamespaces = true;
LockPersonality = true;
2020-07-31 12:53:48 +00:00
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
2020-07-31 12:53:48 +00:00
RestrictSUIDSGID = true;
2023-01-01 11:07:09 +00:00
RemoveIPC = true;
PrivateMounts = true;
2020-07-31 12:53:48 +00:00
# System Call Filtering
SystemCallArchitectures = "native";
2023-01-01 11:07:09 +00:00
SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" "setrlimit" ];
2017-10-18 04:16:46 +00:00
};
environment = {
USER = cfg.user;
HOME = cfg.stateDir;
GITEA_WORK_DIR = cfg.stateDir;
GITEA_CUSTOM = cfg.customDir;
2017-10-18 04:16:46 +00:00
};
};
users.users = mkIf (cfg.user == "gitea") {
gitea = {
2017-10-18 04:16:46 +00:00
description = "Gitea Service";
home = cfg.stateDir;
useDefaultShell = true;
group = cfg.group;
2019-10-12 20:25:28 +00:00
isSystemUser = true;
2017-10-18 04:16:46 +00:00
};
};
users.groups = mkIf (cfg.group == "gitea") {
gitea = {};
};
2020-04-23 21:53:18 +00:00
warnings =
optional (cfg.database.password != "") "config.services.gitea.database.password will be stored as plaintext in the Nix store. Use database.passwordFile instead." ++
2020-04-23 21:53:18 +00:00
optional (cfg.extraConfig != null) ''
services.gitea.`extraConfig` is deprecated, please use services.gitea.`settings`.
'' ++
optional (lib.getName cfg.package == "forgejo") ''
Running forgejo via services.gitea.package is no longer supported.
Please use services.forgejo instead.
See https://nixos.org/manual/nixos/unstable/#module-forgejo for migration instructions.
2020-04-23 21:53:18 +00:00
'';
2017-10-18 04:16:46 +00:00
# Create database passwordFile default when password is configured.
services.gitea.database.passwordFile =
2022-05-19 20:16:44 +00:00
mkDefault (toString (pkgs.writeTextFile {
2017-10-18 04:16:46 +00:00
name = "gitea-database-password";
text = cfg.database.password;
2022-05-19 20:16:44 +00:00
}));
systemd.services.gitea-dump = mkIf cfg.dump.enable {
description = "gitea dump";
after = [ "gitea.service" ];
path = [ cfg.package ];
environment = {
USER = cfg.user;
HOME = cfg.stateDir;
GITEA_WORK_DIR = cfg.stateDir;
GITEA_CUSTOM = cfg.customDir;
};
serviceConfig = {
Type = "oneshot";
User = cfg.user;
ExecStart = "${exe} dump --type ${cfg.dump.type}" + optionalString (cfg.dump.file != null) " --file ${cfg.dump.file}";
2020-07-30 21:04:23 +00:00
WorkingDirectory = cfg.dump.backupDir;
};
};
systemd.timers.gitea-dump = mkIf cfg.dump.enable {
description = "Update timer for gitea-dump";
partOf = [ "gitea-dump.service" ];
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = cfg.dump.interval;
};
2017-10-18 04:16:46 +00:00
};
meta.maintainers = with lib.maintainers; [ srhb ma27 pyrox0 ];
2017-10-18 04:16:46 +00:00
}