forked from mirrors/nixpkgs
2e751c0772
the conversion procedure is simple: - find all things that look like options, ie calls to either `mkOption` or `lib.mkOption` that take an attrset. remember the attrset as the option - for all options, find a `description` attribute who's value is not a call to `mdDoc` or `lib.mdDoc` - textually convert the entire value of the attribute to MD with a few simple regexes (the set from mdize-module.sh) - if the change produced a change in the manual output, discard - if the change kept the manual unchanged, add some text to the description to make sure we've actually found an option. if the manual changes this time, keep the converted description this procedure converts 80% of nixos options to markdown. around 2000 options remain to be inspected, but most of those fail the "does not change the manual output check": currently the MD conversion process does not faithfully convert docbook tags like <code> and <package>, so any option using such tags will not be converted at all.
480 lines
16 KiB
Nix
480 lines
16 KiB
Nix
{ lib, pkgs, config, options, ... }:
|
||
|
||
let
|
||
cfg = config.services.peertube;
|
||
opt = options.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 = lib.mdDoc "User account under which Peertube runs.";
|
||
};
|
||
|
||
group = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = "peertube";
|
||
description = lib.mdDoc "Group under which Peertube runs.";
|
||
};
|
||
|
||
localDomain = lib.mkOption {
|
||
type = lib.types.str;
|
||
example = "peertube.example.com";
|
||
description = lib.mdDoc "The domain serving your PeerTube instance.";
|
||
};
|
||
|
||
listenHttp = lib.mkOption {
|
||
type = lib.types.int;
|
||
default = 9000;
|
||
description = lib.mdDoc "listen port for HTTP server.";
|
||
};
|
||
|
||
listenWeb = lib.mkOption {
|
||
type = lib.types.int;
|
||
default = 9000;
|
||
description = lib.mdDoc "listen port for WEB server.";
|
||
};
|
||
|
||
enableWebHttps = lib.mkOption {
|
||
type = lib.types.bool;
|
||
default = false;
|
||
description = lib.mdDoc "Enable or disable HTTPS protocol.";
|
||
};
|
||
|
||
dataDirs = lib.mkOption {
|
||
type = lib.types.listOf lib.types.path;
|
||
default = [ ];
|
||
example = [ "/opt/peertube/storage" "/var/cache/peertube" ];
|
||
description = lib.mdDoc "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 = lib.mdDoc ''
|
||
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 = lib.mdDoc "Configuration for peertube.";
|
||
};
|
||
|
||
database = {
|
||
createLocally = lib.mkOption {
|
||
type = lib.types.bool;
|
||
default = false;
|
||
description = lib.mdDoc "Configure local PostgreSQL database server for PeerTube.";
|
||
};
|
||
|
||
host = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = if cfg.database.createLocally then "/run/postgresql" else null;
|
||
defaultText = lib.literalExpression ''
|
||
if config.${opt.database.createLocally}
|
||
then "/run/postgresql"
|
||
else null
|
||
'';
|
||
example = "192.168.15.47";
|
||
description = lib.mdDoc "Database host address or unix socket.";
|
||
};
|
||
|
||
port = lib.mkOption {
|
||
type = lib.types.int;
|
||
default = 5432;
|
||
description = lib.mdDoc "Database host port.";
|
||
};
|
||
|
||
name = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = "peertube";
|
||
description = lib.mdDoc "Database name.";
|
||
};
|
||
|
||
user = lib.mkOption {
|
||
type = lib.types.str;
|
||
default = "peertube";
|
||
description = lib.mdDoc "Database user.";
|
||
};
|
||
|
||
passwordFile = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.path;
|
||
default = null;
|
||
example = "/run/keys/peertube/password-posgressql-db";
|
||
description = lib.mdDoc "Password for PostgreSQL database.";
|
||
};
|
||
};
|
||
|
||
redis = {
|
||
createLocally = lib.mkOption {
|
||
type = lib.types.bool;
|
||
default = false;
|
||
description = lib.mdDoc "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;
|
||
defaultText = lib.literalExpression ''
|
||
if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket}
|
||
then "127.0.0.1"
|
||
else null
|
||
'';
|
||
description = lib.mdDoc "Redis host.";
|
||
};
|
||
|
||
port = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.port;
|
||
default = if cfg.redis.createLocally && cfg.redis.enableUnixSocket then null else 31638;
|
||
defaultText = lib.literalExpression ''
|
||
if config.${opt.redis.createLocally} && config.${opt.redis.enableUnixSocket}
|
||
then null
|
||
else 6379
|
||
'';
|
||
description = lib.mdDoc "Redis port.";
|
||
};
|
||
|
||
passwordFile = lib.mkOption {
|
||
type = lib.types.nullOr lib.types.path;
|
||
default = null;
|
||
example = "/run/keys/peertube/password-redis-db";
|
||
description = lib.mdDoc "Password for redis database.";
|
||
};
|
||
|
||
enableUnixSocket = lib.mkOption {
|
||
type = lib.types.bool;
|
||
default = cfg.redis.createLocally;
|
||
defaultText = lib.literalExpression "config.${opt.redis.createLocally}";
|
||
description = lib.mdDoc "Use Unix socket.";
|
||
};
|
||
};
|
||
|
||
smtp = {
|
||
createLocally = lib.mkOption {
|
||
type = lib.types.bool;
|
||
default = false;
|
||
description = lib.mdDoc "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 = lib.mdDoc "Password for smtp server.";
|
||
};
|
||
};
|
||
|
||
package = lib.mkOption {
|
||
type = lib.types.package;
|
||
default = pkgs.peertube;
|
||
defaultText = lib.literalExpression "pkgs.peertube";
|
||
description = lib.mdDoc "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/";
|
||
bin = lib.mkDefault "/var/lib/peertube/storage/bin/";
|
||
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/";
|
||
};
|
||
import = {
|
||
videos = {
|
||
http = {
|
||
youtube_dl_release = {
|
||
python_path = "${pkgs.python3}/bin/python";
|
||
};
|
||
};
|
||
};
|
||
};
|
||
}
|
||
(lib.mkIf cfg.redis.enableUnixSocket { redis = { socket = "/run/redis-peertube/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 python3 ];
|
||
|
||
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.servers.peertube = lib.mkMerge [
|
||
(lib.mkIf cfg.redis.createLocally {
|
||
enable = true;
|
||
})
|
||
(lib.mkIf (cfg.redis.createLocally && !cfg.redis.enableUnixSocket) {
|
||
bind = "127.0.0.1";
|
||
port = cfg.redis.port;
|
||
})
|
||
(lib.mkIf (cfg.redis.createLocally && cfg.redis.enableUnixSocket) {
|
||
unixSocket = "/run/redis-peertube/redis.sock";
|
||
unixSocketPerm = 660;
|
||
})
|
||
];
|
||
|
||
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-peertube" ];})
|
||
];
|
||
|
||
users.groups = lib.optionalAttrs (cfg.group == "peertube") {
|
||
peertube = { };
|
||
};
|
||
};
|
||
}
|