diff options
Diffstat (limited to 'nixos/modules/services')
23 files changed, 640 insertions, 179 deletions
diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix index e18e61eb6d44..77a0e74af9ca 100644 --- a/nixos/modules/services/audio/navidrome.nix +++ b/nixos/modules/services/audio/navidrome.nix @@ -28,10 +28,17 @@ in { ''; }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether to open the TCP port in the firewall"; + }; }; }; config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.settings.Port]; + systemd.services.navidrome = { description = "Navidrome Media Server"; after = [ "network.target" ]; diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md index d65d9616e2f2..e5e0b7efec29 100644 --- a/nixos/modules/services/databases/postgresql.md +++ b/nixos/modules/services/databases/postgresql.md @@ -39,6 +39,125 @@ By default, PostgreSQL stores its databases in {file}`/var/lib/postgresql/$psqlS services.postgresql.dataDir = "/data/postgresql"; ``` +## Initializing {#module-services-postgres-initializing} + +As of NixOS 23.11, +`services.postgresql.ensureUsers.*.ensurePermissions` has been +deprecated, after a change to default permissions in PostgreSQL 15 +invalidated most of its previous use cases: + +- In psql < 15, `ALL PRIVILEGES` used to include `CREATE TABLE`, where + in psql >= 15 that would be a separate permission +- psql >= 15 instead gives only the database owner create permissions +- Even on psql < 15 (or databases migrated to >= 15), it is + recommended to manually assign permissions along these lines + - https://www.postgresql.org/docs/release/15.0/ + - https://www.postgresql.org/docs/15/ddl-schemas.html#DDL-SCHEMAS-PRIV + +### Assigning ownership {#module-services-postgres-initializing-ownership} + +Usually, the database owner should be a database user of the same +name. This can be done with +`services.postgresql.ensureUsers.*.ensureDBOwnership = true;`. + +If the database user name equals the connecting system user name, +postgres by default will accept a passwordless connection via unix +domain socket. This makes it possible to run many postgres-backed +services without creating any database secrets at all + +### Assigning extra permissions {#module-services-postgres-initializing-extra-permissions} + +For many cases, it will be enough to have the database user be the +owner. Until `services.postgresql.ensureUsers.*.ensurePermissions` has +been re-thought, if more users need access to the database, please use +one of the following approaches: + +**WARNING:** `services.postgresql.initialScript` is not recommended +for `ensurePermissions` replacement, as that is *only run on first +start of PostgreSQL*. + +**NOTE:** all of these methods may be obsoleted, when `ensure*` is +reworked, but it is expected that they will stay viable for running +database migrations. + +**NOTE:** please make sure that any added migrations are idempotent (re-runnable). + +#### as superuser {#module-services-postgres-initializing-extra-permissions-superuser} + +**Advantage:** compatible with postgres < 15, because it's run +as the database superuser `postgres`. + +##### in database `postStart` {#module-services-postgres-initializing-extra-permissions-superuser-post-start} + +**Disadvantage:** need to take care of ordering yourself. In this +example, `mkAfter` ensures that permissions are assigned after any +databases from `ensureDatabases` and `extraUser1` from `ensureUsers` +are already created. + +```nix + systemd.services.postgresql.postStart = lib.mkAfter '' + $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"' + $PSQL service1 -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"' + # .... + ''; +``` + +##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-superuser-oneshot} + +```nix + systemd.services."migrate-service1-db1" = { + serviceConfig.Type = "oneshot"; + requiredBy = "service1.service"; + before = "service1.service"; + after = "postgresql.service"; + serviceConfig.User = "postgres"; + environment.PSQL = "psql --port=${toString services.postgresql.port}"; + path = [ postgresql ]; + script = '' + $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"' + $PSQL service1 -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"' + # .... + ''; + }; +``` + +#### as service user {#module-services-postgres-initializing-extra-permissions-service-user} + +**Advantage:** re-uses systemd's dependency ordering; + +**Disadvantage:** relies on service user having grant permission. To be combined with `ensureDBOwnership`. + +##### in service `preStart` {#module-services-postgres-initializing-extra-permissions-service-user-pre-start} + +```nix + environment.PSQL = "psql --port=${toString services.postgresql.port}"; + path = [ postgresql ]; + systemd.services."service1".preStart = '' + $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"' + $PSQL -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"' + # .... + ''; +``` + +##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-service-user-oneshot} + +```nix + systemd.services."migrate-service1-db1" = { + serviceConfig.Type = "oneshot"; + requiredBy = "service1.service"; + before = "service1.service"; + after = "postgresql.service"; + serviceConfig.User = "service1"; + environment.PSQL = "psql --port=${toString services.postgresql.port}"; + path = [ postgresql ]; + script = '' + $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"' + $PSQL -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"' + # .... + ''; + }; +``` + ## Upgrading {#module-services-postgres-upgrading} ::: {.note} diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix index ae695baf42c6..07ca2727cf48 100644 --- a/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -115,8 +115,7 @@ in { environment.systemPackages = [ cfg.package ] ++ lib.optional cfg.jack.enable jack-libs; - systemd.packages = [ cfg.package ] - ++ lib.optional cfg.pulse.enable cfg.package.pulse; + systemd.packages = [ cfg.package ]; # PipeWire depends on DBUS but doesn't list it. Without this booting # into a terminal results in the service crashing with an error. @@ -130,9 +129,13 @@ in { systemd.user.sockets.pipewire.enable = !cfg.systemWide; systemd.user.services.pipewire.enable = !cfg.systemWide; + # Mask pw-pulse if it's not wanted + systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable; + systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable; + systemd.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ]; systemd.user.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ]; - systemd.user.sockets.pipewire-pulse.wantedBy = lib.mkIf (cfg.socketActivation && cfg.pulse.enable) ["sockets.target"]; + systemd.user.sockets.pipewire-pulse.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ]; services.udev.packages = [ cfg.package ]; @@ -140,14 +143,14 @@ in { environment.etc."alsa/conf.d/49-pipewire-modules.conf" = mkIf cfg.alsa.enable { text = '' pcm_type.pipewire { - libs.native = ${cfg.package.lib}/lib/alsa-lib/libasound_module_pcm_pipewire.so ; + libs.native = ${cfg.package}/lib/alsa-lib/libasound_module_pcm_pipewire.so ; ${optionalString enable32BitAlsaPlugins - "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire.lib}/lib/alsa-lib/libasound_module_pcm_pipewire.so ;"} + "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire}/lib/alsa-lib/libasound_module_pcm_pipewire.so ;"} } ctl_type.pipewire { - libs.native = ${cfg.package.lib}/lib/alsa-lib/libasound_module_ctl_pipewire.so ; + libs.native = ${cfg.package}/lib/alsa-lib/libasound_module_ctl_pipewire.so ; ${optionalString enable32BitAlsaPlugins - "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire.lib}/lib/alsa-lib/libasound_module_ctl_pipewire.so ;"} + "libs.32Bit = ${pkgs.pkgsi686Linux.pipewire}/lib/alsa-lib/libasound_module_ctl_pipewire.so ;"} } ''; }; diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix index 7a938459d0cb..7b6c336bd221 100644 --- a/nixos/modules/services/hardware/fwupd.nix +++ b/nixos/modules/services/hardware/fwupd.nix @@ -187,13 +187,20 @@ in { # fwupd-refresh expects a user that we do not create, so just run with DynamicUser # instead and ensure we take ownership of /var/lib/fwupd services.fwupd-refresh.serviceConfig = { - DynamicUser = true; StateDirectory = "fwupd"; + # Better for debugging, upstream sets stderr to null for some reason.. + StandardError = "inherit"; }; timers.fwupd-refresh.wantedBy = [ "timers.target" ]; }; + users.users.fwupd-refresh = { + isSystemUser = true; + group = "fwupd-refresh"; + }; + users.groups.fwupd-refresh = {}; + security.polkit.enable = true; }; diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index a7e8aee1f2a2..76035625fbe1 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -493,6 +493,9 @@ in { RuntimeDirectory = "mailman"; LogsDirectory = "mailman"; PIDFile = "/run/mailman/master.pid"; + Restart = "on-failure"; + TimeoutStartSec = 180; + TimeoutStopSec = 180; }; }; @@ -596,6 +599,7 @@ in { User = cfg.webUser; Group = "mailman"; RuntimeDirectory = "mailman-uwsgi"; + Restart = "on-failure"; }; }); @@ -620,6 +624,7 @@ in { User = cfg.webUser; Group = "mailman"; WorkingDirectory = "/var/lib/mailman-web"; + Restart = "on-failure"; }; }; } // flip lib.mapAttrs' { diff --git a/nixos/modules/services/misc/autofs.nix b/nixos/modules/services/misc/autofs.nix index 55ab15ff003d..723b67e8bb6b 100644 --- a/nixos/modules/services/misc/autofs.nix +++ b/nixos/modules/services/misc/autofs.nix @@ -74,7 +74,7 @@ in config = mkIf cfg.enable { - boot.kernelModules = [ "autofs4" ]; + boot.kernelModules = [ "autofs" ]; systemd.services.autofs = { description = "Automounts filesystems on demand"; diff --git a/nixos/modules/services/misc/forgejo.md b/nixos/modules/services/misc/forgejo.md new file mode 100644 index 000000000000..6a3407382085 --- /dev/null +++ b/nixos/modules/services/misc/forgejo.md @@ -0,0 +1,79 @@ +# Forgejo {#module-forgejo} + +Forgejo is a soft-fork of gitea, with strong community focus, as well +as on self-hosting and federation. [Codeberg](https://codeberg.org) is +deployed from it. + +See [upstream docs](https://forgejo.org/docs/latest/). + +The method of choice for running forgejo is using [`services.forgejo`](#opt-services.forgejo.enable). + +::: {.warning} +Running forgejo using `services.gitea.package = pkgs.forgejo` is no longer +recommended. +If you experience issues with your instance using `services.gitea`, +**DO NOT** report them to the `services.gitea` module maintainers. +**DO** report them to the `services.forgejo` module maintainers instead. +::: + +## Migration from Gitea {#module-forgejo-migration-gitea} + +::: {.note} +Migrating is, while not strictly necessary at this point, highly recommended. +Both modules and projects are likely to divide further with each release. +Which might lead to an even more involved migration. +::: + +### Full-Migration {#module-forgejo-migration-gitea-default} + +This will migrate the state directory (data), rename and chown the database and +delete the gitea user. + +::: {.note} +This will also change the git remote ssh-url user from `gitea@` to `forgejo@`, +when using the host's openssh server (default) instead of the integrated one. +::: + +Instructions for PostgreSQL (default). Adapt accordingly for other databases: + +```sh +systemctl stop gitea +mv /var/lib/gitea /var/lib/forgejo +runuser -u postgres -- psql -c ' + ALTER USER gitea RENAME TO forgejo; + ALTER DATABASE gitea RENAME TO forgejo; +' +nixos-rebuild switch +systemctl stop forgejo +chown -R forgejo:forgejo /var/lib/forgejo +systemctl restart forgejo +``` + +### Alternatively, keeping the gitea user {#module-forgejo-migration-gitea-impersonate} + +Alternatively, instead of renaming the database, copying the state folder and +changing the user, the forgejo module can be set up to re-use the old storage +locations and database, instead of having to copy or rename them. +Make sure to disable `services.gitea`, when doing this. + +```nix +services.gitea.enable = false; + +services.forgejo = { + enable = true; + user = "gitea"; + group = "gitea"; + stateDir = "/var/lib/gitea"; + database.name = "gitea"; + database.user = "gitea"; +}; + +users.users,gitea = { + home = "/var/lib/gitea"; + useDefaultShell = true; + group = "gitea"; + isSystemUser = true; +}; + +users.groups.gitea = {}; +``` diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix index 6f459048f347..15966adfe38e 100644 --- a/nixos/modules/services/misc/forgejo.nix +++ b/nixos/modules/services/misc/forgejo.nix @@ -685,5 +685,6 @@ in }; }; + meta.doc = ./forgejo.md; meta.maintainers = with lib.maintainers; [ bendlas emilylange ]; } diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix index 5e21407042b6..f84d677f14d8 100644 --- a/nixos/modules/services/monitoring/grafana.nix +++ b/nixos/modules/services/monitoring/grafana.nix @@ -1841,6 +1841,7 @@ in serviceConfig = { WorkingDirectory = cfg.dataDir; User = "grafana"; + Restart = "on-failure"; RuntimeDirectory = "grafana"; RuntimeDirectoryMode = "0755"; # Hardening diff --git a/nixos/modules/services/monitoring/parsedmarc.nix b/nixos/modules/services/monitoring/parsedmarc.nix index 44fc359b6a7d..a146e7ab9543 100644 --- a/nixos/modules/services/monitoring/parsedmarc.nix +++ b/nixos/modules/services/monitoring/parsedmarc.nix @@ -301,6 +301,7 @@ in description = lib.mdDoc '' The addresses to send outgoing mail to. ''; + apply = x: if x == [] then null else lib.concatStringsSep "," x; }; }; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 305f235054be..f89522c09864 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -2,8 +2,8 @@ let inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers - mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption - optional types mkOptionDefault flip attrNames; + mapAttrs mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption + optional types mkOptionDefault flip attrNames; cfg = config.services.prometheus.exporters; @@ -20,7 +20,7 @@ let # systemd service must be provided by specifying either # `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart` - exporterOpts = genAttrs [ + exporterOpts = (genAttrs [ "apcupsd" "artifactory" "bind" @@ -34,14 +34,15 @@ let "domain" "dovecot" "fastly" + "flow" "fritzbox" "graphite" "idrac" "imap-mailstat" "influxdb" "ipmi" - "json" "jitsi" + "json" "junos-czerwonk" "kea" "keylight" @@ -74,9 +75,9 @@ let "scaphandre" "script" "shelly" - "snmp" "smartctl" "smokeping" + "snmp" "sql" "statsd" "surfboard" @@ -88,10 +89,39 @@ let "v2ray" "varnish" "wireguard" - "flow" "zfs" - ] (name: - import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; } + ] + (name: + import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; } + )) // (mapAttrs + (name: params: + import (./. + "/exporters/${params.name}.nix") { inherit config lib pkgs options; type = params.type ; }) + { + exportarr-bazarr = { + name = "exportarr"; + type = "bazarr"; + }; + exportarr-lidarr = { + name = "exportarr"; + type = "lidarr"; + }; + exportarr-prowlarr = { + name = "exportarr"; + type = "prowlarr"; + }; + exportarr-radarr = { + name = "exportarr"; + type = "radarr"; + }; + exportarr-readarr = { + name = "exportarr"; + type = "readarr"; + }; + exportarr-sonarr = { + name = "exportarr"; + type = "sonarr"; + }; + } ); mkExporterOpts = ({ name, port }: { diff --git a/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix b/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix new file mode 100644 index 000000000000..132209335410 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, options, type }: + +let + cfg = config.services.prometheus.exporters."exportarr-${type}"; + exportarrEnvironment = ( + lib.mapAttrs (_: toString) cfg.environment + ) // { + PORT = toString cfg.port; + URL = cfg.url; + API_KEY_FILE = lib.mkIf (cfg.apiKeyFile != null) "%d/api-key"; + }; +in +{ + port = 9708; + extraOpts = { + url = lib.mkOption { + type = lib.types.str; + default = "http://127.0.0.1"; + description = lib.mdDoc '' + The full URL to Sonarr, Radarr, or Lidarr. + ''; + }; + + apiKeyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = lib.mdDoc '' + File containing the api-key. + ''; + }; + + package = lib.mkPackageOptionMD pkgs "exportarr" { }; + + environment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = lib.mdDoc '' + See [the configuration guide](https://github.com/onedr0p/exportarr#configuration) for available options. + ''; + example = { + PROWLARR__BACKFILL = true; + }; + }; + }; + serviceOpts = { + serviceConfig = { + LoadCredential = lib.optionalString (cfg.apiKeyFile != null) "api-key:${cfg.apiKeyFile}"; + ExecStart = ''${cfg.package}/bin/exportarr ${type} "$@"''; + ProcSubset = "pid"; + ProtectProc = "invisible"; + SystemCallFilter = ["@system-service" "~@privileged"]; + }; + environment = exportarrEnvironment; + }; +} diff --git a/nixos/modules/services/networking/harmonia.nix b/nixos/modules/services/networking/harmonia.nix index 144fa6c708e2..4733165cf7d1 100644 --- a/nixos/modules/services/networking/harmonia.nix +++ b/nixos/modules/services/networking/harmonia.nix @@ -28,6 +28,8 @@ in }; config = lib.mkIf cfg.enable { + nix.settings.extra-allowed-users = [ "harmonia" ]; + systemd.services.harmonia = { description = "harmonia binary cache service"; diff --git a/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixos/modules/services/networking/ircd-hybrid/builder.sh index d9d2e4264dfd..07a3788abf7d 100644 --- a/nixos/modules/services/networking/ircd-hybrid/builder.sh +++ b/nixos/modules/services/networking/ircd-hybrid/builder.sh @@ -1,4 +1,4 @@ -if [ -e .attrs.sh ]; then source .attrs.sh; fi +if [ -e "$NIX_ATTRS_SH_FILE" ]; then . "$NIX_ATTRS_SH_FILE"; elif [ -f .attrs.sh ]; then . .attrs.sh; fi source $stdenv/setup doSub() { diff --git a/nixos/modules/services/networking/ntp/chrony.nix b/nixos/modules/services/networking/ntp/chrony.nix index afd721e34da5..d370e6946d7b 100644 --- a/nixos/modules/services/networking/ntp/chrony.nix +++ b/nixos/modules/services/networking/ntp/chrony.nix @@ -9,6 +9,7 @@ let stateDir = cfg.directory; driftFile = "${stateDir}/chrony.drift"; keyFile = "${stateDir}/chrony.keys"; + rtcFile = "${stateDir}/chrony.rtc"; configFile = pkgs.writeText "chrony.conf" '' ${concatMapStringsSep "\n" (server: "server " + server + " " + cfg.serverOption + optionalString (cfg.enableNTS) " nts") cfg.servers} @@ -20,8 +21,10 @@ let driftfile ${driftFile} keyfile ${keyFile} + ${optionalString (cfg.enableRTCTrimming) "rtcfile ${rtcFile}"} ${optionalString (cfg.enableNTS) "ntsdumpdir ${stateDir}"} + ${optionalString (cfg.enableRTCTrimming) "rtcautotrim ${builtins.toString cfg.autotrimThreshold}"} ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} ${cfg.extraConfig} @@ -85,6 +88,33 @@ in ''; }; + enableRTCTrimming = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Enable tracking of the RTC offset to the system clock and automatic trimming. + See also [](#opt-services.chrony.autotrimThreshold) + + ::: {.note} + This is not compatible with the `rtcsync` directive, which naively syncs the RTC time every 11 minutes. + + Tracking the RTC drift will allow more precise timekeeping, + especially on intermittently running devices, where the RTC is very relevant. + ::: + ''; + }; + + autotrimThreshold = mkOption { + type = types.ints.positive; + default = 30; + example = 10; + description = '' + Maximum estimated error threshold for the `rtcautotrim` command. + When reached, the RTC will be trimmed. + Only used when [](#opt-services.chrony.enableRTCTrimming) is enabled. + ''; + }; + enableNTS = mkOption { type = types.bool; default = false; @@ -141,7 +171,7 @@ in }; config = mkIf cfg.enable { - meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + meta.maintainers = with lib.maintainers; [ thoughtpolice vifino ]; environment.systemPackages = [ chronyPkg ]; @@ -156,12 +186,19 @@ in services.timesyncd.enable = mkForce false; + # If chrony controls and tracks the RTC, writing it externally causes clock error. + systemd.services.save-hwclock = lib.mkIf cfg.enableRTCTrimming { + enable = lib.mkForce false; + }; + systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; }; systemd.tmpfiles.rules = [ "d ${stateDir} 0750 chrony chrony - -" "f ${driftFile} 0640 chrony chrony - -" "f ${keyFile} 0640 chrony chrony - -" + ] ++ lib.optionals cfg.enableRTCTrimming [ + "f ${rtcFile} 0640 chrony chrony - -" ]; systemd.services.chronyd = diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 0426dbb0c83c..b6579af10a79 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -166,7 +166,7 @@ in { services.unbound.settings = { server = { directory = mkDefault cfg.stateDir; - username = cfg.user; + username = ''""''; chroot = ''""''; pidfile = ''""''; # when running under systemd there is no need to daemonize @@ -245,14 +245,13 @@ in { NotifyAccess = "main"; Type = "notify"; - # FIXME: Which of these do we actually need, can we drop the chroot flag? AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" + "CAP_NET_RAW" # needed if ip-transparent is set to true + ]; + CapabilityBoundingSet = [ + "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" - "CAP_SETGID" - "CAP_SETUID" - "CAP_SYS_CHROOT" - "CAP_SYS_RESOURCE" ]; User = cfg.user; @@ -266,22 +265,19 @@ in { ProtectControlGroups = true; ProtectKernelModules = true; ProtectSystem = "strict"; + ProtectClock = true; + ProtectHostname = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelTunables = true; RuntimeDirectory = "unbound"; ConfigurationDirectory = "unbound"; StateDirectory = "unbound"; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" "AF_UNIX" ]; RestrictRealtime = true; SystemCallArchitectures = "native"; - SystemCallFilter = [ - "~@clock" - "@cpu-emulation" - "@debug" - "@keyring" - "@module" - "mount" - "@obsolete" - "@resources" - ]; + SystemCallFilter = [ "@system-service" ]; RestrictNamespaces = true; LockPersonality = true; RestrictSUIDSGID = true; diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix index 6b6837109806..537a4db95ca7 100644 --- a/nixos/modules/services/networking/unifi.nix +++ b/nixos/modules/services/networking/unifi.nix @@ -1,60 +1,61 @@ { config, options, lib, pkgs, utils, ... }: -with lib; let cfg = config.services.unifi; stateDir = "/var/lib/unifi"; - cmd = '' - @${cfg.jrePackage}/bin/java java \ - ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16") - ("--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED " - + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED " - + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED")} \ - ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \ - ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \ - -jar ${stateDir}/lib/ace.jar - ''; + cmd = lib.escapeShellArgs ([ "@${cfg.jrePackage}/bin/java" "java" ] + ++ lib.optionals (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16") [ + "--add-opens=java.base/java.lang=ALL-UNNAMED" + "--add-opens=java.base/java.time=ALL-UNNAMED" + "--add-opens=java.base/sun.security.util=ALL-UNNAMED" + "--add-opens=java.base/java.io=ALL-UNNAMED" + "--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED" + ] + ++ (lib.optional (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m") + ++ (lib.optional (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m") + ++ cfg.extraJvmOptions + ++ [ "-jar" "${stateDir}/lib/ace.jar" ]); in { options = { - services.unifi.enable = mkOption { - type = types.bool; + services.unifi.enable = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether or not to enable the unifi controller service. ''; }; - services.unifi.jrePackage = mkOption { - type = types.package; + services.unifi.jrePackage = lib.mkOption { + type = lib.types.package; default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8; - defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8''; + defaultText = lib.literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8''; description = lib.mdDoc '' The JRE package to use. Check the release notes to ensure it is supported. ''; }; - services.unifi.unifiPackage = mkOption { - type = types.package; + services.unifi.unifiPackage = lib.mkOption { + type = lib.types.package; default = pkgs.unifi5; - defaultText = literalExpression "pkgs.unifi5"; + defaultText = lib.literalExpression "pkgs.unifi5"; description = lib.mdDoc '' The unifi package to use. ''; }; - services.unifi.mongodbPackage = mkOption { - type = types.package; + services.unifi.mongodbPackage = lib.mkOption { + type = lib.types.package; default = pkgs.mongodb-4_4; - defaultText = literalExpression "pkgs.mongodb"; + defaultText = lib.literalExpression "pkgs.mongodb"; description = lib.mdDoc '' The mongodb package to use. Please note: unifi7 officially only supports mongodb up until 3.6 but works with 4.4. ''; }; - services.unifi.openFirewall = mkOption { - type = types.bool; + services.unifi.openFirewall = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether or not to open the minimum required ports on the firewall. @@ -65,8 +66,8 @@ in ''; }; - services.unifi.initialJavaHeapSize = mkOption { - type = types.nullOr types.int; + services.unifi.initialJavaHeapSize = lib.mkOption { + type = with lib.types; nullOr int; default = null; example = 1024; description = lib.mdDoc '' @@ -75,8 +76,8 @@ in ''; }; - services.unifi.maximumJavaHeapSize = mkOption { - type = types.nullOr types.int; + services.unifi.maximumJavaHeapSize = lib.mkOption { + type = with lib.types; nullOr int; default = null; example = 4096; description = lib.mdDoc '' @@ -85,9 +86,18 @@ in ''; }; + services.unifi.extraJvmOptions = lib.mkOption { + type = with lib.types; listOf str; + default = [ ]; + example = lib.literalExpression ''["-Xlog:gc"]''; + description = lib.mdDoc '' + Set extra options to pass to the JVM. + ''; + }; + }; - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { users.users.unifi = { isSystemUser = true; @@ -97,7 +107,7 @@ in }; users.groups.unifi = {}; - networking.firewall = mkIf cfg.openFirewall { + networking.firewall = lib.mkIf cfg.openFirewall { # https://help.ubnt.com/hc/en-us/articles/218506997 allowedTCPPorts = [ 8080 # Port for UAP to inform controller. @@ -123,8 +133,8 @@ in serviceConfig = { Type = "simple"; - ExecStart = "${(removeSuffix "\n" cmd)} start"; - ExecStop = "${(removeSuffix "\n" cmd)} stop"; + ExecStart = "${cmd} start"; + ExecStop = "${cmd} stop"; Restart = "on-failure"; TimeoutSec = "5min"; User = "unifi"; @@ -166,7 +176,7 @@ in StateDirectory = "unifi"; RuntimeDirectory = "unifi"; LogsDirectory = "unifi"; - CacheDirectory= "unifi"; + CacheDirectory = "unifi"; TemporaryFileSystem = [ # required as we want to create bind mounts below @@ -176,7 +186,7 @@ in # We must create the binary directories as bind mounts instead of symlinks # This is because the controller resolves all symlinks to absolute paths # to be used as the working directory. - BindPaths = [ + BindPaths = [ "/var/log/unifi:${stateDir}/logs" "/run/unifi:${stateDir}/run" "${cfg.unifiPackage}/dl:${stateDir}/dl" @@ -194,7 +204,7 @@ in }; imports = [ - (mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data" ) - (mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ]) + (lib.mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data") + (lib.mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ]) ]; } diff --git a/nixos/modules/services/search/opensearch.nix b/nixos/modules/services/search/opensearch.nix index 9a50e7963138..ae79d5545fd7 100644 --- a/nixos/modules/services/search/opensearch.nix +++ b/nixos/modules/services/search/opensearch.nix @@ -72,6 +72,18 @@ in The port to listen on for transport traffic. ''; }; + + options."plugins.security.disabled" = lib.mkOption { + type = lib.types.bool; + default = true; + description = lib.mdDoc '' + Whether to enable the security plugin, + `plugins.security.ssl.transport.keystore_filepath` or + `plugins.security.ssl.transport.server.pemcert_filepath` and + `plugins.security.ssl.transport.client.pemcert_filepath` + must be set for this plugin to be enabled. + ''; + }; }; default = {}; @@ -186,6 +198,13 @@ in shopt -s inherit_errexit # Install plugins + + # remove plugins directory if it is empty. + if [ -z "$(ls -A ${cfg.dataDir}/plugins)" ]; then + rm -r "${cfg.dataDir}/plugins" + fi + + ln -sfT "${cfg.package}/plugins" "${cfg.dataDir}/plugins" ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib ln -sfT ${cfg.package}/modules ${cfg.dataDir}/modules diff --git a/nixos/modules/services/torrent/flexget.nix b/nixos/modules/services/torrent/flexget.nix index 5cd7ae6ad7db..58a4b7001497 100644 --- a/nixos/modules/services/torrent/flexget.nix +++ b/nixos/modules/services/torrent/flexget.nix @@ -64,7 +64,6 @@ in { path = [ pkg ]; serviceConfig = { User = cfg.user; - Environment = "TZ=${config.time.timeZone}"; ExecStartPre = "${pkgs.coreutils}/bin/install -m644 ${ymlFile} ${configFile}"; ExecStart = "${pkg}/bin/flexget -c ${configFile} daemon start"; ExecStop = "${pkg}/bin/flexget -c ${configFile} daemon stop"; diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix index 5feca2525ea1..8686506b1c28 100644 --- a/nixos/modules/services/web-apps/mastodon.nix +++ b/nixos/modules/services/web-apps/mastodon.nix @@ -17,9 +17,6 @@ let WEB_CONCURRENCY = toString cfg.webProcesses; MAX_THREADS = toString cfg.webThreads; - # mastodon-streaming concurrency. - STREAMING_CLUSTER_NUM = toString cfg.streamingProcesses; - DB_USER = cfg.database.user; REDIS_HOST = cfg.redis.host; @@ -33,13 +30,15 @@ let PAPERCLIP_ROOT_PATH = "/var/lib/mastodon/public-system"; PAPERCLIP_ROOT_URL = "/system"; ES_ENABLED = if (cfg.elasticsearch.host != null) then "true" else "false"; - ES_HOST = cfg.elasticsearch.host; - ES_PORT = toString(cfg.elasticsearch.port); TRUSTED_PROXY_IP = cfg.trustedProxy; } // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; } // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN = cfg.smtp.user; } + // lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_HOST = cfg.elasticsearch.host; } + // lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_PORT = toString(cfg.elasticsearch.port); } + // lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_PRESET = cfg.elasticsearch.preset; } + // lib.optionalAttrs (cfg.elasticsearch.user != null) { ES_USER = cfg.elasticsearch.user; } // cfg.extraConfig; systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@mount" "@obsolete" "@privileged" "@setuid" ]; @@ -141,8 +140,44 @@ let }) ) cfg.sidekiqProcesses; + streamingUnits = builtins.listToAttrs + (map (i: { + name = "mastodon-streaming-${toString i}"; + value = { + after = [ "network.target" "mastodon-init-dirs.service" ] + ++ lib.optional databaseActuallyCreateLocally "postgresql.service" + ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + requires = [ "mastodon-init-dirs.service" ] + ++ lib.optional databaseActuallyCreateLocally "postgresql.service" + ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; + wantedBy = [ "mastodon.target" "mastodon-streaming.target" ]; + description = "Mastodon streaming ${toString i}"; + environment = env // { SOCKET = "/run/mastodon-streaming/streaming-${toString i}.socket"; }; + serviceConfig = { + ExecStart = "${cfg.package}/run-streaming.sh"; + Restart = "always"; + RestartSec = 20; + EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ] ++ cfg.extraEnvFiles; + WorkingDirectory = cfg.package; + # Runtime directory and mode + RuntimeDirectory = "mastodon-streaming"; + RuntimeDirectoryMode = "0750"; + # System Call Filtering + SystemCallFilter = [ ("~" + lib.concatStringsSep " " (systemCallsList ++ [ "@memlock" "@resources" ])) "pipe" "pipe2" ]; + } // cfgService; + }; + }) + (lib.range 1 cfg.streamingProcesses)); + in { + imports = [ + (lib.mkRemovedOptionModule + [ "services" "mastodon" "streamingPort" ] + "Mastodon currently doesn't support streaming via TCP ports. Please open a PR if you need this." + ) + ]; + options = { services.mastodon = { enable = lib.mkEnableOption (lib.mdDoc "Mastodon, a federated social network server"); @@ -191,18 +226,13 @@ in { default = "mastodon"; }; - streamingPort = lib.mkOption { - description = lib.mdDoc "TCP port used by the mastodon-streaming service."; - type = lib.types.port; - default = 55000; - }; streamingProcesses = lib.mkOption { description = lib.mdDoc '' - Processes used by the mastodon-streaming service. - Defaults to the number of CPU cores minus one. + Number of processes used by the mastodon-streaming service. + Recommended is the amount of your CPU cores minus one. ''; - type = lib.types.nullOr lib.types.int; - default = null; + type = lib.types.ints.positive; + example = 3; }; webPort = lib.mkOption { @@ -485,6 +515,31 @@ in { type = lib.types.port; default = 9200; }; + + preset = lib.mkOption { + description = lib.mdDoc '' + It controls the ElasticSearch indices configuration (number of shards and replica). + ''; + type = lib.types.enum [ "single_node_cluster" "small_cluster" "large_cluster" ]; + default = "single_node_cluster"; + example = "large_cluster"; + }; + + user = lib.mkOption { + description = lib.mdDoc "Used for optionally authenticating with Elasticsearch."; + type = lib.types.nullOr lib.types.str; + default = null; + example = "elasticsearch-mastodon"; + }; + + passwordFile = lib.mkOption { + description = lib.mdDoc '' + Path to file containing password for optionally authenticating with Elasticsearch. + ''; + type = lib.types.nullOr lib.types.path; + default = null; + example = "/var/lib/mastodon/secrets/elasticsearch-password"; + }; }; package = lib.mkOption { @@ -603,6 +658,12 @@ in { after = [ "network.target" ]; }; + systemd.targets.mastodon-streaming = { + description = "Target for all Mastodon streaming services"; + wantedBy = [ "multi-user.target" "mastodon.target" ]; + after = [ "network.target" ]; + }; + systemd.services.mastodon-init-dirs = { script = '' umask 077 @@ -631,6 +692,8 @@ in { DB_PASS="$(cat ${cfg.database.passwordFile})" '' + lib.optionalString cfg.smtp.authenticate '' SMTP_PASSWORD="$(cat ${cfg.smtp.passwordFile})" + '' + lib.optionalString (cfg.elasticsearch.passwordFile != null) '' + ES_PASS="$(cat ${cfg.elasticsearch.passwordFile})" '' + '' EOF ''; @@ -688,33 +751,6 @@ in { ++ lib.optional databaseActuallyCreateLocally "postgresql.service"; }; - systemd.services.mastodon-streaming = { - after = [ "network.target" "mastodon-init-dirs.service" ] - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; - requires = [ "mastodon-init-dirs.service" ] - ++ lib.optional databaseActuallyCreateLocally "postgresql.service" - ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service"; - wantedBy = [ "mastodon.target" ]; - description = "Mastodon streaming"; - environment = env // (if cfg.enableUnixSocket - then { SOCKET = "/run/mastodon-streaming/streaming.socket"; } - else { PORT = toString(cfg.streamingPort); } - ); - serviceConfig = { - ExecStart = "${cfg.package}/run-streaming.sh"; - Restart = "always"; - RestartSec = 20; - EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ] ++ cfg.extraEnvFiles; - WorkingDirectory = cfg.package; - # Runtime directory and mode - RuntimeDirectory = "mastodon-streaming"; - RuntimeDirectoryMode = "0750"; - # System Call Filtering - SystemCallFilter = [ ("~" + lib.concatStringsSep " " (systemCallsList ++ [ "@memlock" "@resources" ])) "pipe" "pipe2" ]; - } // cfgService; - }; - systemd.services.mastodon-web = { after = [ "network.target" "mastodon-init-dirs.service" ] ++ lib.optional databaseActuallyCreateLocally "postgresql.service" @@ -780,10 +816,20 @@ in { }; locations."/api/v1/streaming/" = { - proxyPass = (if cfg.enableUnixSocket then "http://unix:/run/mastodon-streaming/streaming.socket" else "http://127.0.0.1:${toString(cfg.streamingPort)}/"); + proxyPass = "http://mastodon-streaming"; proxyWebsockets = true; }; }; + upstreams.mastodon-streaming = { + extraConfig = '' + least_conn; + ''; + servers = builtins.listToAttrs + (map (i: { + name = "unix:/run/mastodon-streaming/streaming-${toString i}.socket"; + value = { }; + }) (lib.range 1 cfg.streamingProcesses)); + }; }; services.postfix = lib.mkIf (cfg.smtp.createLocally && cfg.smtp.host == "127.0.0.1") { @@ -819,7 +865,7 @@ in { users.groups.${cfg.group}.members = lib.optional cfg.configureNginx config.services.nginx.user; } - { systemd.services = sidekiqUnits; } + { systemd.services = lib.mkMerge [ sidekiqUnits streamingUnits ]; } ]); meta.maintainers = with lib.maintainers; [ happy-river erictapen ]; diff --git a/nixos/modules/services/web-apps/plantuml-server.nix b/nixos/modules/services/web-apps/plantuml-server.nix index 5ebee48c3e0b..1fa69814c6c9 100644 --- a/nixos/modules/services/web-apps/plantuml-server.nix +++ b/nixos/modules/services/web-apps/plantuml-server.nix @@ -1,123 +1,110 @@ { config, lib, pkgs, ... }: -with lib; - let + inherit (lib) + literalExpression + mdDoc + mkEnableOption + mkIf + mkOption + mkPackageOptionMD + mkRemovedOptionModule + types + ; cfg = config.services.plantuml-server; in { + imports = [ + (mkRemovedOptionModule [ "services" "plantuml-server" "allowPlantumlInclude" ] "This option has been removed from PlantUML.") + ]; + options = { services.plantuml-server = { - enable = mkEnableOption (lib.mdDoc "PlantUML server"); + enable = mkEnableOption (mdDoc "PlantUML server"); - package = mkOption { - type = types.package; - default = pkgs.plantuml-server; - defaultText = literalExpression "pkgs.plantuml-server"; - description = lib.mdDoc "PlantUML server package to use"; - }; + package = mkPackageOptionMD pkgs "plantuml-server" { }; packages = { - jdk = mkOption { - type = types.package; - default = pkgs.jdk; - defaultText = literalExpression "pkgs.jdk"; - description = lib.mdDoc "JDK package to use for the server"; - }; - jetty = mkOption { - type = types.package; - default = pkgs.jetty; - defaultText = literalExpression "pkgs.jetty"; - description = lib.mdDoc "Jetty package to use for the server"; + jdk = mkPackageOptionMD pkgs "jdk" { }; + jetty = mkPackageOptionMD pkgs "jetty" { + default = "jetty_11"; + extraDescription = '' + At the time of writing (v1.2023.12), PlantUML Server does not support + Jetty versions higher than 12.x. + + Jetty 12.x has introduced major breaking changes, see + <https://github.com/jetty/jetty.project/releases/tag/jetty-12.0.0> and + <https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-migration-11-to-12> + ''; }; }; user = mkOption { type = types.str; default = "plantuml"; - description = lib.mdDoc "User which runs PlantUML server."; + description = mdDoc "User which runs PlantUML server."; }; group = mkOption { type = types.str; default = "plantuml"; - description = lib.mdDoc "Group which runs PlantUML server."; + description = mdDoc "Group which runs PlantUML server."; }; home = mkOption { - type = types.str; + type = types.path; default = "/var/lib/plantuml"; - description = lib.mdDoc "Home directory of the PlantUML server instance."; + description = mdDoc "Home directory of the PlantUML server instance."; }; listenHost = mkOption { type = types.str; default = "127.0.0.1"; - description = lib.mdDoc "Host to listen on."; + description = mdDoc "Host to listen on."; }; listenPort = mkOption { type = types.int; default = 8080; - description = lib.mdDoc "Port to listen on."; + description = mdDoc "Port to listen on."; }; plantumlLimitSize = mkOption { type = types.int; default = 4096; - description = lib.mdDoc "Limits image width and height."; + description = mdDoc "Limits image width and height."; }; - graphvizPackage = mkOption { - type = types.package; - default = pkgs.graphviz; - defaultText = literalExpression "pkgs.graphviz"; - description = lib.mdDoc "Package containing the dot executable."; - }; + graphvizPackage = mkPackageOptionMD pkgs "graphviz" { }; plantumlStats = mkOption { type = types.bool; default = false; - description = lib.mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; + description = mdDoc "Set it to on to enable statistics report (https://plantuml.com/statistics-report)."; }; httpAuthorization = mkOption { type = types.nullOr types.str; default = null; - description = lib.mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; - }; - - allowPlantumlInclude = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Enables !include processing which can read files from the server into diagrams. Files are read relative to the current working directory."; + description = mdDoc "When calling the proxy endpoint, the value of HTTP_AUTHORIZATION will be used to set the HTTP Authorization header."; }; }; }; config = mkIf cfg.enable { - users.users.${cfg.user} = { - isSystemUser = true; - group = cfg.group; - home = cfg.home; - createHome = true; - }; - - users.groups.${cfg.group} = {}; - systemd.services.plantuml-server = { description = "PlantUML server"; wantedBy = [ "multi-user.target" ]; path = [ cfg.home ]; + environment = { PLANTUML_LIMIT_SIZE = builtins.toString cfg.plantumlLimitSize; GRAPHVIZ_DOT = "${cfg.graphvizPackage}/bin/dot"; PLANTUML_STATS = if cfg.plantumlStats then "on" else "off"; HTTP_AUTHORIZATION = cfg.httpAuthorization; - ALLOW_PLANTUML_INCLUDE = if cfg.allowPlantumlInclude then "true" else "false"; }; script = '' ${cfg.packages.jdk}/bin/java \ @@ -128,13 +115,40 @@ in jetty.http.host=${cfg.listenHost} \ jetty.http.port=${builtins.toString cfg.listenPort} ''; + serviceConfig = { User = cfg.user; Group = cfg.group; + StateDirectory = mkIf (cfg.home == "/var/lib/plantuml") "plantuml"; + StateDirectoryMode = mkIf (cfg.home == "/var/lib/plantuml") "0750"; + + # Hardening + AmbientCapabilities = [ "" ]; + CapabilityBoundingSet = [ "" ]; + DynamicUser = true; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateNetwork = false; PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" ]; }; }; }; - meta.maintainers = with lib.maintainers; [ truh ]; + meta.maintainers = with lib.maintainers; [ truh anthonyroussel ]; } diff --git a/nixos/modules/services/web-apps/plausible.nix b/nixos/modules/services/web-apps/plausible.nix index 576b54a7edf2..300a0f892ef7 100644 --- a/nixos/modules/services/web-apps/plausible.nix +++ b/nixos/modules/services/web-apps/plausible.nix @@ -11,13 +11,6 @@ in { package = mkPackageOptionMD pkgs "plausible" { }; - releaseCookiePath = mkOption { - type = with types; either str path; - description = lib.mdDoc '' - The path to the file with release cookie. (used for remote connection to the running node). - ''; - }; - adminUser = { name = mkOption { default = "admin"; @@ -92,6 +85,13 @@ in { framework docs](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Secret.html#content). ''; }; + listenAddress = mkOption { + default = "127.0.0.1"; + type = types.str; + description = lib.mdDoc '' + The IP address on which the server is listening. + ''; + }; port = mkOption { default = 8000; type = types.port; @@ -162,6 +162,10 @@ in { }; }; + imports = [ + (mkRemovedOptionModule [ "services" "plausible" "releaseCookiePath" ] "Plausible uses no distributed Erlang features, so this option is no longer necessary and was removed") + ]; + config = mkIf cfg.enable { assertions = [ { assertion = cfg.adminUser.activate -> cfg.database.postgres.setup; @@ -180,8 +184,6 @@ in { enable = true; }; - services.epmd.enable = true; - environment.systemPackages = [ cfg.package ]; systemd.services = mkMerge [ @@ -209,6 +211,32 @@ in { # Configuration options from # https://plausible.io/docs/self-hosting-configuration PORT = toString cfg.server.port; + LISTEN_IP = cfg.server.listenAddress; + + # Note [plausible-needs-no-erlang-distributed-features]: + # Plausible does not use, and does not plan to use, any of + # Erlang's distributed features, see: + # https://github.com/plausible/analytics/pull/1190#issuecomment-1018820934 + # Thus, disable distribution for improved simplicity and security: + # + # When distribution is enabled, + # Elixir spwans the Erlang VM, which will listen by default on all + # interfaces for messages between Erlang nodes (capable of + # remote code execution); it can be protected by a cookie; see + # https://erlang.org/doc/reference_manual/distributed.html#security). + # + # It would be possible to restrict the interface to one of our choice + # (e.g. localhost or a VPN IP) similar to how we do it with `listenAddress` + # for the Plausible web server; if distribution is ever needed in the future, + # https://github.com/NixOS/nixpkgs/pull/130297 shows how to do it. + # + # But since Plausible does not use this feature in any way, + # we just disable it. + RELEASE_DISTRIBUTION = "none"; + # Additional safeguard, in case `RELEASE_DISTRIBUTION=none` ever + # stops disabling the start of EPMD. + ERL_EPMD_ADDRESS = "127.0.0.1"; + DISABLE_REGISTRATION = if isBool cfg.server.disableRegistration then boolToString cfg.server.disableRegistration else cfg.server.disableRegistration; RELEASE_TMP = "/var/lib/plausible/tmp"; @@ -238,7 +266,10 @@ in { path = [ cfg.package ] ++ optional cfg.database.postgres.setup config.services.postgresql.package; script = '' - export RELEASE_COOKIE="$(< $CREDENTIALS_DIRECTORY/RELEASE_COOKIE )" + # Elixir does not start up if `RELEASE_COOKIE` is not set, + # even though we set `RELEASE_DISTRIBUTION=none` so the cookie should be unused. + # Thus, make a random one, which should then be ignored. + export RELEASE_COOKIE=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20) export ADMIN_USER_PWD="$(< $CREDENTIALS_DIRECTORY/ADMIN_USER_PWD )" export SECRET_KEY_BASE="$(< $CREDENTIALS_DIRECTORY/SECRET_KEY_BASE )" @@ -265,7 +296,6 @@ in { LoadCredential = [ "ADMIN_USER_PWD:${cfg.adminUser.passwordFile}" "SECRET_KEY_BASE:${cfg.server.secretKeybaseFile}" - "RELEASE_COOKIE:${cfg.releaseCookiePath}" ] ++ lib.optionals (cfg.mail.smtp.passwordFile != null) [ "SMTP_USER_PWD:${cfg.mail.smtp.passwordFile}"]; }; }; diff --git a/nixos/modules/services/web-servers/jboss/builder.sh b/nixos/modules/services/web-servers/jboss/builder.sh index ac573089cd5a..8c49b87db060 100644 --- a/nixos/modules/services/web-servers/jboss/builder.sh +++ b/nixos/modules/services/web-servers/jboss/builder.sh @@ -1,6 +1,6 @@ set -e -if [ -e .attrs.sh ]; then source .attrs.sh; fi +if [ -e "$NIX_ATTRS_SH_FILE" ]; then . "$NIX_ATTRS_SH_FILE"; elif [ -f .attrs.sh ]; then . .attrs.sh; fi source $stdenv/setup mkdir -p $out/bin |