diff --git a/hosts/temmie/services/userweb/default.nix b/hosts/temmie/services/userweb/default.nix index 0bfe1b5..18534fd 100644 --- a/hosts/temmie/services/userweb/default.nix +++ b/hosts/temmie/services/userweb/default.nix @@ -5,5 +5,6 @@ ./mail.nix ./module.nix ./packages.nix + ./passwd-sync.nix ]; } diff --git a/hosts/temmie/services/userweb/log-processor.nix b/hosts/temmie/services/userweb/log-processor.nix index 43d2b2a..e49003b 100644 --- a/hosts/temmie/services/userweb/log-processor.nix +++ b/hosts/temmie/services/userweb/log-processor.nix @@ -3,11 +3,6 @@ let mcfg = config.services.pvv-userweb; in { - sops.secrets = { - "httpd/passwd-ssh-key" = { }; - "httpd/ssh-known-hosts" = { }; - }; - systemd.targets.sockets.wants = [ "httpd-log-processor@access.socket" "httpd-log-processor@error.socket" @@ -27,6 +22,9 @@ in systemd.services."httpd-log-processor@" = lib.mkIf config.services.httpd.enable { requiredBy = [ "userweb.target" ]; + after = [ "httpd-passwd-sync.service" ]; + requires = [ "httpd-passwd-sync.service" ]; + serviceConfig = { User = "wwwrun"; Group = "wwwrun"; @@ -37,55 +35,16 @@ in StandardOutput = "journal"; StandardError = "journal"; - LoadCredential = [ - "sshkey:${config.sops.secrets."httpd/passwd-ssh-key".path}" - "ssh-known-hosts:${config.sops.secrets."httpd/ssh-known-hosts".path}" - ]; - ExecStartPre = let - rsyncArgs = lib.cli.toCommandLineShellGNU { } { - archive = true; - verbose = true; - compress = true; - rsh = "${lib.getExe' pkgs.openssh "ssh"} -o BatchMode=yes -o UserKnownHostsFile=%d/ssh-known-hosts -i %d/sshkey"; - }; - inputDir = "/run/httpd-log-processor-%i/pamunix-in"; - outputDir = "/run/httpd-log-processor-%i/pamunix-out"; - in lib.mkForce [ - "${lib.getExe pkgs.rsync} ${rsyncArgs} pvv@smtp.pvv.ntnu.no:/etc/passwd ${inputDir}/" - "${lib.getExe pkgs.rsync} ${rsyncArgs} pvv@smtp.pvv.ntnu.no:/etc/group ${inputDir}/" - - (let - args = lib.cli.toCommandLineShellGNU { } { - passwd-file = "${inputDir}/passwd"; - group-file = "${inputDir}/group"; - output-dir = outputDir; - shadow-file = pkgs.emptyFile; - - output-passwd = true; - - ignore-user-file = toString ./ignore_user_file.txt; - ignore-group-file = toString ./ignore_group_file.txt; - }; - in ''${lib.getExe pkgs.passwd2systemd-users} ${args}'') - "${lib.getExe' pkgs.coreutils "shred"} -u ${inputDir}/passwd ${inputDir}/group" - ":${lib.getExe pkgs.gnused} -i '$ a\\\\root:x:0:0:System administrator:/root:/run/current-system/sw/bin/bash' ${outputDir}/passwd" - ":${lib.getExe pkgs.gnused} -i '$ a\\\\wwwrun:x:54:54:Apache httpd user:/var/empty:/run/current-system/sw/bin/bash' ${outputDir}/passwd" - ":${lib.getExe pkgs.gnused} -i '$ a\\\\root:x:0:' ${outputDir}/group" - ":${lib.getExe pkgs.gnused} -i '$ a\\\\wwwrun:x:54:' ${outputDir}/group" - "+${lib.getExe' pkgs.coreutils "chown"} root:root ${outputDir}/passwd ${outputDir}/group" - "+${lib.getExe' pkgs.coreutils "chmod"} 0644 ${outputDir}/passwd ${outputDir}/group" - "+${lib.getExe pkgs.mount} --bind ${outputDir}/passwd /etc/passwd" - "+${lib.getExe pkgs.mount} --bind ${outputDir}/group /etc/group" - ]; - ExecStart = "${lib.getExe mcfg.apacheLogProcessorPackage} %i"; AmbientCapabilities = [ "CAP_SETUID" "CAP_SETGID" ]; CapabilityBoundingSet = [ "CAP_SETUID" "CAP_SETGID" ]; DeviceAllow = [ "" ]; + IPAddressDeny = "any"; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; + PrivateNetwork = true; PrivateIPC = true; PrivateTmp = true; # PrivateUsers = true; @@ -100,10 +59,7 @@ in ProtectSystem = "strict"; ProtectKernelTunables = true; RemoveIPC = true; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - ]; + RestrictAddressFamilies = [ "" ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; @@ -115,29 +71,17 @@ in ]; UMask = "0077"; - IPAddressAllow = [ - "127.0.0.53" # systemd-resolved - values.hosts.microbel.ipv4 - values.hosts.microbel.ipv6 - ]; - IPAddressDeny = "any"; - RootDirectory = "/run/httpd-log-processor-%i/root-mnt"; MountAPIVFS = true; RuntimeDirectoryMode = "0750"; - RuntimeDirectory = [ - "httpd-log-processor-%i/root-mnt" - "httpd-log-processor-%i/pamunix-in" - "httpd-log-processor-%i/pamunix-out" - ]; + RuntimeDirectory = [ "httpd-log-processor-%i/root-mnt" ]; BindReadOnlyPaths = [ builtins.storeDir "/etc" - "/etc/resolv.conf" - "-/run/httpd-log-processor-%i/pamunix-out/passwd:/etc/passwd" - "-/run/httpd-log-processor-%i/pamunix-out/group:/etc/group" + "/var/lib/httpd-passwd-sync/passwd:/etc/passwd" + "/var/lib/httpd-passwd-sync/group:/etc/group" "${pkgs.writeText "userweb-fake-nsswitch.conf" '' passwd: files diff --git a/hosts/temmie/services/userweb/module.nix b/hosts/temmie/services/userweb/module.nix index 0cb2df2..4a7d4df 100644 --- a/hosts/temmie/services/userweb/module.nix +++ b/hosts/temmie/services/userweb/module.nix @@ -4,7 +4,9 @@ let in { options.services.pvv-userweb = { - enable = lib.mkEnableOption ""; + enable = lib.mkEnableOption "" // { + default = true; + }; debugMode = lib.mkEnableOption ""; diff --git a/hosts/temmie/services/userweb/passwd-sync.nix b/hosts/temmie/services/userweb/passwd-sync.nix new file mode 100644 index 0000000..31e01f7 --- /dev/null +++ b/hosts/temmie/services/userweb/passwd-sync.nix @@ -0,0 +1,147 @@ +{ config, lib, pkgs, values, ... }: +let + mcfg = config.services.pvv-userweb; +in +{ + config = lib.mkIf mcfg.enable { + sops.secrets = { + "httpd/passwd-ssh-key" = { }; + "httpd/ssh-known-hosts" = { }; + }; + + # NOTE: because we are running as `DynamicUser` and we want the result files to be available to + # other services, this directory needs to be created via systemd-tmpfiles + systemd.tmpfiles.settings."10-httpd-passwd-sync"."/var/lib/httpd-passwd-sync".d = { + user = "root"; + group = "root"; + mode = "0700"; + }; + + systemd.timers.httpd-passwd-sync = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + Unit = "httpd-passwd-sync.service"; + }; + }; + + systemd.services."httpd-passwd-sync" = { + requiredBy = [ "userweb.target" ]; + after = [ + "systemd-tmpfiles-setup.service" + "systemd-tmpfiles-resetup.service" + ]; + serviceConfig = { + Type = "oneshot"; + Slice = "system-userweb.slice"; + Restart = "on-failure"; + RestartSec = "3s"; + + DynamicUser = true; + + LoadCredential = [ + "sshkey:${config.sops.secrets."httpd/passwd-ssh-key".path}" + "ssh-known-hosts:${config.sops.secrets."httpd/ssh-known-hosts".path}" + ]; + ExecStart = let + rsyncArgs = lib.cli.toCommandLineShellGNU { } { + verbose = true; + compress = true; + rsh = "${lib.getExe' pkgs.openssh "ssh"} -o BatchMode=yes -o UserKnownHostsFile=%d/ssh-known-hosts -i %d/sshkey"; + }; + inputDir = "/run/httpd-passwd-sync/in"; + wipDir = "/run/httpd-passwd-sync/wip"; + outputDir = "/var/lib/httpd-passwd-sync"; + in [ + "${lib.getExe pkgs.rsync} ${rsyncArgs} pvv@smtp.pvv.ntnu.no:/etc/passwd ${inputDir}/" + "${lib.getExe pkgs.rsync} ${rsyncArgs} pvv@smtp.pvv.ntnu.no:/etc/group ${inputDir}/" + + (let + args = lib.cli.toCommandLineShellGNU { } { + passwd-file = "${inputDir}/passwd"; + group-file = "${inputDir}/group"; + output-dir = wipDir; + shadow-file = pkgs.emptyFile; + + output-passwd = true; + + ignore-user-file = toString ./ignore_user_file.txt; + ignore-group-file = toString ./ignore_group_file.txt; + }; + in ''${lib.getExe pkgs.passwd2systemd-users} ${args}'') + + "${lib.getExe' pkgs.coreutils "shred"} -u ${inputDir}/passwd ${inputDir}/group" + + ":${lib.getExe pkgs.gnused} -i '$ a\\\\root:x:0:0:System administrator:/root:/run/current-system/sw/bin/bash' ${wipDir}/passwd" + ":${lib.getExe pkgs.gnused} -i '$ a\\\\wwwrun:x:54:54:Apache httpd user:/var/empty:/run/current-system/sw/bin/bash' ${wipDir}/passwd" + ":${lib.getExe pkgs.gnused} -i '$ a\\\\root:x:0:' ${wipDir}/group" + ":${lib.getExe pkgs.gnused} -i '$ a\\\\wwwrun:x:54:' ${wipDir}/group" + + "+${lib.getExe' pkgs.coreutils "install"} -m644 -o root -g root -t '${outputDir}' ${wipDir}/passwd ${wipDir}/group" + "${lib.getExe' pkgs.coreutils "shred"} -u ${wipDir}/passwd ${wipDir}/group" + ]; + + AmbientCapabilities = [ "" ]; + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = "tmpfs"; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SocketBindDeny = "any"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@resources" + ]; + UMask = "0077"; + + IPAddressAllow = [ + values.hosts.microbel.ipv4 + values.hosts.microbel.ipv6 + ]; + IPAddressDeny = "any"; + + RootDirectory = "/run/httpd-passwd-sync/root-mnt"; + MountAPIVFS = true; + + RuntimeDirectoryMode = "0750"; + RuntimeDirectory = [ + "httpd-passwd-sync/root-mnt" + "httpd-passwd-sync/in" + "httpd-passwd-sync/wip" + ]; + + BindPaths = [ + "/var/lib/httpd-passwd-sync" + ]; + BindReadOnlyPaths = [ + builtins.storeDir + "/etc" + "/var/run/nscd" + ]; + }; + }; + }; +}