webserver: Use a hardened systemd unit instead of a container
This commit is contained in:
parent
05026614c8
commit
d3c4de5aeb
|
@ -66,22 +66,6 @@
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
clientMaxBodySize = "10G";
|
clientMaxBodySize = "10G";
|
||||||
domain = "tlater.net";
|
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 = {
|
security.acme = {
|
||||||
|
|
|
@ -1,47 +1,20 @@
|
||||||
{
|
{config, ...}: let
|
||||||
config,
|
domain = config.services.nginx.domain;
|
||||||
pkgs,
|
in {
|
||||||
...
|
services.tlaternet-webserver.enable = true;
|
||||||
}: {
|
|
||||||
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;};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers.containers.webserver = {
|
# Set up SSL
|
||||||
image = "tlaternet/webserver";
|
services.nginx.virtualHosts."${domain}" = let
|
||||||
|
inherit (config.services.tlaternet-webserver.listen) addr port;
|
||||||
|
in {
|
||||||
|
serverAliases = ["www.${domain}"];
|
||||||
|
|
||||||
imageFile = pkgs.dockerTools.buildImage {
|
forceSSL = true;
|
||||||
name = "tlaternet/webserver";
|
enableACME = true;
|
||||||
tag = "latest";
|
extraConfig = ''
|
||||||
contents = pkgs.tlaternet-webserver.webserver;
|
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
|
||||||
|
'';
|
||||||
|
|
||||||
config = let
|
locations."/".proxyPass = "http://${addr}:${toString port}";
|
||||||
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"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
39
flake.lock
39
flake.lock
|
@ -15,21 +15,6 @@
|
||||||
"type": "github"
|
"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": {
|
"naersk": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -88,7 +73,6 @@
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"tlaternet-templates": "tlaternet-templates",
|
|
||||||
"tlaternet-webserver": "tlaternet-webserver"
|
"tlaternet-webserver": "tlaternet-webserver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -137,30 +121,9 @@
|
||||||
"type": "github"
|
"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": {
|
"tlaternet-webserver": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils",
|
||||||
"naersk": "naersk",
|
"naersk": "naersk",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
|
|
10
flake.nix
10
flake.nix
|
@ -13,10 +13,6 @@
|
||||||
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
|
url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
tlaternet-templates = {
|
|
||||||
url = "git+https://gitea.tlater.net/tlaternet/tlaternet-templates.git";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
|
@ -25,16 +21,11 @@
|
||||||
nixos-hardware,
|
nixos-hardware,
|
||||||
sops-nix,
|
sops-nix,
|
||||||
tlaternet-webserver,
|
tlaternet-webserver,
|
||||||
tlaternet-templates,
|
|
||||||
}: let
|
}: let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
|
|
||||||
overlays = [
|
overlays = [
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
tlaternet-webserver =
|
|
||||||
tlaternet-webserver.legacyPackages.${prev.system}.packages;
|
|
||||||
tlaternet-templates =
|
|
||||||
tlaternet-templates.legacyPackages.${prev.system}.packages;
|
|
||||||
local = import ./pkgs {
|
local = import ./pkgs {
|
||||||
pkgs = prev;
|
pkgs = prev;
|
||||||
};
|
};
|
||||||
|
@ -59,6 +50,7 @@
|
||||||
(import ./configuration/linode.nix)
|
(import ./configuration/linode.nix)
|
||||||
(import ./configuration/hardware-configuration.nix)
|
(import ./configuration/hardware-configuration.nix)
|
||||||
sops-nix.nixosModules.sops
|
sops-nix.nixosModules.sops
|
||||||
|
tlaternet-webserver.nixosModules.default
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
}
|
|
Loading…
Reference in a new issue