diff --git a/flake.nix b/flake.nix index 73c05f2..04ef7a2 100644 --- a/flake.nix +++ b/flake.nix @@ -129,6 +129,7 @@ ] ++ (lib.optionals enableDefaults [ sops-nix.nixosModules.sops inputs.roowho2.nixosModules.default + self.nixosModules.rsync-pull-targets ]) ++ modules; } (builtins.removeAttrs extraArgs [ @@ -270,11 +271,12 @@ nixosModules = { bluemap = ./modules/bluemap.nix; - snakeoil-certs = ./modules/snakeoil-certs.nix; - snappymail = ./modules/snappymail.nix; - robots-txt = ./modules/robots-txt.nix; gickup = ./modules/gickup; matrix-ooye = ./modules/matrix-ooye.nix; + robots-txt = ./modules/robots-txt.nix; + rsync-pull-targets = ./modules/rsync-pull-targets.nix; + snakeoil-certs = ./modules/snakeoil-certs.nix; + snappymail = ./modules/snappymail.nix; }; devShells = forAllSystems (system: { diff --git a/hosts/bekkalokk/services/website/fetch-gallery.nix b/hosts/bekkalokk/services/website/fetch-gallery.nix index 3061816..fc3fe8a 100644 --- a/hosts/bekkalokk/services/website/fetch-gallery.nix +++ b/hosts/bekkalokk/services/website/fetch-gallery.nix @@ -3,13 +3,21 @@ let galleryDir = config.services.pvv-nettsiden.settings.GALLERY.DIR; transferDir = "${config.services.pvv-nettsiden.settings.GALLERY.DIR}-transfer"; in { - users.users.${config.services.pvv-nettsiden.user} = { - useDefaultShell = true; - - # This is pushed from microbel:/var/www/www-gallery/build-gallery.sh - openssh.authorizedKeys.keys = [ - ''command="${pkgs.rrsync}/bin/rrsync -wo ${transferDir}",restrict,no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIjHhC2dikhWs/gG+m7qP1eSohWzTehn4ToNzDSOImyR gallery-publish'' - ]; + # This is pushed from microbel:/var/www/www-gallery/build-gallery.sh + services.rsync-pull-targets = { + enable = true; + locations.${transferDir} = { + user = config.services.pvv-nettsiden.user; + rrsyncArgs.wo = true; + authorizedKeysAttrs = [ + "restrict" + "no-agent-forwarding" + "no-port-forwarding" + "no-pty" + "no-X11-forwarding" + ]; + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIjHhC2dikhWs/gG+m7qP1eSohWzTehn4ToNzDSOImyR gallery-publish"; + }; }; systemd.paths.pvv-nettsiden-gallery-update = { diff --git a/modules/rsync-pull-targets.nix b/modules/rsync-pull-targets.nix new file mode 100644 index 0000000..016694e --- /dev/null +++ b/modules/rsync-pull-targets.nix @@ -0,0 +1,130 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.rsync-pull-targets; +in +{ + options.services.rsync-pull-targets = { + enable = lib.mkEnableOption ""; + + rrsyncPackage = lib.mkPackageOption pkgs "rrsync" { }; + + locations = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({ name, ... }@submoduleArgs: { + options = { + enable = lib.mkEnableOption "" // { + default = true; + example = false; + }; + + user = lib.mkOption { + type = lib.types.str; + description = "Which user to use as SSH login"; + example = "root"; + }; + + location = lib.mkOption { + type = lib.types.path; + default = name; + defaultText = lib.literalExpression ""; + example = "/path/to/rsyncable/item"; + }; + + # TODO: handle autogeneration of keys + # autoGenerateSSHKeypair = lib.mkOption { + # type = lib.types.bool; + # default = config.publicKey == null; + # defaultText = lib.literalExpression "config.services.rsync-pull-targets..publicKey != null"; + # example = true; + # }; + + publicKey = lib.mkOption { + type = lib.types.str; + # type = lib.types.nullOr lib.types.str; + # default = null; + example = "ssh-ed25519 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA comment"; + }; + + rrsyncPackage = lib.mkPackageOption pkgs "rrsync" { } // { + default = cfg.rrsyncPackage; + defaultText = lib.literalExpression "config.services.rsync-pull-targets.rrsyncPackage"; + }; + + enableRecommendedHardening = lib.mkEnableOption "a commonly used security profile for authorizedKeys attributes and rrsync args"; + + rrsyncArgs = { + ro = lib.mkEnableOption "" // { + description = "Allow only reading from the DIR. Implies -no-del and -no-lock."; + }; + wo = lib.mkEnableOption "" // { + description = "Allow only writing to the DIR."; + }; + munge = lib.mkEnableOption "" // { + description = "Enable rsync's --munge-links on the server side."; + # TODO: set a default? + }; + no-del = lib.mkEnableOption "" // { + description = "Disable rsync's --delete* and --remove* options."; + default = submoduleArgs.config.enableRecommendedHardening; + defaultText = lib.literalExpression "config.services.rsync-pull-targets..enableRecommendedHardening"; + }; + no-lock = lib.mkEnableOption "" // { + description = "Avoid the single-run (per-user) lock check."; + default = submoduleArgs.config.enableRecommendedHardening; + defaultText = lib.literalExpression "config.services.rsync-pull-targets..enableRecommendedHardening"; + }; + no-overwrite = lib.mkEnableOption "" // { + description = "Prevent overwriting existing files by enforcing --ignore-existing"; + default = submoduleArgs.config.enableRecommendedHardening; + defaultText = lib.literalExpression "config.services.rsync-pull-targets..enableRecommendedHardening"; + }; + }; + + authorizedKeysAttrs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = lib.optionals submoduleArgs.config.enableRecommendedHardening [ + "restrict" + "no-agent-forwarding" + "no-port-forwarding" + "no-pty" + "no-X11-forwarding" + ]; + defaultText = lib.literalExpression '' + lib.optionals config.services.rsync-pull-targets..enableRecommendedHardening [ + "restrict" + "no-agent-forwarding" + "no-port-forwarding" + "no-pty" + "no-X11-forwarding" + ] + ''; + example = [ + "restrict" + "no-agent-forwarding" + "no-port-forwarding" + "no-pty" + "no-X11-forwarding" + ]; + }; + }; + })); + }; + }; + + config = lib.mkIf cfg.enable { + services.openssh.enable = true; + users.users = lib.pipe cfg.locations [ + (lib.filterAttrs (_: value: value.enable)) + (lib.mapAttrs' (_: { user, location, rrsyncPackage, rrsyncArgs, authorizedKeysAttrs, publicKey, ... }: let + rrsyncArgString = lib.cli.toCommandLineShellGNU { + isLong = _: false; + } rrsyncArgs; + # TODO: handle " in location + in { + name = user; + value.openssh.authorizedKeys.keys = [ + "command=\"${lib.getExe rrsyncPackage} ${rrsyncArgString} ${location}\",${lib.concatStringsSep "," authorizedKeysAttrs} ${publicKey}" + ]; + })) + ]; + }; +}