From f6e600c20d6a97ebeda23fa2bb5621646222b2b0 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 2 Jan 2021 20:53:17 +0100 Subject: sif: import config --- modules/borgbackup/btrfs-snapshots.nix | 52 ++++ modules/borgbackup/default.nix | 199 +++++++++++++ modules/borgbackup/lvm-snapshots.nix | 133 +++++++++ modules/kill-user.nix | 13 + modules/tinc-networkmanager.nix | 36 +++ modules/uucp.nix | 391 ++++++++++++++++++++++++++ modules/yggdrasil/default.nix | 49 ++++ modules/yggdrasil/hosts/sif/default.nix | 13 + modules/yggdrasil/hosts/sif/private-keys.yaml | 34 +++ modules/yggdrasil/hosts/ymir.nix | 19 ++ 10 files changed, 939 insertions(+) create mode 100644 modules/borgbackup/btrfs-snapshots.nix create mode 100644 modules/borgbackup/default.nix create mode 100644 modules/borgbackup/lvm-snapshots.nix create mode 100644 modules/kill-user.nix create mode 100644 modules/tinc-networkmanager.nix create mode 100644 modules/uucp.nix create mode 100644 modules/yggdrasil/default.nix create mode 100644 modules/yggdrasil/hosts/sif/default.nix create mode 100644 modules/yggdrasil/hosts/sif/private-keys.yaml create mode 100644 modules/yggdrasil/hosts/ymir.nix (limited to 'modules') diff --git a/modules/borgbackup/btrfs-snapshots.nix b/modules/borgbackup/btrfs-snapshots.nix new file mode 100644 index 00000000..96d2b2ba --- /dev/null +++ b/modules/borgbackup/btrfs-snapshots.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.btrfs-snapshots; + + snapshotMount = str: "${str}${cfg.mountSuffix}"; +in { + options = { + + services.btrfs-snapshots = { + enable = mkEnableOption "a systemd unit for btrfs snapshots"; + + mountSuffix = mkOption { + type = types.str; + default = ".snapshot"; + }; + + readOnly = mkOption { + type = types.bool; + default = true; + }; + + persist = mkOption { + type = types.bool; + default = false; + }; + }; + + }; + + + config = mkIf cfg.enable { + systemd.services."btrfs-snapshot@" = { + enable = true; + + unitConfig = { + StopWhenUnneeded = !cfg.persist; + }; + + serviceConfig = with pkgs; { + Type = "oneshot"; + ExecStartPre = "-${btrfs-progs}/bin/btrfs subvolume delete -c ${snapshotMount "%f"}"; + ExecStart = "${btrfs-progs}/bin/btrfs subvolume snapshot ${optionalString cfg.readOnly "-r"} %f ${snapshotMount "%f"}"; + RemainAfterExit = true; + ExecStop = "${btrfs-progs}/bin/btrfs subvolume delete -c ${snapshotMount "%f"}"; + }; + }; + + }; +} diff --git a/modules/borgbackup/default.nix b/modules/borgbackup/default.nix new file mode 100644 index 00000000..47f8e06d --- /dev/null +++ b/modules/borgbackup/default.nix @@ -0,0 +1,199 @@ +{ config, lib, utils, pkgs, ... }: + +with utils; +with lib; + +let + cfg = config.services.borgbackup; + + lvmPath = { + options = { + LV = mkOption { + type = types.str; + }; + VG = mkOption { + type = types.str; + }; + }; + }; + + pathType = if cfg.snapshots == "lvm" then types.submodule lvmPath else types.path; + + systemdPath = path: escapeSystemdPath (if cfg.snapshots == "lvm" then "${path.VG}-${path.LV}" else path); + + withSuffix = path: path + (if cfg.snapshots == "btrfs" then config.services.btrfs-snapshots.mountSuffix else config.services.lvm-snapshots.mountSuffix); + + mountPoint = if cfg.snapshots == "lvm" then config.services.lvm-snapshots.mountPoint else ""; + + targetOptions = { + options = { + repo = mkOption { + type = types.str; + }; + + paths = mkOption { + type = types.listOf pathType; + default = []; + }; + + prune = mkOption { + type = types.attrsOf (types.listOf types.str); + default = {}; + }; + + interval = mkOption { + type = types.str; + default = "6h"; + }; + + jitter = mkOption { + type = with types; nullOr str; + default = "6h"; + }; + + lock = mkOption { + type = types.nullOr types.str; + default = "backup"; + }; + + network = mkOption { + type = types.bool; + default = true; + }; + + lockWait = mkOption { + type = types.int; + default = 600; + }; + }; + }; +in { + disabledModules = [ "services/backup/borgbackup.nix" ]; + + options = { + services.borgbackup = { + snapshots = mkOption { + type = types.nullOr (types.enum ["btrfs" "lvm"]); + default = null; + }; + + targets = mkOption { + type = types.attrsOf (types.submodule targetOptions); + default = {}; + }; + + prefix = mkOption { + type = types.str; + }; + }; + }; + + imports = + [ ./lvm-snapshots.nix + ./btrfs-snapshots.nix + ]; + + config = mkIf (any (t: t.paths != []) (attrValues cfg.targets)) { + services.btrfs-snapshots.enable = mkIf (cfg.snapshots == "btrfs") true; + + services.lvm-snapshots.snapshots = mkIf (cfg.snapshots == "lvm") (listToAttrs (map (path: nameValuePair (path.VG + "-" + path.LV) { + inherit (path) LV VG; + mountName = withSuffix (path.VG + "-" + path.LV); + }) (unique (flatten (mapAttrsToList (target: tCfg: tCfg.paths) cfg.targets))))); + + systemd.targets."timers-borg" = { + wantedBy = [ "timers.target" ]; + }; + + systemd.slices."system-borgbackup" = {}; + + systemd.timers = (listToAttrs (map ({ target, path, tCfg }: nameValuePair "borgbackup-${target}@${systemdPath path}" { + requiredBy = [ "timers-borg.target" ]; + + timerConfig = { + Persistent = false; + OnBootSec = tCfg.interval; + OnUnitActiveSec = tCfg.interval; + RandomizedDelaySec = mkIf (tCfg.jitter != null) tCfg.jitter; + }; + }) (flatten (mapAttrsToList (target: tCfg: map (path: { inherit target path tCfg; }) tCfg.paths) cfg.targets)))) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" { + enable = tCfg.prune != {}; + + requiredBy = [ "timers-borg.target" ]; + + timerConfig = { + Persistent = false; + OnBootSec = tCfg.interval; + OnUnitActiveSec = tCfg.interval; + RandomizedDelaySec = mkIf (tCfg.jitter != null) tCfg.jitter; + }; + }) cfg.targets); + + systemd.services = (mapAttrs' (target: tCfg: nameValuePair "borgbackup-${target}@" (let + deps = flatten [ + (optional (cfg.snapshots == "btrfs") "btrfs-snapshot@%i.service") + (optional tCfg.network "network-online.target") + ]; + in { + bindsTo = deps; + after = deps; + + path = with pkgs; [borgbackup] ++ optional (tCfg.lock != null) utillinux; + + script = let + borgCmd = '' + borg create \ + --lock-wait ${toString tCfg.lockWait} \ + --stats \ + --list \ + --filter 'AME' \ + --exclude-caches \ + --keep-exclude-tags \ + --patterns-from .backup-${target} \ + --one-file-system \ + --compression auto,lzma \ + ${tCfg.repo}::${cfg.prefix}$1-{utcnow} + ''; + in if tCfg.lock == null then borgCmd else "flock -xo /var/lock/${tCfg.lock} ${borgCmd}"; + scriptArgs = if cfg.snapshots == "lvm" then "%I" else "%i"; + + unitConfig = { + AssertPathIsDirectory = mkIf (tCfg.lock != null) "/var/lock"; + DefaultDependencies = false; + RequiresMountsFor = mkIf (cfg.snapshots == "lvm") [ "${mountPoint}/${withSuffix "%I"}" ]; + }; + + serviceConfig = { + Type = "oneshot"; + WorkingDirectory = if (cfg.snapshots == null) then "%I" else (if (cfg.snapshots == "lvm") then "${mountPoint}/${withSuffix "%I"}" else "${withSuffix "%f"}"); + Nice = 15; + IOSchedulingClass = 2; + IOSchedulingPriority = 7; + SuccessExitStatus = [1 2]; + Slice = "system-borgbackup.slice"; + }; + })) cfg.targets) // (mapAttrs' (target: tCfg: nameValuePair "borgbackup-prune-${target}" { + enable = tCfg.prune != {}; + + bindsTo = ["network-online.target"]; + after = ["network-online.target"]; + + path = with pkgs; [borgbackup]; + + script = concatStringsSep "\n" (mapAttrsToList (path: args: '' + borg prune \ + --lock-wait ${toString tCfg.lockWait} \ + --list \ + --stats \ + --prefix ${escapeShellArg "${cfg.prefix}${path}"} \ + ${escapeShellArgs args} \ + ${tCfg.repo} + '') tCfg.prune); + + serviceConfig = { + Type = "oneshot"; + Slice = "system-borgbackup.slice"; + }; + }) cfg.targets); + }; +} diff --git a/modules/borgbackup/lvm-snapshots.nix b/modules/borgbackup/lvm-snapshots.nix new file mode 100644 index 00000000..9b2a6562 --- /dev/null +++ b/modules/borgbackup/lvm-snapshots.nix @@ -0,0 +1,133 @@ +{ config, lib, utils, pkgs, ... }: + +with utils; +with lib; + +let + cfg = config.services.lvm-snapshots; + + snapshotMount = name: "${cfg.mountPoint}/${if isNull cfg.snapshots."${name}".mountName then name else cfg.snapshots."${name}".mountName}"; + snapshotName = name: "${name}-${cfg.mountSuffix}"; + + snapshotConfig = { + options = { + LV = mkOption { + type = types.str; + }; + + VG = mkOption { + type = types.str; + }; + + mountName = mkOption { + type = types.nullOr types.str; + default = null; + }; + + cowSize = mkOption { + type = types.str; + default = "-l20%ORIGIN"; + }; + + readOnly = mkOption { + type = types.bool; + default = true; + }; + + persist = mkOption { + type = types.bool; + default = false; + }; + }; + }; +in { + options = { + + services.lvm-snapshots = { + snapshots = mkOption { + type = types.attrsOf (types.submodule snapshotConfig); + default = {}; + }; + + mountPoint = mkOption { + type = types.path; + default = "/mnt"; + }; + + mountSuffix = mkOption { + type = types.str; + default = "-snapshot"; + }; + }; + }; + + + config = mkIf (cfg != {}) { + + boot.kernelModules = [ "dm_snapshot" ]; + + # system.activationScripts = mapAttrs' (name: scfg: nameValuePair ("lvm-mountpoint" + name) '' + # mkdir -p ${snapshotMount name} + # '') cfg.snapshots; + + systemd.services = mapAttrs' (name: scfg: nameValuePair ("lvm-snapshot@" + escapeSystemdPath name) { + enable = true; + + description = "LVM-snapshot of ${scfg.VG}/${scfg.LV}"; + + bindsTo = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"]; + after = ["${escapeSystemdPath "/dev/${scfg.VG}/${scfg.LV}"}.device"]; + + unitConfig = { + StopWhenUnneeded = !scfg.persist; + AssertPathIsDirectory = "/var/lock"; + }; + + path = with pkgs; [ devicemapper utillinux ]; + + script = '' + ( + flock -xn -E 4 9 + if [[ "$?" -ne 0 ]]; then + exit $? + fi + + lvcreate -s ${scfg.cowSize} --name ${snapshotName name} ${scfg.VG}/${scfg.LV} + + sleep infinity & + ) 9>/var/lock/lvm-snapshot.${scfg.VG} + ''; + + preStart = '' + lvremove -f ${scfg.VG}/${snapshotName name} + ''; + + preStop = '' + lvremove -f ${scfg.VG}/${snapshotName name} + ''; + + serviceConfig = with pkgs; { + Type = "forking"; + RestartForceExitStatus = [ "4" ]; + RestartSec = "5min"; + }; + }) cfg.snapshots; + + systemd.mounts = mapAttrsToList (name: scfg: { + enable = true; + + unitConfig = { + # AssertPathIsDirectory = snapshotMount name; + StopWhenUnneeded = !scfg.persist; + }; + + bindsTo = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ]; + after = [ ("lvm-snapshot@" + escapeSystemdPath name + ".service") ]; + + options = concatStringsSep "," ([ "noauto" ] ++ optional scfg.readOnly "ro"); + + where = snapshotMount name; + what = "/dev/" + scfg.VG + "/" + snapshotName name; + }) cfg.snapshots; + }; +} diff --git a/modules/kill-user.nix b/modules/kill-user.nix new file mode 100644 index 00000000..dd897b36 --- /dev/null +++ b/modules/kill-user.nix @@ -0,0 +1,13 @@ +{ lib, pkgs, config, ... }: +{ + options = { + systemd.kill-user.enable = lib.mkEnableOption "Systemd kill-user@ services"; + }; + + config.systemd.services."kill-user@" = lib.mkIf config.systemd.kill-user.enable { + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.systemd}/bin/loginctl kill-user %I"; + }; + }; +} diff --git a/modules/tinc-networkmanager.nix b/modules/tinc-networkmanager.nix new file mode 100644 index 00000000..ff03abd2 --- /dev/null +++ b/modules/tinc-networkmanager.nix @@ -0,0 +1,36 @@ +{ lib, config, pkgs, ... }: +let + cfg = config.services.tinc; +in { + options = { + services.tinc.networks = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options.nmDispatch = lib.mkOption { + type = lib.types.bool; + default = config.networking.networkmanager.enable; + description = '' + Install a network-manager dispatcher script to automatically + connect to all remotes when networking is available + ''; + }; + }); + }; + }; + + config = { + networking.networkmanager.dispatcherScripts = lib.concatLists (lib.flip lib.mapAttrsToList cfg.networks (network: data: lib.optional data.nmDispatch { + type = "basic"; + source = pkgs.writeScript "connect-${network}.sh" '' + #!${pkgs.stdenv.shell} + + shopt -s extglob + + case "''${2}" in + (?(vpn-)up) + ${data.package}/bin/tinc -n ${network} --pidfile /run/tinc.${network}.pid --batch retry + ;; + esac + ''; + })); + }; +} diff --git a/modules/uucp.nix b/modules/uucp.nix new file mode 100644 index 00000000..8d8ac1a2 --- /dev/null +++ b/modules/uucp.nix @@ -0,0 +1,391 @@ +{ flake, config, lib, pkgs, ... }: + +with lib; + +let + portSpec = name: node: concatStringsSep "\n" (map (port: '' + port ${name}.${port} + type pipe + protocol ${node.protocols} + reliable true + command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port} + '') node.hostnames); + sysSpec = name: node: '' + system ${name} + time any + chat-seven-bit false + chat . "" + protocol ${node.protocols} + command-path ${concatStringsSep " " cfg.commandPath} + commands ${concatStringsSep " " node.commands} + ${concatStringsSep "\nalternate\n" (map (port: '' + port ${name}.${port} + '') node.hostnames)} + ''; + sshConfig = name: node: concatStringsSep "\n" (map (port: '' + Host ${name}.${port} + Hostname ${port} + IdentitiesOnly Yes + IdentityFile ${cfg.sshKeyDir}/${name} + '') node.hostnames); + sshKeyGen = name: node: '' + if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then + ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name} + fi + ''; + restrictKey = key: '' + restrict,command="${chat}" ${key} + ''; + chat = pkgs.writeScript "chat" '' + #!${pkgs.stdenv.shell} + + echo . + exec ${config.security.wrapperDir}/uucico + ''; + + nodeCfg = { + options = { + commands = mkOption { + type = types.listOf types.str; + default = cfg.defaultCommands; + description = "Commands to allow for this remote"; + }; + + protocols = mkOption { + type = types.separatedString ""; + default = cfg.defaultProtocols; + description = "UUCP protocols to use for this remote"; + }; + + publicKeys = mkOption { + type = types.listOf types.str; + default = []; + description = "SSH client public keys for this node"; + }; + + generateKey = mkOption { + type = types.listOf types.str; + default = [ "-t" "ed25519" "-N" "" ]; + description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host"; + }; + + hostnames = mkOption { + type = types.listOf types.str; + default = []; + description = "Hostnames to try in order when connecting"; + }; + }; + }; + + cfg = config.services.uucp; +in { + options = { + services.uucp = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + If enabled we set up an account accesible via uucp over ssh + ''; + }; + + nodeName = mkOption { + type = types.str; + default = "nixos"; + description = "uucp node name"; + }; + + sshUser = mkOption { + type = types.attrs; + default = {}; + description = "Overrides for the local uucp linux-user"; + }; + + extraSSHConfig = mkOption { + type = types.str; + default = ""; + description = "Extra SSH config"; + }; + + remoteNodes = mkOption { + type = types.attrsOf (types.submodule nodeCfg); + default = {}; + description = '' + Ports to set up + Names will probably need to be configured in sshConfig + ''; + }; + + commandPath = mkOption { + type = types.listOf types.path; + default = [ "${pkgs.rmail}/bin" ]; + description = '' + Command search path for all systems + ''; + }; + + defaultCommands = mkOption { + type = types.listOf types.str; + default = ["rmail"]; + description = "Commands allowed for remotes without explicit override"; + }; + + defaultProtocols = mkOption { + type = types.separatedString ""; + default = "te"; + description = "UUCP protocol to use within ssh unless overriden"; + }; + + incomingProtocols = mkOption { + type = types.separatedString ""; + default = "te"; + description = "UUCP protocols to use when called"; + }; + + homeDir = mkOption { + type = types.path; + default = "/var/uucp"; + description = "Home of the uucp user"; + }; + + sshKeyDir = mkOption { + type = types.path; + default = "${cfg.homeDir}/.ssh/"; + description = "Directory to store ssh keypairs"; + }; + + spoolDir = mkOption { + type = types.path; + default = "/var/spool/uucp"; + description = "Spool directory"; + }; + + lockDir = mkOption { + type = types.path; + default = "/var/spool/uucp"; + description = "Lock directory"; + }; + + pubDir = mkOption { + type = types.path; + default = "/var/spool/uucppublic"; + description = "Public directory"; + }; + + logFile = mkOption { + type = types.path; + default = "/var/log/uucp"; + description = "Log file"; + }; + + statFile = mkOption { + type = types.path; + default = "/var/log/uucp.stat"; + description = "Statistics file"; + }; + + debugFile = mkOption { + type = types.path; + default = "/var/log/uucp.debug"; + description = "Debug file"; + }; + + interval = mkOption { + type = types.nullOr types.str; + default = "1h"; + description = '' + Specification of when to run `uucico' in format used by systemd timers + The default is to do so every hour + ''; + }; + + nmDispatch = mkOption { + type = types.bool; + default = config.networking.networkmanager.enable; + description = '' + Install a network-manager dispatcher script to automatically + call all remotes when networking is available + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = '' + run-uuxqt 1 + ''; + description = "Extra configuration to append verbatim to `/etc/uucp/config'"; + }; + + extraSys = mkOption { + type = types.lines; + default = '' + protocol-parameter g packet-size 4096 + ''; + description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`"; + }; + }; + }; + + config = mkIf cfg.enable { + environment.etc."uucp/config" = { + text = '' + hostname ${cfg.nodeName} + + spool ${cfg.spoolDir} + lockdir ${cfg.lockDir} + pubdir ${cfg.pubDir} + logfile ${cfg.logFile} + statfile ${cfg.statFile} + debugfile ${cfg.debugFile} + + ${cfg.extraConfig} + ''; + }; + + users.users."uucp" = { + name = "uucp"; + isSystemUser = true; + isNormalUser = false; + createHome = true; + home = cfg.homeDir; + description = "User for uucp over ssh"; + useDefaultShell = true; + openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes)); + } // cfg.sshUser; + + system.activationScripts."uucp-sshconfig" = '' + mkdir -p ${config.users.users."uucp".home}/.ssh + chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh + chmod 700 ${config.users.users."uucp".home}/.ssh + ln -fs ${builtins.toFile "ssh-config" '' + ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)} + + ${cfg.extraSSHConfig} + ''} ${config.users.users."uucp".home}/.ssh/config + + mkdir -p ${cfg.sshKeyDir} + chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir} + chmod 700 ${cfg.sshKeyDir} + + ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)} + ''; + + system.activationScripts."uucp-logs" = '' + touch ${cfg.logFile} + chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile} + chmod 644 ${cfg.logFile} + touch ${cfg.statFile} + chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile} + chmod 644 ${cfg.statFile} + touch ${cfg.debugFile} + chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile} + chmod 644 ${cfg.debugFile} + ''; + + environment.etc."uucp/port" = { + text = '' + port ssh + type stdin + protocol ${cfg.incomingProtocols} + '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes); + }; + environment.etc."uucp/sys" = { + text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes); + }; + + security.wrappers = let + wrapper = p: { + name = p; + value = { + source = "${pkgs.uucp}/bin/${p}"; + owner = "root"; + group = "root"; + setuid = true; + setgid = false; + }; + }; + in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); + + nixpkgs.overlays = [(self: super: { + uucp = super.stdenv.lib.overrideDerivation super.uucp (oldAttrs: { + configureFlags = "--with-newconfigdir=/etc/uucp"; + patches = [ + (super.writeText "mailprogram" '' + policy.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + + diff --git a/policy.h b/policy.h + index 5afe34b..8e92c8b 100644 + --- a/policy.h + +++ b/policy.h + @@ -240,7 +240,7 @@ + the sendmail choice below. Otherwise, select one of the other + choices as appropriate. */ + #if 1 + -#define MAIL_PROGRAM "/usr/lib/sendmail -t" + +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t" + /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */ + #define MAIL_PROGRAM_TO_BODY 1 + #define MAIL_PROGRAM_SUBJECT_BODY 1 + '') + ]; + }); + rmail = super.writeScriptBin "rmail" '' + #!${super.stdenv.shell} + + # Dummy UUCP rmail command for postfix/qmail systems + + IFS=" " read junk from junk junk junk junk junk junk junk relay + + case "$from" in + *[@!]*) ;; + *) from="$from@$relay";; + esac + + exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@" + ''; + })]; + + environment.systemPackages = with pkgs; [ + uucp + ]; + + systemd.services."uucico@" = { + serviceConfig = { + User = "uucp"; + Type = "oneshot"; + ExecStart = "${config.security.wrapperDir}/uucico -D -S %i"; + }; + }; + + systemd.timers."uucico@" = { + timerConfig.OnActiveSec = cfg.interval; + timerConfig.OnUnitActiveSec = cfg.interval; + }; + + systemd.targets."multi-user" = { + wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes; + }; + + systemd.kill-user.enable = true; + systemd.targets."sleep" = { + after = [ "kill-user@uucp.service" ]; + wants = [ "kill-user@uucp.service" ]; + }; + + networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch { + type = "basic"; + source = pkgs.writeScript "callRemotes.sh" '' + #!${pkgs.stdenv.shell} + + shopt -s extglob + + case "''${2}" in + (?(vpn-)up) + ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)} + ;; + esac + ''; + }; + }; +} diff --git a/modules/yggdrasil/default.nix b/modules/yggdrasil/default.nix new file mode 100644 index 00000000..91a550d6 --- /dev/null +++ b/modules/yggdrasil/default.nix @@ -0,0 +1,49 @@ +{ config, lib, customUtils, ... }: +let + cfg = config.services.tinc.yggdrasil; +in { + options = { + services.tinc.yggdrasil = lib.mkOption { + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Yggdrasil tinc network"; + + connect = lib.mkOption { + default = true; + type = lib.types.bool; + description = '' + Connect to central server + ''; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + services.tinc.networks.yggdrasil = { + name = config.networking.hostName; + hostSettings = customUtils.recImport { dir = ./hosts; }; + debugLevel = 2; + interfaceType = "tap"; + settings = { + Mode = "switch"; + PingTimeout = 30; + ConnectTo = lib.mkIf cfg.connect "ymir"; + }; + }; + + sops.secrets = { + tinc-yggdrasil-rsa = { + key = "rsa"; + path = "/etc/tinc/yggdrasil/rsa_key.priv"; + sopsFile = ./hosts + "/${config.services.tinc.networks.yggdrasil.name}/private-keys.yaml"; + }; + tinc-yggdrasil-ed25519 = { + key = "ed25519"; + path = "/etc/tinc/yggdrasil/rsa_key.priv"; + sopsFile = ./hosts + "/${config.services.tinc.networks.yggdrasil.name}/private-keys.yaml"; + }; + }; + }; +} diff --git a/modules/yggdrasil/hosts/sif/default.nix b/modules/yggdrasil/hosts/sif/default.nix new file mode 100644 index 00000000..32b844de --- /dev/null +++ b/modules/yggdrasil/hosts/sif/default.nix @@ -0,0 +1,13 @@ +{ + settings.Ed25519PublicKey = "qJqty+wiTNcYaHQCvQNiMqXYz30C9M3+LI/qjmU/9hK"; + rsaPublicKey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEA0ACaacg9EN0hBQct8ZwQ/i6EsXKP4DIwKwabM2rp8azValTHU2uI + WW6JRY+Eii6zRx9B5kJ96C4rJJeAGV6lZPAogaC2LbM7lcsZ7oRDWZGaQKcZFNGi + laEcDg2dRuDx1W4at0rb03SDLNPt8sXSV6BcK9n/7m7+s9cwM/+PB8FHDMnWvwbC + usbP23020s+CVr/PU1z/7J0y3Eat+Acut6x5X8DNewpqV96wQpqdAggbhtYERMFH + +i0sa1WUDQtJ6HGChbENRTMlsPJ6lnzXY+J0pzatzzvetLsOljES9uJ8dtk6qBC7 + KRZo5lvdUwR6j9XiHMQeRerUt23b9ATFXQIDAQAB + -----END RSA PUBLIC KEY----- + ''; +} diff --git a/modules/yggdrasil/hosts/sif/private-keys.yaml b/modules/yggdrasil/hosts/sif/private-keys.yaml new file mode 100644 index 00000000..9be82bc1 --- /dev/null +++ b/modules/yggdrasil/hosts/sif/private-keys.yaml @@ -0,0 +1,34 @@ +ed25519: ENC[AES256_GCM,data:1CqB4y6CIm5JUsznpXPqqLJqCKmmoAJOZQTWb7+Jbn0oZMX27qSMK4CchHF7Bmo24EK8rk5EyW5aQLnoxp/2NA62p8SXdaoI8Qgz3EgsQ5QrlJrt1jvERpNs4vttT9V6+aK3Yojr9IuQSvJ4jyKSLrzrTnLzF9pXlaOf1Ru5SxySRWtVzynzurRpdUVS6goE+lb+Irg6x2geV719iQ9bu1C2smeQDREdS+dlfoxp02/pU6kTFA7KAm5vA91HKEfMqfSEzuBgUB0=,iv:n6Yh0zZ9AbT+83P42QNO2rCCISJV5nbO9wYcwaRYD2E=,tag:dJpXV9ZzLSO1B+LsyV3vAg==,type:str] +rsa: ENC[AES256_GCM,data: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,iv:ZUAqvOpcVCXQD2PFzUh0e2m20t6gVT3mYb7S50iV/m8=,tag:AssxMqjVUEwQ4R6Y7eG9Tg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + lastmodified: '2021-01-02T14:46:16Z' + mac: ENC[AES256_GCM,data:Phng7z7UlE6nO3FFIQPOHgKCqDm2uOGL57ryJbokjipSSdoWPinpz0zIJv9Z67b9uOf3CQoGtV4YwcudNkzDBKOyD8uA6RYwCKpbYcZIdiy8DLL46+VT/wq9toTkeDXM6jKupzzOARZhHT8DCOLqW7u8Q3S645cbTJmw0+LMIGk=,iv:y4KEh0+bKhtnSobKVdfaPuRsueNC1lcrEbUGfEAn+Bg=,tag:3Oi4e/hSgPVsoFQpnVQj+g==,type:str] + pgp: + - created_at: '2021-01-02T14:45:04Z' + enc: | + -----BEGIN PGP MESSAGE----- + + hF4Dgwm4NZSaLAcSAQdAwWM12Zara3T2xDIX3rhakGxXFyme4LE5QZgE2GjnnWEw + T/vhPfsKFCjA2kAmj41NupjvTPL/nzfd7+MrdHRfC462Jrq+UF1W8A4bUa3OMH5J + 0l4BuFhl93w/VBftvnG8oSBAFCPNDapNADjTVJQStgsZa0/uD93NnCxyQmtuJYsQ + URlH0KMT6Kouaec4qk3SqkAHzaIIAukahBHAPf2C5cvXYw7AAOOBOdRaWycsmZDc + =S4Ig + -----END PGP MESSAGE----- + fp: F1AF20B9511B63F681A14E8D51AEFBCD1DEF68F8 + - created_at: '2021-01-02T14:45:04Z' + enc: | + -----BEGIN PGP MESSAGE----- + + hF4DXxoViZlp6dISAQdA7apd+ipJ0lUiuPI5Sq6uj6iOQYFfuNDuzse1JFJMfn4w + McsGPcbMorZV0OVFmg9vuZ0GP9sb7mkm+oRuY9OeMDEifjWGHJ2UN4TvdEcCO1zx + 0l4BvYyzFbShlQjge7+nrzVi2lzEvqsozEW76K3arWb/iYLCRyl0/Vhw5WT4K/UE + fw4cbqz7JrogVLFNeWSRPk3Y+Dg4Pf9rQnw1EJhUEIczYjnfajPhYe5K4M01mOby + =B0n7 + -----END PGP MESSAGE----- + fp: 30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51 + unencrypted_suffix: _unencrypted + version: 3.6.1 diff --git a/modules/yggdrasil/hosts/ymir.nix b/modules/yggdrasil/hosts/ymir.nix new file mode 100644 index 00000000..b77a9216 --- /dev/null +++ b/modules/yggdrasil/hosts/ymir.nix @@ -0,0 +1,19 @@ +{ + addresses = [{ address = "ymir.yggdrasil.li"; }]; + settings.Ed25519PublicKey = "b/SobnMqByzHOQeO+iU7OZ1liD8a++knbi5ebNawnaC"; + rsaPublicKey = '' + -----BEGIN RSA PUBLIC KEY----- + MIICCgKCAgEAuInSfQf5euFXEVkLLzf9TumQJ+3WRsxX4uKdOXBqrIC7yjSBP8j9 + ql5rNWPzgXxFF5ERmwW+E3cyzJLU9Htu7r3muqM6nhSZizhCskifPRFc3e5ssSke + XhHICHfe90+qvab/hWx/NjkW59bBYIzDuJfq+ijDFMVNgOxaiM2f3/2prUUhP7bN + r3wVI8KCkOaknc0SOOmOhLzfJaD5wosqLOjgaNhlro2eMgMjQlxbyW8dVVgjwseR + Cl/mpu7r1pSMhS66RFH68wDoC3X81f7Zs9ZGDLTD8KXWhx0qgUMUAH4n6YGY0RM6 + BZ3qR/3KFRU64QPVAERpb0JdsU9ggCVydHkjrWW23ptHOPAOO5+yQj7tSDCKTRy9 + dHMQnbtPrgAb6iMhO1XTxA8Hdta1sCHsewsQekarwsA1bmk3hTgi/k8vwoGDUWtk + jgiDEPuutfmH4C6qxq9s+6lRboNKH8wgkVGpHiaq7mmePFdhzFdrj4+fYAMZTbil + 2iygsJ+yFOjA7U+iT6QDK33/MLsrQg0Ue6RPiG1qnDyax7gBAjz52iWkiuSkUXk0 + E5ImdP4XMILgGcWk8iPq5iRS03edE0pCpxGX3ZZwFE5+CoXgO6wR1ToL1vZEEHMQ + SHJPufKjkavPKbejPps/mLaJQVw3W10PAJssB9nxW2aHX3n0ugGaIvMCAwEAAQ== + -----END RSA PUBLIC KEY----- + ''; +} -- cgit v1.2.3