diff options
author | Izorkin <izorkin@elven.pw> | 2021-04-11 11:11:11 +0300 |
---|---|---|
committer | Raphael Megzari <raphael@megzari.com> | 2021-10-25 13:12:30 +0900 |
commit | 4c092350ed0375f24d64918f77b5c14bc250ee33 (patch) | |
tree | 1f745e167d047da6ca8626c309329d2581efac6d /nixos/modules/services/web-apps/peertube.nix | |
parent | 5729103df3e35b5139deac00cec9739da5d57ba5 (diff) | |
download | nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar.gz nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar.bz2 nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar.lz nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar.xz nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.tar.zst nixlib-4c092350ed0375f24d64918f77b5c14bc250ee33.zip |
nixos/peertube: init service
Co-authored-by: Moritz Hedtke <Moritz.Hedtke@t-online.de> Co-authored-by: Steven Roose <steven@stevenroose.org> Co-authored-by: Matthias Beyer <mail@beyermatthias.de> Co-authored-by: Ismaël Bouya <ismael.bouya@normalesup.org>
Diffstat (limited to 'nixos/modules/services/web-apps/peertube.nix')
-rw-r--r-- | nixos/modules/services/web-apps/peertube.nix | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/nixos/modules/services/web-apps/peertube.nix b/nixos/modules/services/web-apps/peertube.nix new file mode 100644 index 000000000000..362a3358b793 --- /dev/null +++ b/nixos/modules/services/web-apps/peertube.nix @@ -0,0 +1,447 @@ +{ lib, pkgs, config, ... }: + +let + cfg = config.services.peertube; + + settingsFormat = pkgs.formats.json {}; + configFile = settingsFormat.generate "production.json" cfg.settings; + + env = { + NODE_CONFIG_DIR = "/var/lib/peertube/config"; + NODE_ENV = "production"; + NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt"; + NPM_CONFIG_PREFIX = cfg.package; + HOME = cfg.package; + }; + + systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@memlock" "@mount" "@obsolete" "@privileged" "@setuid" ]; + + cfgService = { + # Proc filesystem + ProcSubset = "pid"; + ProtectProc = "invisible"; + # Access write directories + UMask = "0027"; + # Capabilities + CapabilityBoundingSet = ""; + # Security + NoNewPrivileges = true; + # Sandboxing + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + LockPersonality = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + # System Call Filtering + SystemCallArchitectures = "native"; + }; + + envFile = pkgs.writeText "peertube.env" (lib.concatMapStrings (s: s + "\n") ( + (lib.concatLists (lib.mapAttrsToList (name: value: + if value != null then [ + "${name}=\"${toString value}\"" + ] else [] + ) env)))); + + peertubeEnv = pkgs.writeShellScriptBin "peertube-env" '' + set -a + source "${envFile}" + eval -- "\$@" + ''; + + peertubeCli = pkgs.writeShellScriptBin "peertube" '' + node ~/dist/server/tools/peertube.js $@ + ''; + +in { + options.services.peertube = { + enable = lib.mkEnableOption "Enable Peertube’s service"; + + user = lib.mkOption { + type = lib.types.str; + default = "peertube"; + description = "User account under which Peertube runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "peertube"; + description = "Group under which Peertube runs."; + }; + + localDomain = lib.mkOption { + type = lib.types.str; + example = "peertube.example.com"; + description = "The domain serving your PeerTube instance."; + }; + + listenHttp = lib.mkOption { + type = lib.types.int; + default = 9000; + description = "listen port for HTTP server."; + }; + + listenWeb = lib.mkOption { + type = lib.types.int; + default = 9000; + description = "listen port for WEB server."; + }; + + enableWebHttps = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable or disable HTTPS protocol."; + }; + + dataDirs = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + example = [ "/opt/peertube/storage" "/var/cache/peertube" ]; + description = "Allow access to custom data locations."; + }; + + serviceEnvironmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/peertube/password-init-root"; + description = '' + Set environment variables for the service. Mainly useful for setting the initial root password. + For example write to file: + PT_INITIAL_ROOT_PASSWORD=changeme + ''; + }; + + settings = lib.mkOption { + type = settingsFormat.type; + example = lib.literalExpression '' + { + listen = { + hostname = "0.0.0.0"; + }; + log = { + level = "debug"; + }; + storage = { + tmp = "/opt/data/peertube/storage/tmp/"; + logs = "/opt/data/peertube/storage/logs/"; + cache = "/opt/data/peertube/storage/cache/"; + }; + } + ''; + description = "Configuration for peertube."; + }; + + database = { + createLocally = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Configure local PostgreSQL database server for PeerTube."; + }; + + host = lib.mkOption { + type = lib.types.str; + default = if cfg.database.createLocally then "/run/postgresql" else null; + example = "192.168.15.47"; + description = "Database host address or unix socket."; + }; + + port = lib.mkOption { + type = lib.types.int; + default = 5432; + description = "Database host port."; + }; + + name = lib.mkOption { + type = lib.types.str; + default = "peertube"; + description = "Database name."; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "peertube"; + description = "Database user."; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/peertube/password-posgressql-db"; + description = "Password for PostgreSQL database."; + }; + }; + + redis = { + createLocally = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Configure local Redis server for PeerTube."; + }; + + host = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then "127.0.0.1" else null; + description = "Redis host."; + }; + + port = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = if cfg.redis.createLocally && cfg.redis.enableUnixSocket then null else 6379; + description = "Redis port."; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/peertube/password-redis-db"; + description = "Password for redis database."; + }; + + enableUnixSocket = lib.mkOption { + type = lib.types.bool; + default = cfg.redis.createLocally; + description = "Use Unix socket."; + }; + }; + + smtp = { + createLocally = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Configure local Postfix SMTP server for PeerTube."; + }; + + passwordFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + example = "/run/keys/peertube/password-smtp"; + description = "Password for smtp server."; + }; + }; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.peertube; + description = "Peertube package to use."; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { assertion = cfg.serviceEnvironmentFile == null || !lib.hasPrefix builtins.storeDir cfg.serviceEnvironmentFile; + message = '' + <option>services.peertube.serviceEnvironmentFile</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + { assertion = !(cfg.redis.enableUnixSocket && (cfg.redis.host != null || cfg.redis.port != null)); + message = '' + <option>services.peertube.redis.createLocally</option> and redis network connection (<option>services.peertube.redis.host</option> or <option>services.peertube.redis.port</option>) enabled. Disable either of them. + ''; + } + { assertion = cfg.redis.enableUnixSocket || (cfg.redis.host != null && cfg.redis.port != null); + message = '' + <option>services.peertube.redis.host</option> and <option>services.peertube.redis.port</option> needs to be set if <option>services.peertube.redis.enableUnixSocket</option> is not enabled. + ''; + } + { assertion = cfg.redis.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.redis.passwordFile; + message = '' + <option>services.peertube.redis.passwordFile</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + { assertion = cfg.database.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.database.passwordFile; + message = '' + <option>services.peertube.database.passwordFile</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + { assertion = cfg.smtp.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.smtp.passwordFile; + message = '' + <option>services.peertube.smtp.passwordFile</option> points to + a file in the Nix store. You should use a quoted absolute path to + prevent this. + ''; + } + ]; + + services.peertube.settings = lib.mkMerge [ + { + listen = { + port = cfg.listenHttp; + }; + webserver = { + https = (if cfg.enableWebHttps then true else false); + hostname = "${cfg.localDomain}"; + port = cfg.listenWeb; + }; + database = { + hostname = "${cfg.database.host}"; + port = cfg.database.port; + name = "${cfg.database.name}"; + username = "${cfg.database.user}"; + }; + redis = { + hostname = "${toString cfg.redis.host}"; + port = (if cfg.redis.port == null then "" else cfg.redis.port); + }; + storage = { + tmp = lib.mkDefault "/var/lib/peertube/storage/tmp/"; + avatars = lib.mkDefault "/var/lib/peertube/storage/avatars/"; + videos = lib.mkDefault "/var/lib/peertube/storage/videos/"; + streaming_playlists = lib.mkDefault "/var/lib/peertube/storage/streaming-playlists/"; + redundancy = lib.mkDefault "/var/lib/peertube/storage/redundancy/"; + logs = lib.mkDefault "/var/lib/peertube/storage/logs/"; + previews = lib.mkDefault "/var/lib/peertube/storage/previews/"; + thumbnails = lib.mkDefault "/var/lib/peertube/storage/thumbnails/"; + torrents = lib.mkDefault "/var/lib/peertube/storage/torrents/"; + captions = lib.mkDefault "/var/lib/peertube/storage/captions/"; + cache = lib.mkDefault "/var/lib/peertube/storage/cache/"; + plugins = lib.mkDefault "/var/lib/peertube/storage/plugins/"; + client_overrides = lib.mkDefault "/var/lib/peertube/storage/client-overrides/"; + }; + } + (lib.mkIf cfg.redis.enableUnixSocket { redis = { socket = "/run/redis/redis.sock"; }; }) + ]; + + systemd.tmpfiles.rules = [ + "d '/var/lib/peertube/config' 0700 ${cfg.user} ${cfg.group} - -" + "z '/var/lib/peertube/config' 0700 ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.peertube-init-db = lib.mkIf cfg.database.createLocally { + description = "Initialization database for PeerTube daemon"; + after = [ "network.target" "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + + script = let + psqlSetupCommands = pkgs.writeText "peertube-init.sql" '' + SELECT 'CREATE USER "${cfg.database.user}"' WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${cfg.database.user}')\gexec + SELECT 'CREATE DATABASE "${cfg.database.name}" OWNER "${cfg.database.user}" TEMPLATE template0 ENCODING UTF8' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${cfg.database.name}')\gexec + \c '${cfg.database.name}' + CREATE EXTENSION IF NOT EXISTS pg_trgm; + CREATE EXTENSION IF NOT EXISTS unaccent; + ''; + in "${config.services.postgresql.package}/bin/psql -f ${psqlSetupCommands}"; + + serviceConfig = { + Type = "oneshot"; + WorkingDirectory = cfg.package; + # User and group + User = "postgres"; + Group = "postgres"; + # Sandboxing + RestrictAddressFamilies = [ "AF_UNIX" ]; + MemoryDenyWriteExecute = true; + # System Call Filtering + SystemCallFilter = "~" + lib.concatStringsSep " " (systemCallsList ++ [ "@resources" ]); + } // cfgService; + }; + + systemd.services.peertube = { + description = "PeerTube daemon"; + after = [ "network.target" ] + ++ lib.optionals cfg.redis.createLocally [ "redis.service" ] + ++ lib.optionals cfg.database.createLocally [ "postgresql.service" "peertube-init-db.service" ]; + wantedBy = [ "multi-user.target" ]; + + environment = env; + + path = with pkgs; [ bashInteractive ffmpeg nodejs-16_x openssl yarn youtube-dl ]; + + script = '' + #!/bin/sh + umask 077 + cat > /var/lib/peertube/config/local.yaml <<EOF + ${lib.optionalString ((!cfg.database.createLocally) && (cfg.database.passwordFile != null)) '' + database: + password: '$(cat ${cfg.database.passwordFile})' + ''} + ${lib.optionalString (cfg.redis.passwordFile != null) '' + redis: + auth: '$(cat ${cfg.redis.passwordFile})' + ''} + ${lib.optionalString (cfg.smtp.passwordFile != null) '' + smtp: + password: '$(cat ${cfg.smtp.passwordFile})' + ''} + EOF + ln -sf ${cfg.package}/config/default.yaml /var/lib/peertube/config/default.yaml + ln -sf ${configFile} /var/lib/peertube/config/production.json + npm start + ''; + serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = 20; + TimeoutSec = 60; + WorkingDirectory = cfg.package; + # User and group + User = cfg.user; + Group = cfg.group; + # State directory and mode + StateDirectory = "peertube"; + StateDirectoryMode = "0750"; + # Access write directories + ReadWritePaths = cfg.dataDirs; + # Environment + EnvironmentFile = cfg.serviceEnvironmentFile; + # Sandboxing + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ]; + MemoryDenyWriteExecute = false; + # System Call Filtering + SystemCallFilter = [ ("~" + lib.concatStringsSep " " systemCallsList) "pipe" "pipe2" ]; + } // cfgService; + }; + + services.postgresql = lib.mkIf cfg.database.createLocally { + enable = true; + }; + + services.redis = lib.mkMerge [ + (lib.mkIf cfg.redis.createLocally { + enable = true; + }) + (lib.mkIf (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { + unixSocket = "/run/redis/redis.sock"; + unixSocketPerm = 770; + }) + ]; + + services.postfix = lib.mkIf cfg.smtp.createLocally { + enable = true; + hostname = lib.mkDefault "${cfg.localDomain}"; + }; + + users.users = lib.mkMerge [ + (lib.mkIf (cfg.user == "peertube") { + peertube = { + isSystemUser = true; + group = cfg.group; + home = cfg.package; + }; + }) + (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs-16_x pkgs.yarn ]) + (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis" ];}) + ]; + + users.groups = lib.optionalAttrs (cfg.group == "peertube") { + peertube = { }; + }; + }; +} |