From f563ddece04adfd8d80d4e984405f5c70a6c94f3 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Wed, 2 Nov 2022 18:20:24 +0100 Subject: surtr: borg backup to vidhar --- hosts/surtr/borg.nix | 50 ++++ hosts/surtr/default.nix | 2 +- hosts/surtr/matrix/default.nix | 4 - modules/borgsnap/borgsnap/borgsnap/__main__.py | 4 +- modules/coturn.nix | 369 +++++++++++++++++++++++++ modules/zfssnap/default.nix | 34 ++- 6 files changed, 444 insertions(+), 19 deletions(-) create mode 100644 hosts/surtr/borg.nix create mode 100644 modules/coturn.nix diff --git a/hosts/surtr/borg.nix b/hosts/surtr/borg.nix new file mode 100644 index 00000000..b9fe53d7 --- /dev/null +++ b/hosts/surtr/borg.nix @@ -0,0 +1,50 @@ +{ lib, config, ... }: + +with lib; + +{ + config = { + services.borgsnap = { + enable = true; + target = "borg.vidhar:."; + + extraConfig = mkForce { + daily = "31"; + monthly = "-1"; + }; + + sshConfig = '' + Include /etc/ssh/ssh_config + + ControlMaster auto + ControlPath /var/lib/borg/.borgssh-master-%r@%n:%p + ControlPersist yes + + Host borg.vidhar + HostName vidhar.yggdrasil.li + User borg + IdentityFile ${config.sops.secrets."append.borg.vidhar".path} + IdentitiesOnly yes + + BatchMode yes + ServerAliveInterval 10 + ServerAliveCountMax 30 + ''; + }; + + sops.secrets."append.borg.vidhar" = { + format = "binary"; + sopsFile = ../vidhar/borg/jotnar/surtr; + owner = "borg"; + group = "borg"; + mode = "0400"; + }; + + users.users.borg = { + useDefaultShell = true; + isSystemUser = true; + group = "borg"; + }; + users.groups.borg = {}; + }; +} diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index f616d749..cebb2b6c 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix @@ -2,7 +2,7 @@ { imports = with flake.nixosModules.systemProfiles; [ tmpfs-root qemu-guest openssh rebuild-machines zfs - ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix ./prometheus ./email ./vpn + ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix ./prometheus ./email ./vpn ./borg.nix ]; config = { diff --git a/hosts/surtr/matrix/default.nix b/hosts/surtr/matrix/default.nix index 46c2f338..f5a411ac 100644 --- a/hosts/surtr/matrix/default.nix +++ b/hosts/surtr/matrix/default.nix @@ -228,10 +228,6 @@ with lib; "turn.synapse.li" = { zone = "synapse.li"; certCfg = { - server = "https://acme.zerossl.com/v2/DV90"; - extraLegoFlags = [ - "--cert.timeout" "300" - ]; postRun = '' ${pkgs.systemd}/bin/systemctl try-restart coturn.service ''; diff --git a/modules/borgsnap/borgsnap/borgsnap/__main__.py b/modules/borgsnap/borgsnap/borgsnap/__main__.py index 91144780..ad46a7bf 100644 --- a/modules/borgsnap/borgsnap/borgsnap/__main__.py +++ b/modules/borgsnap/borgsnap/borgsnap/__main__.py @@ -246,7 +246,9 @@ def create(*, snapshot, target, archive_prefix, dry_run): env['BORG_FILES_CACHE_SUFFIX'] = basename archive_name = _archive_name(snapshot, target, archive_prefix) target_host, _, target_path = target.rpartition(':') - *parents_init, _ = list(Path(target_path).parents) + parents_init = list() + if Path(target_path).parents: + *parents_init, _ = list(Path(target_path).parents) backup_patterns = [*(map(lambda p: Path('.backup') / f'{target_host}:{p}', [Path(target_path), *parents_init])), Path('.backup') / target_host, Path('.backup')] for pattern_file in backup_patterns: if (dir / pattern_file).is_file(): diff --git a/modules/coturn.nix b/modules/coturn.nix new file mode 100644 index 00000000..faa4b5a2 --- /dev/null +++ b/modules/coturn.nix @@ -0,0 +1,369 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.coturn; + pidfile = "/run/turnserver/turnserver.pid"; + configFile = pkgs.writeText "turnserver.conf" '' +listening-port=${toString cfg.listening-port} +tls-listening-port=${toString cfg.tls-listening-port} +alt-listening-port=${toString cfg.alt-listening-port} +alt-tls-listening-port=${toString cfg.alt-tls-listening-port} +${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)} +${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)} +min-port=${toString cfg.min-port} +max-port=${toString cfg.max-port} +${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"} +${lib.optionalString cfg.no-auth "no-auth"} +${lib.optionalString cfg.use-auth-secret "use-auth-secret"} +${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")} +${lib.optionalString (cfg.static-auth-secret-file != null) ("static-auth-secret=#static-auth-secret#")} +realm=${cfg.realm} +${lib.optionalString cfg.no-udp "no-udp"} +${lib.optionalString cfg.no-tcp "no-tcp"} +${lib.optionalString cfg.no-tls "no-tls"} +${lib.optionalString cfg.no-dtls "no-dtls"} +${lib.optionalString cfg.no-udp-relay "no-udp-relay"} +${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"} +${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"} +${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"} +${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")} +no-stdout-log +syslog +pidfile=${pidfile} +${lib.optionalString cfg.secure-stun "secure-stun"} +${lib.optionalString cfg.no-cli "no-cli"} +cli-ip=${cfg.cli-ip} +cli-port=${toString cfg.cli-port} +${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")} +${cfg.extraConfig} +''; +in { + disabledModules = [ "services/networking/coturn.nix" ]; + + options = { + services.coturn = { + enable = mkEnableOption (lib.mdDoc "coturn TURN server"); + listening-port = mkOption { + type = types.int; + default = 3478; + description = lib.mdDoc '' + TURN listener port for UDP and TCP. + Note: actually, TLS and DTLS sessions can connect to the + "plain" TCP and UDP port(s), too - if allowed by configuration. + ''; + }; + tls-listening-port = mkOption { + type = types.int; + default = 5349; + description = lib.mdDoc '' + TURN listener port for TLS. + Note: actually, "plain" TCP and UDP sessions can connect to the TLS and + DTLS port(s), too - if allowed by configuration. The TURN server + "automatically" recognizes the type of traffic. Actually, two listening + endpoints (the "plain" one and the "tls" one) are equivalent in terms of + functionality; but we keep both endpoints to satisfy the RFC 5766 specs. + For secure TCP connections, we currently support SSL version 3 and + TLS version 1.0, 1.1 and 1.2. + For secure UDP connections, we support DTLS version 1. + ''; + }; + alt-listening-port = mkOption { + type = types.int; + default = cfg.listening-port + 1; + defaultText = literalExpression "listening-port + 1"; + description = lib.mdDoc '' + Alternative listening port for UDP and TCP listeners; + default (or zero) value means "listening port plus one". + This is needed for RFC 5780 support + (STUN extension specs, NAT behavior discovery). The TURN Server + supports RFC 5780 only if it is started with more than one + listening IP address of the same family (IPv4 or IPv6). + RFC 5780 is supported only by UDP protocol, other protocols + are listening to that endpoint only for "symmetry". + ''; + }; + alt-tls-listening-port = mkOption { + type = types.int; + default = cfg.tls-listening-port + 1; + defaultText = literalExpression "tls-listening-port + 1"; + description = lib.mdDoc '' + Alternative listening port for TLS and DTLS protocols. + ''; + }; + listening-ips = mkOption { + type = types.listOf types.str; + default = []; + example = [ "203.0.113.42" "2001:DB8::42" ]; + description = lib.mdDoc '' + Listener IP addresses of relay server. + If no IP(s) specified in the config file or in the command line options, + then all IPv4 and IPv6 system IPs will be used for listening. + ''; + }; + relay-ips = mkOption { + type = types.listOf types.str; + default = []; + example = [ "203.0.113.42" "2001:DB8::42" ]; + description = lib.mdDoc '' + Relay address (the local IP address that will be used to relay the + packets to the peer). + Multiple relay addresses may be used. + The same IP(s) can be used as both listening IP(s) and relay IP(s). + + If no relay IP(s) specified, then the turnserver will apply the default + policy: it will decide itself which relay addresses to be used, and it + will always be using the client socket IP address as the relay IP address + of the TURN session (if the requested relay address family is the same + as the family of the client socket). + ''; + }; + min-port = mkOption { + type = types.int; + default = 49152; + description = lib.mdDoc '' + Lower bound of UDP relay endpoints + ''; + }; + max-port = mkOption { + type = types.int; + default = 65535; + description = lib.mdDoc '' + Upper bound of UDP relay endpoints + ''; + }; + lt-cred-mech = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Use long-term credential mechanism. + ''; + }; + no-auth = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + This option is opposite to lt-cred-mech. + (TURN Server with no-auth option allows anonymous access). + If neither option is defined, and no users are defined, + then no-auth is default. If at least one user is defined, + in this file or in command line or in usersdb file, then + lt-cred-mech is default. + ''; + }; + use-auth-secret = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + TURN REST API flag. + Flag that sets a special authorization option that is based upon authentication secret. + This feature can be used with the long-term authentication mechanism, only. + This feature purpose is to support "TURN Server REST API", see + "TURN REST API" link in the project's page + https://github.com/coturn/coturn/ + + This option is used with timestamp: + + usercombo -> "timestamp:userid" + turn user -> usercombo + turn password -> base64(hmac(secret key, usercombo)) + + This allows TURN credentials to be accounted for a specific user id. + If you don't have a suitable id, the timestamp alone can be used. + This option is just turning on secret-based authentication. + The actual value of the secret is defined either by option static-auth-secret, + or can be found in the turn_secret table in the database. + ''; + }; + static-auth-secret = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + 'Static' authentication secret value (a string) for TURN REST API only. + If not set, then the turn server + will try to use the 'dynamic' value in turn_secret table + in user database (if present). The database-stored value can be changed on-the-fly + by a separate program, so this is why that other mode is 'dynamic'. + ''; + }; + static-auth-secret-file = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Path to the file containing the static authentication secret. + ''; + }; + realm = mkOption { + type = types.str; + default = config.networking.hostName; + defaultText = literalExpression "config.networking.hostName"; + example = "example.com"; + description = lib.mdDoc '' + The default realm to be used for the users when no explicit + origin/realm relationship was found in the database, or if the TURN + server is not using any database (just the commands-line settings + and the userdb file). Must be used with long-term credentials + mechanism or with TURN REST API. + ''; + }; + cert = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/acme/example.com/fullchain.pem"; + description = lib.mdDoc '' + Certificate file in PEM format. + ''; + }; + pkey = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/acme/example.com/key.pem"; + description = lib.mdDoc '' + Private key file in PEM format. + ''; + }; + dh-file = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Use custom DH TLS key, stored in PEM format in the file. + ''; + }; + secure-stun = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Require authentication of the STUN Binding request. + By default, the clients are allowed anonymous access to the STUN Binding functionality. + ''; + }; + no-cli = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Turn OFF the CLI support. + ''; + }; + cli-ip = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Local system IP address to be used for CLI server endpoint. + ''; + }; + cli-port = mkOption { + type = types.int; + default = 5766; + description = lib.mdDoc '' + CLI server port. + ''; + }; + cli-password = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + CLI access password. + For the security reasons, it is recommended to use the encrypted + for of the password (see the -P command in the turnadmin utility). + ''; + }; + no-udp = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable UDP client listener"; + }; + no-tcp = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable TCP client listener"; + }; + no-tls = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable TLS client listener"; + }; + no-dtls = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable DTLS client listener"; + }; + no-udp-relay = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable UDP relay endpoints"; + }; + no-tcp-relay = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Disable TCP relay endpoints"; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = lib.mdDoc "Additional configuration options"; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge ([ + { assertions = [ + { assertion = cfg.static-auth-secret != null -> cfg.static-auth-secret-file == null ; + message = "static-auth-secret and static-auth-secret-file cannot be set at the same time"; + } + ];} + + { + users.users.turnserver = + { uid = config.ids.uids.turnserver; + group = "turnserver"; + description = "coturn TURN server user"; + }; + users.groups.turnserver = + { gid = config.ids.gids.turnserver; + members = [ "turnserver" ]; + }; + + systemd.services.coturn = let + runConfig = "/run/coturn/turnserver.cfg"; + in { + description = "coturn TURN server"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + unitConfig = { + Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)"; + }; + + script = '' + cat ${configFile} > ${runConfig} + ${optionalString (cfg.static-auth-secret-file != null) '' + ${pkgs.replace-secret}/bin/replace-secret \ + "#static-auth-secret#" \ + ${cfg.static-auth-secret-file} \ + ${runConfig} + '' } + chmod 640 ${runConfig} + + exec ${pkgs.coturn}/bin/turnserver -c ${runConfig} + ''; + serviceConfig = { + Type = "simple"; + RuntimeDirectory = "turnserver"; + User = "turnserver"; + Group = "turnserver"; + AmbientCapabilities = + mkIf ( + cfg.listening-port < 1024 || + cfg.alt-listening-port < 1024 || + cfg.tls-listening-port < 1024 || + cfg.alt-tls-listening-port < 1024 || + cfg.min-port < 1024 + ) "cap_net_bind_service"; + Restart = "on-abort"; + }; + }; + systemd.tmpfiles.rules = [ + "d /run/coturn 0700 turnserver turnserver - -" + ]; + }])); +} diff --git a/modules/zfssnap/default.nix b/modules/zfssnap/default.nix index f6f32852..23041c36 100644 --- a/modules/zfssnap/default.nix +++ b/modules/zfssnap/default.nix @@ -27,19 +27,27 @@ in { enable = mkEnableOption "zfssnap service"; config = mkOption { - type = with types; attrsOf (attrsOf str); - default = { - keep = { - within = "15m"; - "5m" = "48"; - "15m" = "32"; - hourly = "48"; - "4h" = "24"; - "12h" = "12"; - daily = "62"; - halfweekly = "32"; - weekly = "24"; - monthly = "-1"; + type = types.submodule { + options = { + keep = mkOption { + type = with types; attrsOf str; + default = { + within = "15m"; + "5m" = "48"; + "15m" = "32"; + hourly = "48"; + "4h" = "24"; + "12h" = "12"; + daily = "62"; + halfweekly = "32"; + weekly = "24"; + monthly = "-1"; + }; + }; + exec = mkOption { + type = with types; attrsOf str; + default = {}; + }; }; }; }; -- cgit v1.2.3