summaryrefslogtreecommitdiff
path: root/sys/web/sites
diff options
context:
space:
mode:
Diffstat (limited to 'sys/web/sites')
-rw-r--r--sys/web/sites/default.nix8
-rw-r--r--sys/web/sites/home.nix38
-rw-r--r--sys/web/sites/host.nix106
-rw-r--r--sys/web/sites/portal.nix42
-rw-r--r--sys/web/sites/pxe.nix180
5 files changed, 374 insertions, 0 deletions
diff --git a/sys/web/sites/default.nix b/sys/web/sites/default.nix
new file mode 100644
index 0000000..85b6020
--- /dev/null
+++ b/sys/web/sites/default.nix
@@ -0,0 +1,8 @@
+{
+ imports = [
+ ./home.nix
+ ./host.nix
+ ./portal.nix
+ ./pxe.nix
+ ];
+}
diff --git a/sys/web/sites/home.nix b/sys/web/sites/home.nix
new file mode 100644
index 0000000..fed9b84
--- /dev/null
+++ b/sys/web/sites/home.nix
@@ -0,0 +1,38 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.local.web.sites.home;
+ inherit (config.local) domains;
+in {
+ options.local.web.sites.home = {
+ enable = mkEnableOption "home site";
+
+ proxyUrl = mkOption {
+ type = types.str;
+ };
+ };
+
+ config = mkIf cfg.enable {
+ local.web = {
+ enable = mkDefault true;
+ ownedCerts = ["home"];
+ };
+
+ services.nginx.virtualHosts.${domains.home.main} = {
+ forceSSL = true;
+ useACMEHost = domains.home.main;
+
+ locations."/".extraConfig = ''
+ proxy_pass ${cfg.proxyUrl};
+ proxy_redirect http:// https://;
+
+ # Necesario debido a websockets
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ '';
+ };
+ };
+}
diff --git a/sys/web/sites/host.nix b/sys/web/sites/host.nix
new file mode 100644
index 0000000..ea6cc23
--- /dev/null
+++ b/sys/web/sites/host.nix
@@ -0,0 +1,106 @@
+{
+ config,
+ lib,
+ ...
+}:
+with lib; let
+ cfg = config.local.web.sites.host;
+
+ inherit (config.local) domains;
+ inherit (config.local.net) hostname;
+
+ users = filterAttrs (_: user: user.install) config.local.users;
+ hostDomain = domains.${hostDomainName};
+ hostDomainName = "host-${hostname}";
+
+ userCerts = flatten (flatten (mapAttrsToList
+ (name: user:
+ map
+ (cert: {
+ fprint = config.local.pki.byPath.${cert}.fingerprint.sha1-lower;
+ inherit name;
+ })
+ user.mail.certs)
+ users));
+in {
+ options.local.web.sites.host = {
+ enable = mkEnableOption "host site, restricted to per-user client certs";
+ };
+
+ config = mkIf cfg.enable {
+ local.web = {
+ enable = mkDefault true;
+ ownedCerts = [hostDomainName];
+ };
+
+ services = {
+ nginx = {
+ appendHttpConfig = ''
+ map $ssl_client_fingerprint $host_user_from_fprint {
+ default "";
+ ${concatMapStringsSep "\n " (pair: "\"${escapeRegex pair.fprint}\" \"${pair.name}\";") userCerts}
+ }
+ '';
+
+ virtualHosts = {
+ ${hostDomain.main} = {
+ forceSSL = true;
+ useACMEHost = hostDomain.main;
+
+ extraConfig = ''
+ ssl_verify_depth 2;
+ ssl_verify_client optional;
+ ssl_client_certificate ${config.local.pki.ca.mail.fullchain};
+
+ #if ($ssl_client_verify != "SUCCESS") {
+ #return 403;
+ #}
+ '';
+
+ locations =
+ {
+ "/".return = 403;
+ }
+ // concatMapAttrs
+ (name: user: let
+ userLocation = config: {
+ extraConfig =
+ ''
+ if ($host_user_from_fprint != "${name}") {
+ return 403;
+ }
+ ''
+ + config;
+ };
+
+ userLocations =
+ {
+ "/${name}" = ''
+ return 404;
+ '';
+ }
+ // optionalAttrs user.mail.dav {
+ "/${name}/dav" = ''
+ proxy_pass http://unix:/run/host-www/${name}/dav.sock;
+ '';
+ };
+ in
+ mapAttrs (_: userLocation) userLocations)
+ (filterAttrs (_: user: user.mail.certs != []) users);
+ };
+ };
+ };
+ };
+
+ systemd.tmpfiles.settings."10-run-host-www" =
+ concatMapAttrs
+ (name: _: {
+ "/run/host-www/${name}".d = {
+ mode = "0750";
+ user = name;
+ group = "nginx";
+ };
+ })
+ users;
+ };
+}
diff --git a/sys/web/sites/portal.nix b/sys/web/sites/portal.nix
new file mode 100644
index 0000000..c4d948e
--- /dev/null
+++ b/sys/web/sites/portal.nix
@@ -0,0 +1,42 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.local.web.sites.portal;
+ inherit (config.local) domains;
+in {
+ options.local.web.sites.portal = {
+ enable = mkEnableOption "public non-fqdn portal";
+ };
+
+ config = mkIf cfg.enable {
+ local.web = {
+ enable = mkDefault true;
+ ownedCerts = ["host" "sysret"];
+ defaultACMEHost = domains.host.main;
+ };
+
+ services.nginx.virtualHosts = {
+ ${domains.host.www} = {
+ forceSSL = true;
+ useACMEHost = domains.host.main;
+ serverAliases = [domains.host.main];
+ };
+
+ ${domains.sysret.main} = {
+ forceSSL = true;
+ useACMEHost = domains.sysret.main;
+ serverAliases = [domains.sysret.www];
+
+ locations = {
+ "/".alias = "${pkgs.local.sysret-static}/";
+ "/fsociety".return = "301 https://meet.posixlycorrect.com/%C6%92%C6%A8%C5%8F%C4%8B%D3%80%C9%99%CF%AE%D0%A3";
+ "/.well-known/openpgpkey/hu/".alias = "/var/lib/pgp-wkd/";
+ };
+ };
+ };
+ };
+}
diff --git a/sys/web/sites/pxe.nix b/sys/web/sites/pxe.nix
new file mode 100644
index 0000000..54f3e56
--- /dev/null
+++ b/sys/web/sites/pxe.nix
@@ -0,0 +1,180 @@
+{
+ config,
+ flakes,
+ lib,
+ pkgs,
+ ...
+}:
+with lib; let
+ cfg = config.local.web.sites.pxe;
+
+ pxeEnabled = machine: machine.config.local.boot.pxe.enable;
+ machines = listToAttrs (map (name: {
+ inherit name;
+ value = flakes.self.nixosConfigurations.${name};
+ })
+ cfg.machines);
+
+ stage2For = name: machine:
+ # https://purpleidea.com/blog/2025/01/23/working-around-an-ipxe-issue/
+ # "The answer:
+ # They told me that one of their engineers had struggled with the same issue for a while. The solution:
+ # Add the imgfree iPXE command before you boot!
+ # I wish the docs would hint that this is often required, but now I know, and now you know too!
+ # You can see that line here."
+ pkgs.writeText "stage2-${name}.ipxe" ''
+ #!ipxe
+ echo stage2: booting ${name}...
+ imgfree
+ kernel kernel init=${machine.config.system.build.toplevel}/init ${concatStringsSep " " machine.config.boot.kernelParams} || goto fail
+ initrd initrd || goto fail
+ boot ||
+ :fail
+ echo stage2: failed, rebooting in 5 seconds...
+ sleep 5
+ reboot --warm
+ '';
+
+ machineLocations = name: machine: let
+ mac = machine.config.local.boot.pxe.mac;
+ build = machine.config.system.build;
+ in {
+ "= /${mac}/initrd".alias = "${build.initialRamdisk}/initrd";
+ "= /${mac}/kernel".alias = "${build.kernel}/${pkgs.stdenv.hostPlatform.linux-kernel.target}";
+ "= /${mac}/stage2.ipxe".alias = "${stage2For name machine}";
+ };
+in {
+ options.local.web.sites.pxe = {
+ enable = mkEnableOption "PXE netboot image server";
+
+ linkLocal6 = mkOption {
+ type = types.str;
+ };
+
+ network = mkOption {
+ type = types.str;
+ };
+
+ machines = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions =
+ mapAttrsToList (name: machine: {
+ message = "PXE boot is not enabled in config '${name}'";
+ assertion = pxeEnabled machine;
+ })
+ machines;
+
+ local.web.enable = mkDefault true;
+
+ networking = {
+ firewall.extraCommands = ''
+ ip6tables -t filter -A local-input -p udp -i ${cfg.network} --dport 1792 -d ${cfg.linkLocal6} -j ACCEPT
+ '';
+ };
+
+ services = {
+ nginx = {
+ virtualHosts = {
+ "[${cfg.linkLocal6}]" = {
+ listenAddresses = ["[${cfg.linkLocal6}]"];
+
+ addSSL = false;
+ forceSSL = false;
+
+ locations = mergeAttrsList (mapAttrsToList machineLocations (filterAttrs (_: pxeEnabled) machines));
+ };
+ };
+ };
+ };
+
+ systemd = {
+ network = {
+ networks.${cfg.network}.addresses = [
+ {
+ Address = "${cfg.linkLocal6}/128";
+ AddPrefixRoute = "no";
+ PreferredLifetime = "0";
+ }
+ ];
+ };
+
+ services = {
+ pxe-store-upload = {
+ path = [config.nix.package pkgs.sshfs pkgs.squashfsTools];
+
+ script = let
+ host = lib.throw "TODO: pxe host";
+ machine = machines.${host};
+ in ''
+ set -e
+
+ pxe_ip='${machine.config.local.boot.pxe.linkLocal6}'
+ pxe_host='${host}'
+ pxe_system='${machine.config.system.build.toplevel}'
+
+ mountpoint="./pxe-initrd.$pxe_host"
+ store_img="$mountpoint/nix-store.squashfs"
+
+ mkdir -p "$mountpoint"
+
+ export PATH="/run/wrappers/bin:$PATH"
+ sshfs \
+ "root@[$pxe_ip%${cfg.network}]:/nix/.ro-store-img/" \
+ "$mountpoint" \
+ -o idmap=user
+
+ nix-store --query --requisites "$pxe_system" | \
+ xargs -I{} mksquashfs {} - \
+ -stream -comp zstd -one-file-system -no-xattrs \
+ >"$store_img.tmp"
+
+ mksquashfs -fix "$store_img.tmp"
+
+ sync "$store_img.tmp"
+ mv -- "$store_img.tmp" "$store_img"
+ fusermount3 -u "$mountpoint"
+ '';
+
+ serviceConfig = {
+ #AmbientCapabilities = ["CAP_SYS_ADMIN"];
+ #CapabilityBoundingSet = ["CAP_SYS_ADMIN"];
+ #BindPaths = ["/dev/fuse"];
+ #BindReadOnlyPaths = ["/nix/store"];
+ #DeviceAllow = ["/dev/fuse rwm"];
+ #DevicePolicy = "closed";
+ #MountAPIVFS = true;
+ ProtectSystem = "strict";
+ ProtectControlGroups = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ #PrivateDevices = true;
+ #PrivateTmp = true;
+ #PrivateUsers = true;
+ #TemporaryFileSystem = ["/"];
+ #DynamicUser = true;
+ RuntimeDirectory = "pxe-store-upload";
+ WorkingDirectory = "/run/pxe-store-upload";
+ };
+ };
+ };
+
+ sockets = {
+ pxe-store-upload = {
+ after = ["network-online.target"];
+ wants = ["network-online.target"];
+ wantedBy = ["sockets.target"];
+
+ socketConfig = {
+ ListenDatagram = "[${cfg.linkLocal6}]:1792%%${cfg.network}";
+ };
+ };
+ };
+ };
+ };
+}