# SPDX-FileCopyrightText: 2020 Luke Granger-Brown # # SPDX-License-Identifier: Apache-2.0 { depot, lib, pkgs, config, ... }: { imports = [ ../lib/bgp.nix ../lib/zfs.nix ../lib/pomerium.nix ../totoro/barf.nix # eww ]; boot.initrd = { availableKernelModules = [ "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" "sr_mod" "bnx2" # ethernet ]; network = { enable = true; ssh = { enable = true; hostKeys = ["/persist/etc/ssh/ssh_host_ed25519_key"]; authorizedKeys = map builtins.readFile config.users.users.lukegb.openssh.authorizedKeys.keyFiles; }; postCommands = '' echo "zfs load-key -a; killall zfs" >> /root/.profile ''; }; }; boot.kernelParams = [ "ip=103.141.25.50::103.141.25.49:255.255.255.252:etheroute-lon01:eno1:none" ]; boot.kernelModules = [ "kvm-intel" ]; boot.kernel.sysctl = { "net.ipv4.conf.all.forwarding" = true; "net.ipv4.conf.default.forwarding" = true; }; # Use the systemd-boot EFI boot loader. boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; powerManagement.cpuFreqGovernor = lib.mkDefault "performance"; services.zfs.rollbackOnBoot = { enable = true; snapshot = "tank/local/root@blank"; }; systemd.services."systemd-networkd-wait-online".wantedBy = lib.mkForce []; fileSystems = let zfs = device: { device = device; fsType = "zfs"; }; in { "/" = zfs "tank/local/root"; "/nix" = zfs "tank/local/nix"; "/tmp" = zfs "tank/local/tmp"; "/persist" = zfs "tank/safe/persist"; "/home" = zfs "tank/safe/home"; "/boot" = { device = "/dev/disk/by-partlabel/ESP"; fsType = "vfat"; }; }; nix.settings.max-jobs = lib.mkDefault 8; # Networking! networking = { hostName = "etheroute-lon01"; domain = "as205479.net"; hostId = "420bee1b"; useNetworkd = true; nameservers = [ "2001:4860:4860::8888" "2001:4860:4860::8844" "8.8.8.8" "8.8.4.4" ]; defaultGateway = { address = "103.141.25.49"; interface = "eno1"; }; defaultGateway6 = { address = "2a07:242:800:64::1"; interface = "eno1"; }; interfaces.eno1 = { ipv4.addresses = [{ address = "103.141.25.50"; prefixLength = 30; }]; ipv6.addresses = [{ address = "2a07:242:800:64::68"; prefixLength = 64; }]; }; #interfaces.quadv1-4 = { # ipv4.addresses = [{ address = "92.118.31.254"; prefixLength = 24; }]; # virtual = true; #}; firewall.allowedTCPPorts = [ 80 443 ]; firewall.allowedUDPPorts = [ 51820 ]; firewall.extraCommands = '' # Flush old rules. ip46tables -D FORWARD -j lukegb-forward 2>/dev/null || true for chain in lukegb-forward lukegb-fwd-accept lukegb-fwd-reject; do ip46tables -F "$chain" 2>/dev/null || true ip46tables -X "$chain" 2>/dev/null || true done ip46tables -N lukegb-fwd-accept ip46tables -A lukegb-fwd-accept -j ACCEPT ip46tables -N lukegb-fwd-reject ip46tables -A lukegb-fwd-reject -p tcp ! --syn -j REJECT --reject-with tcp-reset ip46tables -A lukegb-fwd-reject -j REJECT ip46tables -N lukegb-forward # Accept from "trusted" quadv1 interface ip46tables -A lukegb-forward -i quadv1 -j lukegb-fwd-accept # Accept to quadv1 interface if we're multipathing. ip46tables -A lukegb-forward -o quadv1 -j lukegb-fwd-accept # Accept from established/related connections. ip46tables -A lukegb-forward -m conntrack --ctstate ESTABLISHED,RELATED -j lukegb-fwd-accept # Set up the firewall. ip46tables -A lukegb-forward -j lukegb-fwd-reject ip46tables -A FORWARD -j lukegb-forward ''; }; my.ip.tailscale = "100.99.227.112"; my.ip.tailscale6 = "fd7a:115c:a1e0:ab12:4843:cd96:6263:e370"; systemd.network.netdevs.quadv1 = { netdevConfig = { Name = "quadv1"; Kind = "wireguard"; }; wireguardConfig = { PrivateKeyFile = pkgs.writeText "etheroute-lon01-quadv" depot.ops.secrets.wireguard.quadv1.lukegb.privateKey; ListenPort = 51820; RouteTable = "off"; }; wireguardPeers = [{ PublicKey = depot.ops.secrets.wireguard.quadv1.quadv.publicKey; AllowedIPs = "0.0.0.0/0,::/0"; }]; }; systemd.network.networks.quadv1 = { matchConfig.Name = "quadv1"; networkConfig.Address = "169.254.111.0/31"; routes = [{ Gateway = "169.254.111.1"; Destination = "92.118.31.0/24"; }]; }; services.openssh.hostKeys = [ { path = "/persist/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; } { path = "/persist/etc/ssh/ssh_host_rsa_key"; type = "rsa"; bits = 4096; } ]; users.users = { lukegb.extraGroups = [ "bird2" ]; }; services.lukegbgp = let local = { asn = 205479; }; in { enable = true; config = { local = { routerID = "103.141.25.50"; }; peering = { etheroute = { local = local // { v4 = "103.141.25.50"; v6 = "2a07:242:800:64::68"; }; remote = { asn = 3170; export_community = 4000; routers = [{ v4 = "103.141.25.49"; v6 = "2a07:242:800:64::1"; }]; }; }; bgptoolscollector = { local = local // { v4 = "103.141.25.50"; v6 = "2a07:242:800:64::68"; }; remote = { asn = 212232; export_community = 5000; is_route_collector = true; routers = [{ v4 = "185.230.223.48"; v6 = "2a0c:2f07:9459::b6"; multihop = 64; }]; prefix_limit.v4 = 0; prefix_limit.v6 = 0; }; }; quadv = { local = local // { v4 = "169.254.111.0"; }; remote = { asn = 197753; export_community = 4099; routers = [{ v4 = "169.254.111.1"; }]; prefix_limit.v4 = 10; prefix_limit.v6 = 10; }; }; }; }; }; systemd.mounts = let bindMount' = dir: { unitConfig.RequiresMountsFor = dir; options = "bind"; what = "/persist${dir}"; where = dir; }; bindMountSvc = dir: svc: (bindMount' dir) // { bindsTo = [svc]; partOf = [svc]; }; bindMountSvcDynamic = dir: svc: (bindMount' "/var/lib/private/${dir}") // { requiredBy = [svc]; before = [svc]; wantedBy = ["multi-user.target"]; }; bindMount = dir: (bindMount' dir) // { wantedBy = ["multi-user.target"]; }; in [ (bindMountSvc "/var/lib/tailscale" "tailscaled.service") (bindMountSvcDynamic "barf-fe" "barf-fe.service") ]; services.redis.servers."" = { enable = true; bind = "127.0.0.1"; }; services.pomerium = { settings = { certificates = [ { cert = "/var/lib/acme/lukegb.com/fullchain.pem"; key = "/var/lib/acme/lukegb.com/privkey.pem"; } ]; routes = let baseConfig = { policy = [{ allow.and = [{ domain.is = "lukegb.com"; }]; }]; pass_identity_headers = true; timeout = "30s"; }; service = server: hostName: extraConfig: baseConfig // { from = "https://${hostName}"; to = "http://${server}"; preserve_host_header = true; } // extraConfig; secureService = server: hostName: extraConfig: service server hostName ({ to = "https://${server}"; tls_server_name = hostName; } // extraConfig); public = extraConfig: { allow_public_unauthenticated_access = true; policy = null; } // extraConfig; in [ (service "localhost:12001" "barf.lukegb.com" (public {})) (service "cofractal-ams01.int.as205479.net" "int.lukegb.com" {}) (service "cofractal-ams01.int.as205479.net" "logged-out.int.lukegb.com" (public {})) (service "cofractal-ams01.int.as205479.net" "sonarr.int.lukegb.com" {}) (service "cofractal-ams01.int.as205479.net" "radarr.int.lukegb.com" {}) (service "cofractal-ams01.int.as205479.net" "deluge.int.lukegb.com" {}) (service "cofractal-ams01.int.as205479.net" "content.int.lukegb.com" (public { timeout = "0"; # Downloads can take a while; bump the timeout. })) (service "totoro.int.as205479.net:9090" "prometheus.int.lukegb.com" {}) (service "totoro.int.as205479.net:9093" "alertmanager.int.lukegb.com" {}) (service "totoro.int.as205479.net:3000" "grafana.int.lukegb.com" {}) (service "totoro.int.as205479.net:10908" "tumblrandom.int.lukegb.com" {}) (secureService "swann.int.as205479.net:8443" "unifi.int.lukegb.com" { tls_skip_verify = true; allow_websockets = true; timeout = "0"; }) (secureService "totoro.int.as205479.net" "invoices.lukegb.com" (public { regex = "^/((third_party|ajax|client_area|pdf)/.*|[a-zA-Z0-9]{8})$"; tls_skip_verify = true; })) (secureService "totoro.int.as205479.net" "invoices.lukegb.com" { tls_skip_verify = true; }) (baseConfig // { from = "https://httpbin.int.lukegb.com"; to = "https://verify.pomerium.com"; }) (service "bvm-twitterchiver.int.as205479.net:8080" "twitterchiver.int.lukegb.com" {}) (service "bvm-twitterchiver.int.as205479.net:8080" "twitterchiver.lukegb.com" {}) (service "bvm-nixosmgmt.int.as205479.net:4440" "rundeck.int.lukegb.com" { set_request_headers = { "X-Forwarded-Roles" = "pomerium"; }; }) (service "bvm-ipfs.int.as205479.net:5001" "ipfs.int.lukegb.com" {}) (service "bvm-ipfs.int.as205479.net:8080" "ipfs-gw.int.lukegb.com" {}) (service "bvm-netbox.int.as205479.net:80" "netbox.int.lukegb.com" {}) (service "localhost:9901" "envoy-debug.int.lukegb.com" {}) (service "bvm-paperless.int.as205479.net:28981" "paperless.int.lukegb.com" { regex = "^/ws/.*"; allow_websockets = true; timeout = "0"; }) (service "bvm-paperless.int.as205479.net:28981" "paperless.int.lukegb.com" { regex = "^/api/.*"; timeout = "0"; }) (service "bvm-paperless.int.as205479.net:28981" "paperless.int.lukegb.com" {}) (service "totoro.int.as205479.net:1880" "nodered.int.lukegb.com" { allow_websockets = true; timeout = "0"; }) (service "totoro.int.as205479.net:3002" "code.int.lukegb.com" { allow_websockets = true; timeout = "0"; }) (service "totoro.int.as205479.net:8099" "zigbee2mqtt.int.lukegb.com" { allow_websockets = true; timeout = "0"; }) # These services are included for policy reasons only. # They have their own reverse proxy instances. (service "localhost:3000" "git.lukegb.com" { policy = [{ allow.not = [{ http_path.starts_with = "/user/login"; }]; } { allow.and = [{ domain.is = "lukegb.com"; }]; }]; }) ]; }; }; systemd.services.pomerium = { wants = lib.mkAfter [ "redis.service" ]; after = lib.mkAfter [ "redis.service" ]; serviceConfig = { SupplementaryGroups = [ "acme" ]; ReadOnlyPaths = [ "/var/lib/acme" ]; }; }; my.vault.acmeCertificates."lukegb.com" = { hostnames = [ "lukegb.com" "*.lukegb.com" "*.int.lukegb.com" ]; reloadOrRestartUnits = [ "pomerium.service" ]; }; users.groups.acme = {}; system.stateVersion = "22.11"; }