diff options
Diffstat (limited to 'system/linde.nix')
-rw-r--r-- | system/linde.nix | 957 |
1 files changed, 594 insertions, 363 deletions
diff --git a/system/linde.nix b/system/linde.nix index 45c62ccb..730e0a14 100644 --- a/system/linde.nix +++ b/system/linde.nix @@ -13,15 +13,26 @@ let net-gw = "172.31.1.1"; net-ip6 = "2a01:4f8:c012:23a4::1"; net-rdnsip = "2a01:4f8:c012:23a4::53"; + net-acmeip = "2a01:4f8:c012:23a4::715"; net-mask6 = "64"; net-gw6 = "fe80::1"; + domain = "alanpearce.eu"; + ts-domain = "hydra-pinecone.ts.net"; + golink = (builtins.getFlake (toString <golink>)).nixosModules.default; in { imports = [ + <personal/modules/nixos/laminar.nix> + <home-manager/nixos> + <agenix/modules/age.nix> + <searchix/nix/modules> + golink # Include the results of the hardware scan. ./linde-hardware.nix - <agenix/modules/age.nix> + + ./settings/pin.nix + ./settings/services/git-server.nix ]; age.secrets = { paperless = @@ -37,7 +48,16 @@ in }; acme.file = ../secrets/acme.age; binarycache.file = ../secrets/binarycache.age; + dex.file = ../secrets/dex.age; powerdns.file = ../secrets/powerdns.age; + golink = let golink = config.services.golink; in { + # hope this doesn't collide... + path = "${golink.dataDir}/.config/tsnet-golink/auth.key"; + owner = golink.user; + mode = "400"; + symlink = false; + file = ../secrets/golink.age; + }; }; # Use the systemd-boot EFI boot loader. @@ -55,7 +75,6 @@ in environment.systemPackages = with pkgs; [ htop lsof - gitMinimal powerdns sqlite-interactive knot-dns @@ -64,51 +83,6 @@ in nix-output-monitor ]; - programs.ssh = with pkgs; { - knownHostsFiles = [ - (writeText "github.keys" '' - # github.com:22 SSH-2.0-babeld-05989c77 - # github.com:22 SSH-2.0-babeld-05989c77 - # github.com:22 SSH-2.0-babeld-05989c77 - # github.com:22 SSH-2.0-babeld-05989c77 - # github.com:22 SSH-2.0-babeld-05989c77 - github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= - github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= - github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl - '') - (writeText "gitlab.keys" '' - # gitlab.com:22 SSH-2.0-GitLab-SSHD - # gitlab.com:22 SSH-2.0-GitLab-SSHD - # gitlab.com:22 SSH-2.0-GitLab-SSHD - # gitlab.com:22 SSH-2.0-GitLab-SSHD - # gitlab.com:22 SSH-2.0-GitLab-SSHD - gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 - gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= - gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf - '') - (writeText "codeberg.keys" '' - # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 - # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 - # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 - # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 - # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 - codeberg.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8hZi7K1/2E2uBX8gwPRJAHvRAob+3Sn+y2hxiEhN0buv1igjYFTgFO2qQD8vLfU/HT/P/rqvEeTvaDfY1y/vcvQ8+YuUYyTwE2UaVU5aJv89y6PEZBYycaJCPdGIfZlLMmjilh/Sk8IWSEK6dQr+g686lu5cSWrFW60ixWpHpEVB26eRWin3lKYWSQGMwwKv4LwmW3ouqqs4Z4vsqRFqXJ/eCi3yhpT+nOjljXvZKiYTpYajqUC48IHAxTWugrKe1vXWOPxVXXMQEPsaIRc2hpK+v1LmfB7GnEGvF1UAKnEZbUuiD9PBEeD5a1MZQIzcoPWCrTxipEpuXQ5Tni4mN - codeberg.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL2pDxWr18SoiDJCGZ5LmxPygTlPu+cCKSkpqkvCyQzl5xmIMeKNdfdBpfbCGDPoZQghePzFZkKJNR/v9Win3Sc= - codeberg.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVIC02vnjFyL+I4RHfvIGNtOgJMe769VTF1VR4EB3ZB - '') - (writeText "sr.ht.keys" '' - # git.sr.ht:22 SSH-2.0-OpenSSH_9.6 - # git.sr.ht:22 SSH-2.0-OpenSSH_9.6 - # git.sr.ht:22 SSH-2.0-OpenSSH_9.6 - # git.sr.ht:22 SSH-2.0-OpenSSH_9.6 - # git.sr.ht:22 SSH-2.0-OpenSSH_9.6 - git.sr.ht ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ+l/lvYmaeOAPeijHL8d4794Am0MOvmXPyvHTtrqvgmvCJB8pen/qkQX2S1fgl9VkMGSNxbp7NF7HmKgs5ajTGV9mB5A5zq+161lcp5+f1qmn3Dp1MWKp/AzejWXKW+dwPBd3kkudDBA1fa3uK6g1gK5nLw3qcuv/V4emX9zv3P2ZNlq9XRvBxGY2KzaCyCXVkL48RVTTJJnYbVdRuq8/jQkDRA8lHvGvKI+jqnljmZi2aIrK9OGT2gkCtfyTw2GvNDV6aZ0bEza7nDLU/I+xmByAOO79R1Uk4EYCvSc1WXDZqhiuO2sZRmVxa0pQSBDn1DB3rpvqPYW+UvKB3SOz - git.sr.ht ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCj6y+cJlqK3BHZRLZuM+KP2zGPrh4H66DacfliU1E2DHAd1GGwF4g1jwu3L8gOZUTIvUptqWTkmglpYhFp4Iy4= - git.sr.ht ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60 - '') - ]; - }; - # Initial empty root password for easy login: users.users.root.initialHashedPassword = ""; services.openssh = { @@ -126,13 +100,14 @@ in programs.mosh.enable = true; system.autoUpgrade = { - enable = true; + enable = false; dates = "02:10"; randomizedDelaySec = "59 min"; allowReboot = true; flake = "git+file://${config.services.gitolite.dataDir}/repositories/nixfiles.git"; flags = [ "--no-write-lock-file" + "--impure" "--update-input" "nixpkgs-small" "--update-input" @@ -169,6 +144,7 @@ in services.nix-serve = { enable = true; + package = pkgs.nix-serve-ng; secretKeyFile = config.age.secrets.binarycache.path; }; @@ -181,6 +157,7 @@ in networking = { hostName = hostname; + inherit domain; useDHCP = false; dhcpcd.enable = false; nameservers = [ @@ -193,6 +170,7 @@ in ${net-ip4} = [ "${hostname}.alanpearce.eu" hostname ]; ${net-ip6} = [ "${hostname}.alanpearce.eu" hostname ]; ${net-rdnsip} = [ "dns" ]; + ${net-acmeip} = [ "acme" ]; }; firewall = { enable = true; @@ -210,52 +188,113 @@ in ]; allowedUDPPorts = [ 53 + 443 # HTTP/3 (QUIC) 3478 6885 # DHT 6922 ]; + trustedInterfaces = [ "tailscale0" ]; }; resolvconf = { - enable = true; + enable = false; useLocalResolver = false; }; }; - services.resolved.enable = false; + services.resolved = { + enable = true; + llmnr = "false"; + dnssec = "true"; + }; systemd.network = { enable = true; networks.${netif} = { name = netif; - gateway = [ net-gw ]; - routes = [{ - routeConfig = { + routes = [ + { Gateway = net-gw6; PreferredSource = net-ip6; - }; - }]; + QuickAck = true; + InitialCongestionWindow = 30; + InitialAdvertisedReceiveWindow = 30; + } + { + Gateway = net-gw; + QuickAck = true; + InitialCongestionWindow = 30; + InitialAdvertisedReceiveWindow = 30; + } + ]; address = [ "${net-ip6}/${net-mask6}" "${net-rdnsip}/${net-mask6}" + "${net-acmeip}/${net-mask6}" ]; addresses = [{ - addressConfig = { - Address = "${net-ip4}/${net-mask4}"; - Peer = "${net-gw}/32"; - }; + Address = "${net-ip4}/${net-mask4}"; + Peer = "${net-gw}/32"; }]; }; + wait-online = { + extraArgs = [ "--interface=${netif}" ]; + }; + }; + + services.tailscale = { + enable = true; + extraUpFlags = [ "--accept-routes" ]; + useRoutingFeatures = "client"; + }; + services.golink = { + enable = true; + tailscaleAuthKeyFile = config.age.secrets.golink.path; }; services.journald.extraConfig = '' MaxRetentionSec=1 month ''; - boot.kernel.sysctl = { - "net.ipv4.tcp_allowed_congestion_control" = "bbr illinois reno"; - "net.ipv4.tcp_congestion_control" = "bbr"; - "net.core.default_qdisc" = "fq"; + zramSwap = { + enable = true; + algorithm = "zstd"; }; + boot.kernel.sysctl = + let + buffer_size = 16 * 1024 * 1024; + server_count = 2; + max_clients = 100; + page_size = 4096; + # This server might have 100 clients simultaneously, so: + # max(tcp_wmem) * 2 * 100 / 4096 + mem = toString (buffer_size * server_count * max_clients / page_size); + in + { + "net.ipv4.tcp_allowed_congestion_control" = "bbr illinois reno"; + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "fq"; + + # Provide adequate buffer memory. + # rmem_max and wmem_max are TCP max buffer size + # settable with setsockopt(), in bytes + # tcp_rmem and tcp_wmem are per socket in bytes. + # tcp_mem is for all TCP streams, in 4096-byte pages. + # The following are suggested on IBM's + # High Performance Computing page + "net.core.rmem_max" = buffer_size; + "net.core.wmem_max" = buffer_size; + "net.core.rmem_default" = buffer_size; + "net.core.wmem_default" = buffer_size; + "net.ipv4.tcp_rmem" = "4096 87380 ${toString buffer_size}"; + "net.ipv4.tcp_wmem" = "4096 87380 ${toString buffer_size}"; + "net.ipv4.tcp_mem" = "${mem} ${mem} ${mem}"; + + "net.ipv4.tcp_sack" = false; + "net.ipv4.tcp_dsack" = false; + + "net.ipv4.tcp_slow_start_after_idle" = false; + }; + security.sudo.execWheelOnly = true; security.sudo.extraConfig = '' Defaults:root,%wheel env_keep+=EDITOR @@ -275,7 +314,7 @@ in users.users.root.shell = "${pkgs.fish}/bin/fish"; users.users.alan = { shell = "${pkgs.fish}/bin/fish"; - extraGroups = [ "wheel" "caddy" "docker" ]; + extraGroups = [ "wheel" "caddy" "docker" "laminar" ]; isNormalUser = true; home = "/home/alan"; createHome = true; @@ -284,6 +323,9 @@ in "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox" ]; }; + home-manager = { + users.alan = import ../user/server.nix; + }; users.users.nixremote = { shell = "/bin/sh"; @@ -295,6 +337,7 @@ in "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBmDSZnUzIPQowLrKSa24eSb1WFQe7yPjTcDPPe3UY0Q nix@mba" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9of82WBHK8nr8L9RGeieLMfcAWaFCeCkmvYHM9LCuT nanopi" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIy9jFioBvV0JA0lc+De2N+vDOABGHgCECW6vkD33CE4 sourcehut" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIII7sWEwsm8JZiJ0LUnjSt0Kg1RXypG6p5AzP/R2n5ca actions@github.com" ]; }; @@ -306,41 +349,69 @@ in # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). system.stateVersion = "23.05"; # Did you read the comment? - services.powerdns = { + services.goatcounter = { enable = true; - secretFile = config.age.secrets.powerdns.path; - extraConfig = '' - launch=gsqlite3 - dnsupdate=yes - allow-dnsupdate-from=0.0.0.0/0,::/0 - only-notify= - also-notify=216.218.130.2 - allow-axfr-ips=216.218.133.2,2001:470:600::2 - outgoing-axfr-expand-alias=yes - expand-alias=yes - resolver=1.1.1.1 - local-address=${net-ip4} ${net-ip6} - reuseport=yes - log-dns-details=no - log-dns-queries=no - loglevel=5 - primary=yes - secondary=yes - send-signed-notify=no - prevent-self-notification=no - - default-soa-edit=inception-increment - - api=yes - # replaced by secretFile/envsubst - api-key=$API_KEY - - gsqlite3-database=/var/db/pdns/zones.db - gsqlite3-pragma-foreign-keys=yes - gsqlite3-dnssec=yes - ''; + address = "localhost"; + port = 8082; + proxy = true; + extraArgs = [ + "-db" + "sqlite3+db/goatcounter.sqlite3" + "-websocket" + "-automigrate" + "-smtp" + "smtp://localhost:25" + ]; }; + services.powerdns = + let + inherit (lib.lists) flatten; + inherit (lib.strings) concatStringsSep; + he = { + notify = "216.218.130.2"; + axfr = [ + "216.218.133.2" + "2001:470:600::2" + ]; + }; + iplist = ips: concatStringsSep "," (flatten ips); + in + { + enable = true; + secretFile = config.age.secrets.powerdns.path; + extraConfig = '' + launch=gsqlite3 + dnsupdate=yes + allow-dnsupdate-from=0.0.0.0/0,::/0 + only-notify= + also-notify=${iplist [ he.notify ]} + allow-axfr-ips=${iplist [ he.axfr ]} + outgoing-axfr-expand-alias=yes + expand-alias=yes + resolver=1.1.1.1 + local-address=${net-ip4} ${net-ip6} + reuseport=yes + log-dns-details=no + log-dns-queries=no + loglevel=5 + primary=yes + secondary=yes + send-signed-notify=no + prevent-self-notification=no + + default-soa-edit=inception-increment + + api=yes + # replaced by secretFile/envsubst + api-key=$API_KEY + + gsqlite3-database=/var/db/pdns/zones.db + gsqlite3-pragma-foreign-keys=yes + gsqlite3-dnssec=yes + ''; + }; + systemd.services.hagezi-blocklist-update = { enable = true; startAt = "daily"; @@ -394,6 +465,24 @@ in }; }; + services.postfix = + let + localUser = "alan"; + forwardingAddress = "alan@alanpearce.eu"; + in + { + enable = true; + destination = [ ]; + domain = config.networking.domain; + virtual = '' + @${config.networking.hostName}.${config.networking.domain} ${localUser} + ${localUser} ${forwardingAddress} + ''; + config = { + inet_interfaces = "loopback-only"; + }; + }; + services.kresd = { enable = true; # package = pkgs.knot-resolver.override { extraFeatures = true; }; @@ -498,21 +587,6 @@ in }; }; - systemd.services.backup-etc-nixos = { - startAt = "04:30"; - path = with pkgs; [ - rdiff-backup - openssh - ]; - script = '' - rdiff-backup --api-version 201 backup /etc/nixos ${hostname}@home.alanpearce.eu::nixos - rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::nixos - ''; - serviceConfig = { - Type = "oneshot"; - }; - }; - systemd.services.backup-gitolite = { startAt = "daily"; path = with pkgs; [ @@ -544,23 +618,52 @@ in }; }; + services.acme-dns = { + enable = true; + settings = + let + me = "acme.${domain}"; + in + { + general = { + listen = "[${net-acmeip}]:53"; + protocol = "both6"; + domain = me; + nsname = me; + nsadmin = builtins.replaceStrings [ "@" ] [ "." ] config.security.acme.defaults.email; + records = [ + "${me}. AAAA ${net-acmeip}" + "${me}. NS ${me}." + ]; + }; + api = { + ip = "[${net-acmeip}]"; + tls = "letsencrypt"; + port = 443; + notification-email = config.security.acme.defaults.email; + }; + }; + }; + security.acme = { defaults = { email = "alan@alanpearce.eu"; - dnsProvider = "pdns"; - dnsResolver = "1.1.1.1:53"; + dnsProvider = "acme-dns"; credentialsFile = config.age.secrets.acme.path; reloadServices = [ "caddy" ]; validMinDays = 32; }; acceptTerms = true; certs."alanpearce.eu" = { - extraDomainNames = [ "*.alanpearce.eu" ]; + extraDomainNames = [ "*.alanpearce.eu" "*.linde.alanpearce.eu" ]; }; certs."dns.alanpearce.eu" = { reloadServices = map (x: "kresd@${toString x}") (range 1 config.services.kresd.instances); group = "knot-resolver"; }; + certs."stats.alanpearce.eu" = { + extraDomainNames = [ "*.stats.alanpearce.eu" ]; + }; }; users.groups.acme.members = [ "caddy" @@ -573,260 +676,322 @@ in auto_https disable_certs default_bind ${net-ip6} ${net-ip4} ''; - virtualHosts = { - "http://" = { - # Needed for HTTP->HTTPS servers - }; - "${hostname}.alanpearce.eu" = { - serverAliases = [ "https://" ]; - useACMEHost = "alanpearce.eu"; - extraConfig = '' - respond * 204 - ''; - }; - "pdns.alanpearce.eu" = { - useACMEHost = "alanpearce.eu"; - extraConfig = '' - log { - output discard - } - reverse_proxy 127.0.0.1:8081 - ''; - }; - "dns.alanpearce.eu" = { - useACMEHost = "alanpearce.eu"; - extraConfig = '' - log { - output discard - } - reverse_proxy localhost:443 { - transport http { - tls_server_name dns.alanpearce.eu - } - } - ''; - }; - "files.alanpearce.eu" = { - useACMEHost = "alanpearce.eu"; - extraConfig = '' - encode zstd gzip - root * /srv/http/files - file_server browse - ''; - }; - "git.alanpearce.eu" = - let - fcgi = config.services.fcgiwrap; - fcgisocket = "${fcgi.socketType}/${fcgi.socketAddress}"; - in - { + virtualHosts = + let + inherit (import ../lib/caddy.nix { inherit lib; }) security-headers; + in + { + "http://" = { + # Needed for HTTP->HTTPS servers + }; + "alanpearce.eu" = { + serverAliases = [ "www.alanpearce.eu" "test.alanpearce.eu" ]; useACMEHost = "alanpearce.eu"; extraConfig = '' - root * ${pkgs.cgit-pink}/cgit/ encode zstd gzip - handle_path /custom/* { - file_server { - root /srv/http/cgit/ - } + root * /srv/http/website/public + file_server + ${security-headers {}} + handle_errors { + rewrite * /404.html + file_server } - rewrite /robots.txt /assets/robots.txt - handle_path /assets/* { - file_server { - hide cgit.cgi - } + ''; + }; + "${hostname}.alanpearce.eu" = { + serverAliases = [ "https://" ]; + useACMEHost = "alanpearce.eu"; + extraConfig = '' + respond * 204 + ${security-headers {}} + ''; + }; + "pdns.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + log { + output discard } - @git_http_backend path_regexp "^/.+/(info/refs|git-upload-pack)$" - handle @git_http_backend { - reverse_proxy ${fcgisocket} { - transport fastcgi { - env SCRIPT_FILENAME ${pkgs.git}/libexec/git-core/git-http-backend - env GIT_PROJECT_ROOT ${config.services.gitolite.dataDir}/repositories - } - } + reverse_proxy 127.0.0.1:8081 + ''; + }; + "id.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + encode zstd gzip + ${security-headers {}} + reverse_proxy http://${config.services.dex.settings.web.http} + ''; + }; + "dns.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + log { + output discard } - handle { - reverse_proxy ${fcgisocket} { - transport fastcgi { - env SCRIPT_FILENAME {http.vars.root}/cgit.cgi - env CGIT_CONFIG ${pkgs.writeText "cgitrc" '' - head-include=/srv/http/cgit/responsive-cgit-css-master/head.html - css=/custom/responsive-cgit-css-master/cgit.css - virtual-root=/ - logo= - readme=:README.md - source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py - about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh - enable-git-config=1 - enable-index-owner=0 - enable-index-links=1 - enable-follow-links=0 - enable-log-linecount=1 - max-stats=year - snapshots=tar.lz tar.zst zip - cache-size=10240 - enable-http-clone=1 - enable-commit-graph=1 - mimetype-file=${pkgs.nginx}/conf/mime.types - section-from-path=1 - noplainemail=1 - repository-sort=age - root-title=my personal projects - clone-url=git://git.alanpearce.eu/$CGIT_REPO_URL https://git.alanpearce.eu/$CGIT_REPO_URL - remove-suffix=1 - strict-export=git-daemon-export-ok - scan-path=${config.services.gitolite.dataDir}/repositories/ - ''} - } - } + encode zstd gzip + reverse_proxy localhost:443 { + transport http { + tls_server_name dns.alanpearce.eu + } } ''; }; - "ntfy.alanpearce.eu" = { - useACMEHost = "alanpearce.eu"; - extraConfig = '' - encode zstd gzip - reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http} - ''; - }; - "searchix.alanpearce.eu" = { - useACMEHost = "alanpearce.eu"; - extraConfig = '' - reverse_proxy localhost:${toString config.services.searchix.settings.web.port} { - health_uri /health - health_status 2xx - } - encode zstd gzip { - match { - header Content-Type text/* - header Content-Type application/json* - header Content-Type application/javascript* - header Content-Type application/opensearchdescription+xml - header Content-Type application/atom+xml* - header Content-Type application/rss+xml* - header Content-Type image/svg+xml* - } - } - ''; - }; - "legit.alanpearce.eu" = - let - server = config.services.legit.settings.server; - in - { + "files.alanpearce.eu" = { useACMEHost = "alanpearce.eu"; extraConfig = '' encode zstd gzip - handle_path /static/* { - root * /srv/http/legit/src/static - file_server + ${security-headers {}} + root * /srv/http/files + file_server browse + ''; + }; + "ntfy.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + encode zstd gzip + ${security-headers {}} + reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http} { + health_uri /v1/health + health_body `"healthy":true` } - reverse_proxy ${server.host}:${toString server.port} ''; }; - "papers.alanpearce.eu" = { - extraConfig = '' - encode zstd gzip - handle_path /static/* { - root * ${config.services.paperless.package}/lib/paperless-ngx/static - file_server - } - reverse_proxy localhost:${toString config.services.paperless.port} - - ''; - }; - "binarycache.alanpearce.eu" = - let - ns = config.services.nix-serve; - in - { + "searchix.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + serverAliases = [ "searchix.linde.alanpearce.eu" ]; extraConfig = '' - reverse_proxy ${ns.bindAddress}:${toString ns.port} + reverse_proxy localhost:${toString config.services.searchix.settings.web.port} { + health_uri /health + health_status 2xx + } + encode zstd gzip { + match { + header Content-Type text/* + header Content-Type application/json* + header Content-Type application/javascript* + header Content-Type application/opensearchdescription+xml + header Content-Type application/atom+xml* + header Content-Type application/rss+xml* + header Content-Type image/svg+xml* + } + } ''; }; - }; + "binarycache.alanpearce.eu" = + let + ns = config.services.nix-serve; + in + { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + reverse_proxy ${ns.bindAddress}:${toString ns.port} + ''; + }; + "ci.alanpearce.eu" = + let + srv = config.services.laminar; + in + { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + reverse_proxy ${srv.settings.bindHTTP} + ''; + }; + "stats.alanpearce.eu" = + let + srv = config.services.goatcounter; + in + { + useACMEHost = "stats.alanpearce.eu"; + serverAliases = [ "*.stats.alanpearce.eu" ]; + extraConfig = '' + reverse_proxy ${srv.address}:${toString srv.port} + ''; + }; + "go.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + encode zstd gzip + ${security-headers {}} + root * /srv/http/go + file_server + ''; + }; + }; }; systemd.services.caddy.serviceConfig = { UMask = "007"; }; - services.fcgiwrap = { + networking.nat = { enable = true; - group = "gitolite"; - preforkProcesses = 2; - socketType = "tcp6"; - socketAddress = "[::1]:9000"; + internalInterfaces = [ "ve-+" ]; + externalInterface = netif; + enableIPv6 = true; }; - services.gitolite = { - enable = true; - adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox"; - extraGitoliteRc = '' - $RC{UMASK} = 0027; - $RC{LOG_EXTRA} = 0; - $RC{HOSTNAME} = "${config.networking.hostName}"; - $RC{LOCAL_CODE} = "$rc{GL_ADMIN_BASE}/local"; - push( @{$RC{ENABLE}}, 'D' ); - push( @{$RC{ENABLE}}, 'Shell alan' ); - push( @{$RC{ENABLE}}, 'cgit' ); - push( @{$RC{ENABLE}}, 'repo-specific-hooks' ); - ''; + + users.users.paperless = { + group = "paperless"; + uid = config.ids.uids.paperless; + home = "/srv/paperless"; }; - services.legit = { - enable = true; - group = "gitolite"; - settings = { - server.name = "legit.alanpearce.eu"; - dirs = { - templates = "/srv/http/legit/src/templates"; + users.groups.paperless.members = [ "alan" "syncthing" ]; + containers.papers = + let + hostDataDir = config.users.users.paperless.home; + localAddress6 = "fc00::2"; + tsHostname = "papers.${ts-domain}"; + tsPort = 41642; + in + { + # or maybe socket activated? + autoStart = true; + # does TS need this? + enableTun = true; + privateNetwork = true; + hostAddress6 = "fc00::1"; + inherit localAddress6; + forwardPorts = [{ + hostPort = tsPort; + }]; + bindMounts = { + ${config.services.paperless.dataDir} = { + hostPath = hostDataDir; + isReadOnly = false; + }; }; - repo = { - scanPath = "/srv/http/legit/repos"; - readme = [ - "readme" - "readme.md" - "README.md" + config = { + environment.systemPackages = with pkgs; [ + lsof ]; - }; - }; - }; - users.groups.git.gid = config.ids.gids.git; - services.gitDaemon = { - enable = true; - user = "git"; - group = "gitolite"; - basePath = "${config.services.gitolite.dataDir}/repositories/"; - }; + networking = { + useHostResolvConf = false; + resolvconf.enable = false; + firewall.trustedInterfaces = [ "tailscale0" ]; + firewall.rejectPackets = true; + nameservers = config.networking.nameservers; + }; + services.resolved = { + enable = true; + llmnr = "false"; + }; + services.tailscale = { + enable = true; + openFirewall = true; + permitCertUid = "caddy"; + port = tsPort; + }; + services.caddy = { + enable = true; + email = "caddy@alanpearce.eu"; + virtualHosts = { + "http://" = { + # avoid logging to an awkward file name based on the attribute name i.e. http:// + hostName = "papers"; + extraConfig = '' + redir ${tsHostname}{uri} + ''; + }; + ${tsHostname} = { + extraConfig = '' + encode zstd gzip + tls { + get_certificate tailscale + } + handle_path /static/* { + root * ${config.services.paperless.package}/lib/paperless-ngx/static + file_server + } + reverse_proxy [::1]:${toString config.services.paperless.port} + ''; + }; + }; + }; + services.paperless = { + enable = true; + address = "[::1]"; + settings = { + PAPERLESS_DBENGINE = "sqlite"; + PAPERLESS_TIME_ZONE = "Europe/Berlin"; - users.groups.paperless.members = [ "alan" "syncthing" ]; - services.paperless = { - enable = true; - package = pkgs.paperless-ngx; - dataDir = "/srv/paperless"; - settings = { - PAPERLESS_DBENGINE = "sqlite"; - PAPERLESS_TIME_ZONE = "Europe/Berlin"; + PAPERLESS_URL = "https://${tsHostname}"; + PAPERLESS_TRUSTED_PROXIES = "[::1]"; + PAPERLESS_USE_X_FORWARD_HOST = true; + PAPERLESS_USE_X_FORWARD_PORT = true; + PAPERLESS_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_PROTO" "https" ]; + PAPERLESS_ENABLE_COMPRESSION = false; # let caddy do it - PAPERLESS_URL = "https://papers.alanpearce.eu"; - PAPERLESS_TRUSTED_PROXIES = "127.0.0.1"; - PAPERLESS_USE_X_FORWARD_HOST = true; - PAPERLESS_USE_X_FORWARD_PORT = true; - PAPERLESS_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_PROTO" "https" ]; - PAPERLESS_ENABLE_COMPRESSION = false; # let caddy do it + PAPERLESS_OCR_SKIP_ARCHIVE_FILE = "with_text"; + PAPERLESS_OCR_LANGUAGE = "deu+eng"; + PAPERLESS_IGNORE_DATES = "09.08.90"; - PAPERLESS_OCR_SKIP_ARCHIVE_FILE = "with_text"; - PAPERLESS_OCR_LANGUAGE = "deu+eng"; - PAPERLESS_IGNORE_DATES = "09.08.90"; + PAPERLESS_TASK_WORKERS = 2; + PAPERLESS_THREADS_PER_WORKER = 1; + PAPERLESS_NUMBER_OF_SUGGESTED_DATES = 4; - PAPERLESS_TASK_WORKERS = 2; - PAPERLESS_THREADS_PER_WORKER = 1; - PAPERLESS_NUMBER_OF_SUGGESTED_DATES = 4; + PAPERLESS_CONSUMER_IGNORE_PATTERN = [ ".DS_STORE/*" "desktop.ini" ".stfolder/*" ".stversions/*" ]; - PAPERLESS_CONSUMER_IGNORE_PATTERN = [ ".DS_STORE/*" "desktop.ini" ".stfolder/*" ".stversions/*" ]; + PAPERLESS_FILENAME_FORMAT = "{correspondent}/{created} {title} {asn}"; + PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = true; + }; + }; + system.stateVersion = "24.11"; + }; + }; - PAPERLESS_FILENAME_FORMAT = "{correspondent}/{created} {title} {asn}"; - PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = true; + services.etcd = { + enable = true; + initialClusterState = "existing"; + dataDir = "/var/lib/etcd"; # TODO backup + extraConf = { + AUTO_COMPACTION_RETENTION = "1h"; }; }; + services.dex = + let + issuer = "https://id.alanpearce.eu/"; + in + { + enable = true; + environmentFile = config.age.secrets.dex.path; + settings = { + inherit issuer; + storage = { + type = "etcd"; + config = { + endpoints = config.services.etcd.listenClientUrls; + namespace = "dex/"; + }; + }; + web.http = "127.0.0.1:5556"; + connectors = [{ + type = "github"; + id = "github"; + name = "GitHub"; + config = { + clientID = "$GITHUB_CLIENT_ID"; + clientSecret = "$GITHUB_CLIENT_SECRET"; + redirectURI = "${issuer}callback"; + orgs = [{ + name = "alan-pearce"; + }]; + teamNameField = "slug"; + useLoginAsID = true; + }; + }]; + staticClients = [ + { + name = "Tailscale"; + id = "oCaiv7aije1thaep0eib"; + secretEnv = "TAILSCALE_CLIENT_SECRET"; + redirectURIs = [ "https://login.tailscale.com/a/oauth_response" ]; + } + ]; + }; + }; + services.syncthing = { enable = true; dataDir = "/srv/syncthing"; @@ -839,46 +1004,112 @@ in services.searchix = { enable = true; settings = { - web = { - baseURL = "https://searchix.alanpearce.eu"; - sentryDSN = "https://26d4cd8d20157ae2f6b4726ceae1a563@o4507187730120704.ingest.de.sentry.io/4507187734970448"; - contentSecurityPolicy = { - script-src = [ - "'self'" - "https://gc.zgo.at" - "https://js-de.sentry-cdn.com" - "https://browser.sentry-cdn.com" - ]; - img-src = [ - "'self'" - "https://gc.zgo.at" - ]; - connect-src = [ - "'self'" - "https://searchix.goatcounter.com/count" - "*.sentry.io" - ]; - worker-src = [ - "blob:" - ]; + web = + let + baseURL = "https://searchix.alanpearce.eu"; + in + { + inherit baseURL; + sentryDSN = "https://26d4cd8d20157ae2f6b4726ceae1a563@o4507187730120704.ingest.de.sentry.io/4507187734970448"; + contentSecurityPolicy = + let + self = "'self'"; + in + { + script-src = [ + (baseURL + "/static/") + "https://searchix.stats.alanpearce.eu" + "https://js-de.sentry-cdn.com" + "https://browser.sentry-cdn.com" + ]; + img-src = [ + self + "https://searchix.stats.alanpearce.eu" + ]; + connect-src = [ + self + "https://searchix.stats.alanpearce.eu/count" + "*.sentry.io" + ]; + worker-src = [ + "blob:" + ]; + }; + extraHeadHTML = '' + <script async + src="https://js-de.sentry-cdn.com/d735e99613a86e1625fb85d0e8e762de.min.js" + crossorigin="anonymous"></script> + <script data-goatcounter="https://searchix.stats.alanpearce.eu/count" + async src="//searchix.stats.alanpearce.eu/count.v4.js" + crossorigin="anonymous" + integrity="sha384-nRw6qfbWyJha9LhsOtSb2YJDyZdKvvCFh0fJYlkquSFjUxp9FVNugbfy8q1jdxI+"></script> + ''; }; - extraHeadHTML = '' - <script async - src="https://js-de.sentry-cdn.com/d735e99613a86e1625fb85d0e8e762de.min.js" - crossorigin="anonymous"></script> - <script data-goatcounter="https://searchix.goatcounter.com/count" - async src="//gc.zgo.at/count.js"></script> - ''; - }; importer.sources = { darwin = { enable = true; fetcher = "download"; - url = "https://alanpearce.github.io/nix-darwin-options"; + url = "https://alanpearce.github.io/nix-options/darwin"; + }; + home-manager = { + enable = true; + fetcher = "download"; + url = "https://alanpearce.github.io/nix-options/home-manager"; + }; + nixpkgs = { + enable = true; + fetcher = "channel-nixpkgs"; + channel = "nixos-unstable"; }; - home-manager.enable = true; + nixos = { + enable = true; + fetcher = "channel-nixpkgs"; + channel = "nixos-unstable"; + }; + }; + }; + }; + + programs.git = { + enable = true; + package = pkgs.gitMinimal; + config = { + advice = { + detachedHead = false; + mergeConflict = false; }; }; }; + + systemd.services.laminar.environment = { + NIX_PATH = "nixpkgs=${<nixpkgs>}"; + }; + services.laminar = { + enable = true; + path = with pkgs; [ + bash + coreutils + git + cached-nix-shell + nix + config.programs.ssh.package + flock + just + ]; + settings = { + bindHTTP = "[::1]:8002"; + keepRundirs = 1; + }; + }; + users.users.laminar = { + homeMode = "770"; + }; + + virtualisation.containers = { + enable = true; + policy = { + default = [{ type = "insecureAcceptAnything"; }]; + }; + }; } |