From dc25aed7041c51f1780c0a9f311e6eb90fd42217 Mon Sep 17 00:00:00 2001 From: Isabella Bastaja Date: Sun, 30 Aug 2026 17:16:32 +0200 Subject: WIP: init enode host --- .gitignore | 1 + hosts/enode/caddy.nix | 45 +++++++++++++++ hosts/enode/default.nix | 55 ++++++++++++++++++ hosts/enode/headscale-policy.json | 9 +++ hosts/enode/headscale.nix | 114 ++++++++++++++++++++++++++++++++++++++ hosts/tower/web/idm.nix | 18 +++--- 6 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 hosts/enode/caddy.nix create mode 100644 hosts/enode/default.nix create mode 100644 hosts/enode/headscale-policy.json create mode 100644 hosts/enode/headscale.nix diff --git a/.gitignore b/.gitignore index 8129dc3..4836661 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.pre-commit-config.yaml result .env +*.qcow2 diff --git a/hosts/enode/caddy.nix b/hosts/enode/caddy.nix new file mode 100644 index 0000000..bc3b745 --- /dev/null +++ b/hosts/enode/caddy.nix @@ -0,0 +1,45 @@ +{ config, ... }: +let + headscale = { + domain = config.services.headscale.address; + port = config.services.headscale.port; + }; + + simpleProxies = { + "sorre.xyz" = "tower.sorre.internal:7291"; + "git.sorre.xyz" = "tower.sorre.internal:8721"; + "idm.sorre.xyz" = "tower.sorre.internal:8443"; + }; + + simpleProxyVirtualHosts = builtins.mapAttrs (domain: upstream: { + extraConfig = '' + reverse_proxy ${upstream} { + headers_up True-Client-IP {remote_host} + header_up X-Real-IP {remote_host} + } + ''; + }) simpleProxies; +in +{ + services.caddy = { + enable = true; + virtualHosts = { + "http://headscale.sorre.xyz".extraConfig = '' + handle /generate_204 { + respond 204 + } + + handle * { + redir https://{host}{uri} + } + ''; + "headscale.sorre.xyz".extraConfig = '' + reverse_proxy ${headscale.domain}:${toString headscale.port} { + headers_up True-Client-IP {remote_host} + header_up X-Real-IP {remote_host} + } + ''; + } + // simpleProxyVirtualHosts; + }; +} diff --git a/hosts/enode/default.nix b/hosts/enode/default.nix new file mode 100644 index 0000000..0351393 --- /dev/null +++ b/hosts/enode/default.nix @@ -0,0 +1,55 @@ +{ pkgs, modulesPath, ... }: { + imports = [ + "${modulesPath}/virtualisation/qemu-vm.nix" + ./headscale.nix + ./caddy.nix + ]; + + boot.loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + + users.users.isabella = { + isNormalUser = true; + description = "Isabella Morgana Bastaja"; + extraGroups = [ "wheel" ]; + initialPassword = "isabella"; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM4aG8NI25TgjcDevjsY/Y6d3iYsQxSEtIpsqprMHmad osiris@underworld" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUcWGKF3uIf/gqbo1/lxb8XLoi67Bc2tVi1Bd/Ddhoz isabella@wonderland" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEvtzeVHgenfSUrpZeyZBoBTzN96rdYMy3KEJoRXja0O isabella@nyaan" + ]; + }; + + services.openssh = { + enable = true; + ports = [ 22 ]; + openFirewall = true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + AllowUsers = [ + "isabella" + ]; + }; + }; + + environment = { + systemPackages = [ + pkgs.git + pkgs.helix + ]; + shellAliases = { + rb = "cd /home/isabella/nixos-config && git pull --rebase && sudo nixos-rebuild switch --flake"; + }; + }; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "26.05"; # Did you read the comment? +} diff --git a/hosts/enode/headscale-policy.json b/hosts/enode/headscale-policy.json new file mode 100644 index 0000000..fd0db54 --- /dev/null +++ b/hosts/enode/headscale-policy.json @@ -0,0 +1,9 @@ +{ + "grants": [ + { + "src": ["autogroup:member"], + "dst": ["autogroup:self"], + "ip": ["*"] + } + ] +} diff --git a/hosts/enode/headscale.nix b/hosts/enode/headscale.nix new file mode 100644 index 0000000..d481917 --- /dev/null +++ b/hosts/enode/headscale.nix @@ -0,0 +1,114 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + mkIf + flip + ; + + cfg = config.services.headscale; +in +{ + services.headscale = { + enable = true; + address = "127.0.0.1"; + port = 8080; + settings = { + server_url = "https://headscale.sorre.xyz"; + database.sqlite = { + path = "/var/lib/headscale/db.sqlite"; + write_ahead_log = true; + }; + trusted_proxies = [ + "127.0.0.1/32" + "::1/128" + ]; + dns = { + base_domain = "sorre.internal"; + override_local_dns = true; + nameservers.global = [ + "1.1.1.1" + "1.0.0.1" + ]; + magic_dns = true; + extra_records = + let + towerAddr = "100.0.0.6"; + towerSubdomains = [ + "music" + "vaultwarden" + "git" + "kanboard" + ]; + in + flip map towerSubdomains (subdomain: { + type = "A"; + name = "${subdomain}.${cfg.settings.dns.base_domain}"; + value = towerAddr; + }); + }; + policy = { + mode = "file"; + path = ./headscale-policy.json; + }; + }; + }; + + systemd.services.backup-headscale = mkIf cfg.enable { + serviceConfig = { + Type = "exec"; + User = cfg.user; + Group = cfg.group; + StateDirectory = "backups/headscale"; + StateDirectoryMode = "0700"; + Umask = "077"; + }; + description = "Back up headscale data"; + conflicts = [ "headscale.service" ]; + path = [ pkgs.gnutar ]; + enableStrictShellChecks = true; + script = '' + #!/usr/bin/env bash + set -euo pipefail + + TIMESTAMP="$(date +%Y%m%d%H%M%S)" + BACKUP_PATH="$STATE_DIRECTORY/$TIMESTAMP.backup.tar" + echo "Backing up headscale to $BACKUP_PATH" >&2 + + tar cf "$BACKUP_PATH" /var/lib/headscale + chmod u=r,go-rwx "$BACKUP_PATH" + + find "$STATE_DIRECTORY" -type f -name '*.backup.tar' -print0 | + sort --zero-terminated --reverse | + tail --zero-terminated --lines=+6 | + xargs --null --no-run-if-empty rm -- + ''; + }; + + # See: https://unix.stackexchange.com/a/804263 + systemd.services.backup-headscale-restart-service = mkIf cfg.enable { + unitConfig = { + StopPropagatedFrom = "backup-headscale.service"; + Before = "backup-headscale.service"; + User = "root"; + }; + path = [ pkgs.systemdMinimal ]; + serviceConfig = { + Type = "oneshot"; + User = "root"; + RemainAfterExit = true; + ExecStop = "systemctl start headscale.service"; + }; + wantedBy = [ "backup-headscale.service" ]; + }; + + systemd.timers.backup-headscale = mkIf cfg.enable { + description = "Back up headscale every morning"; + timerConfig.OnCalendar = "*-*-* 05:00:00"; + wantedBy = [ "timers.target" ]; + }; +} diff --git a/hosts/tower/web/idm.nix b/hosts/tower/web/idm.nix index 8c93097..5707de7 100644 --- a/hosts/tower/web/idm.nix +++ b/hosts/tower/web/idm.nix @@ -14,15 +14,15 @@ let domainName = "idm.sorre.xyz"; in { - security.acme."${domainName}" = { - group = "kanidm"; - postRun = '' - cp --dereference --verbose {cert,key,chain}.pem /var/lib/kanidm/ - chown kanidm:kanidm /var/lib/kanidm/{cert,key,chain}.pem - chmod 400 /var/lib/kanidm/{cert,key,chain}.pem - ''; - reloadServices = [ "kanidm.service" ]; - }; + # security.acme.certs."${domainName}" = { + # group = "kanidm"; + # postRun = '' + # cp --dereference --verbose {cert,key,chain}.pem /var/lib/kanidm/ + # chown kanidm:kanidm /var/lib/kanidm/{cert,key,chain}.pem + # chmod 400 /var/lib/kanidm/{cert,key,chain}.pem + # ''; + # reloadServices = [ "kanidm.service" ]; + # }; services.kanidm = { package = pkgs.kanidm_1_11; -- cgit v1.3.1