From bad554df4a4f9c626d6cba58bf5a7c7d16b44ad6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tristan=20Dani=C3=ABl=20Maat?= <tm@tlater.net>
Date: Wed, 12 Oct 2022 19:58:09 +0100
Subject: [PATCH] webserver: Use a hardened systemd unit instead of a container

---
 configuration/default.nix            |  16 --
 configuration/services/webserver.nix |  55 ++-----
 flake.lock                           |  39 +----
 flake.nix                            |  10 +-
 modules/virtualisation/pods.nix      | 222 ---------------------------
 5 files changed, 14 insertions(+), 328 deletions(-)
 delete mode 100644 modules/virtualisation/pods.nix

diff --git a/configuration/default.nix b/configuration/default.nix
index 7e94105..24f9ac7 100644
--- a/configuration/default.nix
+++ b/configuration/default.nix
@@ -66,22 +66,6 @@
     recommendedProxySettings = true;
     clientMaxBodySize = "10G";
     domain = "tlater.net";
-
-    virtualHosts = let
-      proxyPassToPort = port: extra:
-        lib.recursiveUpdate {
-          forceSSL = true;
-          enableACME = true;
-          locations."/".proxyPass = "http://127.0.0.1:${toString port}";
-          extraConfig = ''
-            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-          '';
-        }
-        extra;
-      domain = config.services.nginx.domain;
-    in {
-      "${domain}" = proxyPassToPort 3002 {serverAliases = ["www.${domain}"];};
-    };
   };
 
   security.acme = {
diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix
index 093da3d..2cce796 100644
--- a/configuration/services/webserver.nix
+++ b/configuration/services/webserver.nix
@@ -1,47 +1,16 @@
-{
-  config,
-  pkgs,
-  ...
-}: {
-  users = {
-    extraUsers.webserver = {
-      uid = config.ids.uids.webserver;
-      group = config.users.extraGroups.webserver.name;
-      isSystemUser = true;
-      description = "tlater.net web server user";
-    };
-    extraGroups.webserver = {gid = config.ids.gids.webserver;};
-  };
+{config, ...}: {
+  services.tlaternet-webserver.enable = true;
 
-  virtualisation.oci-containers.containers.webserver = {
-    image = "tlaternet/webserver";
+  # Set up SSL
+  services.nginx.virtualHosts."${config.services.nginx.domain}" = let
+    inherit (config.services.tlaternet-webserver.listen) addr port;
+  in {
+    forceSSL = true;
+    enableACME = true;
+    extraConfig = ''
+      add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+    '';
 
-    imageFile = pkgs.dockerTools.buildImage {
-      name = "tlaternet/webserver";
-      tag = "latest";
-      contents = pkgs.tlaternet-webserver.webserver;
-
-      config = let
-        uid = toString config.users.extraUsers.webserver.uid;
-        gid = toString config.users.extraGroups.webserver.gid;
-      in {
-        Cmd = ["tlaternet-webserver"];
-        Volumes = {"/srv/mail" = {};};
-        Env = [
-          "ROCKET_PORT=3002"
-          "ROCKET_TEMPLATE_DIR=${pkgs.tlaternet-templates.templates}/browser/"
-        ];
-        ExposedPorts = {"3002" = {};};
-        User = "${uid}:${gid}";
-      };
-    };
-
-    ports = ["3002:3002"];
-    volumes = ["tlaternet-mail:/srv/mail"];
-    extraOptions = [
-      "--hostname=tlater.net"
-      # Rocket 0.4 doesn't support SIGTERM anyway, so SIGKILL is the cleanest exit possible.
-      "--stop-signal=SIGKILL"
-    ];
+    locations."/".proxyPass = "http://${addr}:${toString port}";
   };
 }
diff --git a/flake.lock b/flake.lock
index c6f9923..2b15a85 100644
--- a/flake.lock
+++ b/flake.lock
@@ -15,21 +15,6 @@
         "type": "github"
       }
     },
-    "flake-utils_2": {
-      "locked": {
-        "lastModified": 1659877975,
-        "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
     "naersk": {
       "inputs": {
         "nixpkgs": [
@@ -88,7 +73,6 @@
         "nixos-hardware": "nixos-hardware",
         "nixpkgs": "nixpkgs",
         "sops-nix": "sops-nix",
-        "tlaternet-templates": "tlaternet-templates",
         "tlaternet-webserver": "tlaternet-webserver"
       }
     },
@@ -137,30 +121,9 @@
         "type": "github"
       }
     },
-    "tlaternet-templates": {
-      "inputs": {
-        "flake-utils": "flake-utils",
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1633432574,
-        "narHash": "sha256-IjGaJAQuFIJ1Is9gtHXsryPOnTDE6tlA61PUKuS8dzw=",
-        "ref": "master",
-        "rev": "555a2949bdf643c74b535bd0c623d98f99d33628",
-        "revCount": 61,
-        "type": "git",
-        "url": "https://gitea.tlater.net/tlaternet/tlaternet-templates.git"
-      },
-      "original": {
-        "type": "git",
-        "url": "https://gitea.tlater.net/tlaternet/tlaternet-templates.git"
-      }
-    },
     "tlaternet-webserver": {
       "inputs": {
-        "flake-utils": "flake-utils_2",
+        "flake-utils": "flake-utils",
         "naersk": "naersk",
         "nixpkgs": [
           "nixpkgs"
diff --git a/flake.nix b/flake.nix
index 5ff0a51..ed94531 100644
--- a/flake.nix
+++ b/flake.nix
@@ -13,10 +13,6 @@
       url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
       inputs.nixpkgs.follows = "nixpkgs";
     };
-    tlaternet-templates = {
-      url = "git+https://gitea.tlater.net/tlaternet/tlaternet-templates.git";
-      inputs.nixpkgs.follows = "nixpkgs";
-    };
   };
 
   outputs = {
@@ -25,16 +21,11 @@
     nixos-hardware,
     sops-nix,
     tlaternet-webserver,
-    tlaternet-templates,
   }: let
     system = "x86_64-linux";
 
     overlays = [
       (final: prev: {
-        tlaternet-webserver =
-          tlaternet-webserver.legacyPackages.${prev.system}.packages;
-        tlaternet-templates =
-          tlaternet-templates.legacyPackages.${prev.system}.packages;
         local = import ./pkgs {
           pkgs = prev;
         };
@@ -59,6 +50,7 @@
           (import ./configuration/linode.nix)
           (import ./configuration/hardware-configuration.nix)
           sops-nix.nixosModules.sops
+          tlaternet-webserver.nixosModules.default
         ];
       };
 
diff --git a/modules/virtualisation/pods.nix b/modules/virtualisation/pods.nix
deleted file mode 100644
index 5a96cc8..0000000
--- a/modules/virtualisation/pods.nix
+++ /dev/null
@@ -1,222 +0,0 @@
-{
-  lib,
-  config,
-  options,
-  ...
-}:
-with lib; let
-  cfg = config.virtualisation.pods;
-  list-to-args = arg: list:
-    concatStringsSep " " (map (e: "--${arg}=${escapeShellArg e}") list);
-  possibly-unset-arg = arg: val: (optionalString (val != null) "--${arg}=${escapeShellArg val}");
-
-  mkPod = name: pod: rec {
-    path = [config.virtualisation.podman.package];
-
-    wants = ["network.target"];
-    after = ["network-online.target"];
-    wantedBy = ["multi-user.target" "default.target"];
-
-    environment.PODMAN_SYSTEMD_UNIT = "%n";
-
-    preStart = concatStringsSep " " [
-      "mkdir -p /run/podman/pods/ ;"
-      "podman pod create"
-      "--infra-conmon-pidfile=${escapeShellArg "/run/podman/pods/${name}.pid"}"
-      "--name=${escapeShellArg name}"
-      "--replace"
-      (list-to-args "add-host" pod.added-hosts)
-      (possibly-unset-arg "cgroup-parent" pod.cgroup-parent)
-      (list-to-args "dns" pod.dns)
-      (list-to-args "dns-opt" pod.dns-opt)
-      (list-to-args "dns-search" pod.dns-search)
-      (possibly-unset-arg "hostname" pod.hostname)
-      (possibly-unset-arg "infra" pod.infra)
-      (possibly-unset-arg "infra-command" pod.infra-command)
-      (possibly-unset-arg "infra-image" pod.infra-image)
-      (possibly-unset-arg "ip" pod.ip)
-      (possibly-unset-arg "mac-address" pod.mac-address)
-      (possibly-unset-arg "network" pod.network)
-      (possibly-unset-arg "network-alias" pod.network-alias)
-      (possibly-unset-arg "no-hosts" pod.no-hosts)
-      (list-to-args "publish" pod.publish)
-      (list-to-args "share" pod.share)
-    ];
-
-    script = "podman pod start ${escapeShellArg name}";
-    preStop = "podman pod stop ${escapeShellArg name}";
-    # `podman generate systemd` generates a second stop after the
-    # first; not sure why but clearly it's recommended.
-    postStop = preStop;
-
-    serviceConfig = rec {
-      Type = "forking";
-      TimeoutStopSec = 70;
-      Restart = "on-failure";
-      PIDFile = "/run/podman/pods/${name}.pid";
-    };
-  };
-in {
-  options.virtualisation.pods = mkOption {
-    type = with types;
-      attrsOf (submodule {
-        options = {
-          added-hosts = mkOption {
-            type = listOf str;
-            default = [];
-            description = "Additional hosts to add to /etc/hosts for each container.";
-            example = literalExample ''
-              [ "database:10.0.0.1" ]
-            '';
-          };
-
-          cgroup-parent = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "The cgroups path under which the pod cgroup will be created.";
-          };
-
-          dns = mkOption {
-            type = listOf str;
-            default = [];
-            description = "The dns servers to set in /etc/resolv.conf.";
-          };
-
-          dns-opt = mkOption {
-            type = listOf str;
-            default = [];
-            description = "dns options to set in /etc/resolv.conf.";
-          };
-
-          dns-search = mkOption {
-            type = listOf str;
-            default = [];
-            description = "Search domains to set in /etc/resolv.conf.";
-          };
-
-          hostname = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "The pod hostname.";
-          };
-
-          infra = mkOption {
-            type = nullOr bool;
-            default = null;
-            description = "Whether to create the infra container for the pod.";
-          };
-
-          infra-command = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "The command to run in the infra container.";
-          };
-
-          infra-image = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "The image to use for the infra container.";
-          };
-
-          ip = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "A static IP address for the pod network.";
-          };
-
-          # TODO: set up label file stuff.
-          #
-          # labels = mkOption {};
-
-          mac-address = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "A static mac address for the pod network.";
-          };
-
-          network = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "Network configuration for the pod.";
-          };
-
-          network-alias = mkOption {
-            type = nullOr str;
-            default = null;
-            description = "DNS alias for the pod.";
-          };
-
-          no-hosts = mkOption {
-            type = nullOr bool;
-            default = null;
-            description = "Whether to disable /etc/hosts creation for the pod.";
-          };
-
-          publish = mkOption {
-            type = listOf str;
-            default = [];
-            description = "List of ports to publish from the pod.";
-          };
-
-          share = mkOption {
-            type = listOf str;
-            default = [];
-            description = "List of kernel namespaces to share.";
-          };
-
-          containers = options.virtualisation.oci-containers.containers;
-        };
-      });
-    default = {};
-    description = "Podman pods to run as systemd services.";
-  };
-
-  config = let
-    # Merge a list of attribute sets together
-    #
-    # TODO: See if there's a generic version for this somewhere in the
-    # pkgs lib?
-    mergeAttrs = attrList: foldr (a: b: a // b) {} attrList;
-
-    # Create services for all defined pods
-    pod-services = mapAttrs' (n: v: nameValuePair "pod-${n}" (mkPod n v)) cfg;
-
-    # Override the systemd-specific settings of containers defined in
-    # pods.
-    #
-    # I.e., make a systemd unit dependency on the pod service.
-    pod-container-services = mergeAttrs (mapAttrsToList (pname: pod:
-      mapAttrs' (cname: container:
-        nameValuePair "podman-${pname}-${cname}" rec {
-          after = ["pod-${pname}.service"];
-          requires = after;
-        })
-      pod.containers)
-    cfg);
-
-    # Override the oci-container settings for containers defined in pods.
-    #
-    # I.e., set the --pod=podname setting, and update the dependsOn so
-    # it points to containers in the same pod.
-    podifyContainer = container: podname:
-      container
-      // {
-        dependsOn =
-          map (dependency: "${podname}-${dependency}") container.dependsOn;
-        extraOptions = container.extraOptions ++ ["--pod=${podname}"];
-      };
-  in
-    lib.mkIf (cfg != {}) {
-      virtualisation.podman.enable = true;
-      virtualisation.oci-containers.backend = "podman";
-
-      systemd.services = pod-services // pod-container-services;
-
-      virtualisation.oci-containers.containers = mergeAttrs (mapAttrsToList
-        (pname: pod:
-          mapAttrs' (cname: container:
-            nameValuePair "${pname}-${cname}" (podifyContainer container pname))
-          pod.containers)
-        cfg);
-    };
-}