diff --git a/flake.lock b/flake.lock index 78c0194..e7ad6de 100644 --- a/flake.lock +++ b/flake.lock @@ -338,11 +338,11 @@ "secrets": { "flake": false, "locked": { - "lastModified": 1777076192, - "narHash": "sha256-N7n2OPN2IRWwL73Cr6mc5nNhucHmMeFas9hQ/NF0bFg=", + "lastModified": 1777152364, + "narHash": "sha256-yS8TxtPFFf7xIDNbsErZUnTBLn2fnyCcC4On+t3v1Zs=", "ref": "refs/heads/main", - "rev": "34ff1c4b0460a2e103a8fec183f53f274dc123ed", - "revCount": 32, + "rev": "bfb7da1297d73100a56a044d09792fc6e59357e6", + "revCount": 34, "type": "git", "url": "ssh://git@git.jfreudenberger.de/JuliusFreudenberger/nix-private.git" }, diff --git a/flake.nix b/flake.nix index 74a3034..d3bb15e 100644 --- a/flake.nix +++ b/flake.nix @@ -120,6 +120,9 @@ specialArgs = { inherit inputs outputs; + pkgs-unstable = import nixpkgs-unstable { + inherit system; + }; }; modules = [ diff --git a/hosts/busch/default.nix b/hosts/busch/default.nix index 199d471..e6efa4c 100644 --- a/hosts/busch/default.nix +++ b/hosts/busch/default.nix @@ -1,4 +1,4 @@ -{ inputs, outputs, config, lib, pkgs, ... }: +{ inputs, outputs, config, lib, pkgs, pkgs-unstable, ... }: { imports = @@ -7,6 +7,7 @@ ../../modules/nix.nix ../../modules/auto-upgrade.nix + ../../users/julius/nixos-server.nix ../../modules/locale.nix ../../modules/server-cli.nix ../../modules/sshd.nix @@ -23,21 +24,29 @@ }; tmp.useTmpfs = true; }; + + system.autoUpgrade.allowReboot = false; + networking.hostName = "busch"; # Define your hostname. - users = { - users = { - julius = { - isNormalUser = true; - uid = 1000; - extraGroups = [ "wheel" "julius" ]; + + services.netbird = { + package = pkgs-unstable.netbird; + useRoutingFeatures = "both"; + clients.wt0 = { + hardened = false; + login = { + enable = true; + setupKeyFile = (pkgs.writeText "setupKey" '' + A99F5508-D543-40B7-A31A-A8931B1AE246 + '').outPath; }; - }; - groups = { - julius = { - gid = 1000; + port = 51820; + environment = { + NB_MANAGEMENT_URL = "https://netbird.jfreudenberger.de"; }; }; }; + systemd.services.${config.services.netbird.clients.wt0.service.name}.path = [ pkgs.shadow ]; nix.settings = { substituters = [ diff --git a/hosts/srv03/default.nix b/hosts/srv03/default.nix index b56205c..1cc29bf 100644 --- a/hosts/srv03/default.nix +++ b/hosts/srv03/default.nix @@ -16,6 +16,7 @@ ../../modules/docker.nix ../../modules/traefik.nix ../../modules/pocket-id.nix + ../../modules/netbird-docker.nix ../../modules/auto-upgrade.nix "${inputs.secrets}/modules/opkssh.nix" # Include the results of the hardware scan. @@ -42,6 +43,24 @@ }; environmentFile = config.age.secrets.pocket-id.path; }; + + netbird-docker = { + enable = true; + secrets = config.age.secrets.netbird-server; + proxy = { + domain = "netbird.jfreudenberger.de"; + token-secret = config.age.secrets.netbird-proxy; + }; + }; + netbird.server = let + domain = "netbird.jfreudenberger.de"; + in { + domain = domain; + management.domain = domain; + dashboard.domain = domain; + signal.domain = domain; + management.oidcConfigEndpoint = "https://login.jfreudenberger.de/.well-known/openid-configuration"; + }; }; systemd.network = { diff --git a/hosts/srv03/secrets.nix b/hosts/srv03/secrets.nix index 2a119d0..e7368f7 100644 --- a/hosts/srv03/secrets.nix +++ b/hosts/srv03/secrets.nix @@ -3,5 +3,7 @@ age.secrets = { inwx.file = "${inputs.secrets}/secrets/dns-management/inwx"; pocket-id.file = "${inputs.secrets}/secrets/srv03/pocket-id"; + netbird-server.file = "${inputs.secrets}/secrets/srv03/netbird-server"; + netbird-proxy.file = "${inputs.secrets}/secrets/srv03/netbird-proxy"; }; } diff --git a/modules/auto-upgrade.nix b/modules/auto-upgrade.nix index 3dc9849..27bb8bc 100644 --- a/modules/auto-upgrade.nix +++ b/modules/auto-upgrade.nix @@ -1,6 +1,7 @@ { inputs, pkgs, + lib, ... }: { system.autoUpgrade = { @@ -12,7 +13,7 @@ flake = inputs.self.outPath; dates = "02:00"; randomizedDelaySec = "45min"; - allowReboot = true; + allowReboot = lib.mkDefault true; rebootWindow = { lower = "01:00"; upper = "05:00"; diff --git a/modules/dockhand.nix b/modules/dockhand.nix index 1e8f57b..5f43cda 100644 --- a/modules/dockhand.nix +++ b/modules/dockhand.nix @@ -38,7 +38,7 @@ in { }; extraOptions = [ ''--mount=type=volume,source=dockhand-data,target=/app/data,volume-driver=local'' - ''--group-add=${config.ids.gids.docker}'' + ''--group-add=${toString config.ids.gids.docker}'' ]; }; }; diff --git a/modules/netbird-docker.nix b/modules/netbird-docker.nix new file mode 100644 index 0000000..076fdaf --- /dev/null +++ b/modules/netbird-docker.nix @@ -0,0 +1,208 @@ +{ + pkgs, + utils, + config, + lib, + ... +}: +let + + cfg = config.services.netbird-docker; + netbirdCfg = config.services.netbird; + + serverVersion = "0.69.0"; + dashboardVersion = "2.37.1"; + +in { + + options.services.netbird-docker = { + enable = lib.mkEnableOption "Netbird Server stack, comprising the dashboard, management API, signal service, relay and STUN server"; + enableLocalAuth = lib.mkEnableOption "local authentication"; + proxy = lib.mkOption { + description = "Configuration for proxy"; + type = lib.types.submodule { + options = { + domain = lib.mkOption { + description = "Domain the proxy is reachable at. Custom domains will need to add a CNAME record of the wildcard subdomain to this domain."; + type = lib.types.str; + }; + token-secret = lib.mkOption { + description = '' + Proxy token in env-file notation. + Name of the environment variable is `NB_PROXY_TOKEN`. + Create the proxy token after netbird is installed with the following command: docker exec -it netbird-server /go/bin/netbird-server --config /etc/netbird/config.yaml token create --name local + ''; + type = lib.types.anything; + }; + }; + }; + }; + secrets = lib.mkOption { + description = '' + Secret for combined server in env-file notation. + Name of the relevant environment variables: + - NETBIRD_RELAY_AUTH_SECRET - Shared authentication secret for relay + - NETBIRD_DATASTORE_ENC_KEY - Encryption key for sensitive data + ''; + type = lib.types.anything; + }; + }; + + config = lib.mkIf cfg.enable { + virtualisation.oci-containers.containers = { + netbird-dashboard = { + image = "netbirdio/dashboard:v${dashboardVersion}"; + autoStart = true; + networks = [ + "webproxy" + ]; + environment = { + NETBIRD_MGMT_API_ENDPOINT = "https://${netbirdCfg.server.management.domain}"; + NETBIRD_MGMT_GRPC_API_ENDPOINT = "https://${netbirdCfg.server.management.domain}"; + AUTH_AUDIENCE="netbird-dashboard"; + AUTH_CLIENT_ID="netbird-dashboard"; + AUTH_CLIENT_SECRET=""; + AUTH_AUTHORITY = "https://${netbirdCfg.server.domain}/oauth2"; + USE_AUTH0="false"; + AUTH_SUPPORTED_SCOPES="openid profile email groups"; + AUTH_REDIRECT_URI="/nb-auth"; + AUTH_SILENT_REDIRECT_URI="/nb-silent-auth"; + NGINX_SSL_PORT="443"; + LETSENCRYPT_DOMAIN="none"; + }; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.netbird-dashboard.rule" = "Host(`${netbirdCfg.server.dashboard.domain}`)"; + "traefik.http.routers.netbird-dashboard.entrypoints" = "websecure"; + "traefik.http.routers.netbird-dashboard.tls" = "true"; + "traefik.http.routers.netbird-dashboard.tls.certresolver" = "letsencrypt"; + "traefik.http.routers.netbird-dashboard.service" = "dashboard"; + "traefik.http.routers.netbird-dashboard.priority" = "1"; + "traefik.http.services.dashboard.loadbalancer.server.port" = "80"; + }; + dependsOn = [ + "netbird-server" + ]; + }; + netbird-server = { + image = "netbirdio/netbird-server:${serverVersion}"; + autoStart = true; + networks = [ + "webproxy" + ]; + entrypoint = "/bin/sh"; + cmd = [ + "-c" + ''sed -e "s|__AUTH_SECRET__|$NETBIRD_RELAY_AUTH_SECRET|" -e "s|__DATASTORE_ENC_KEY__|$NETBIRD_DATASTORE_ENC_KEY|" /etc/netbird/config.yaml.tmpl > /etc/netbird/config.yaml && /go/bin/netbird-server --config /etc/netbird/config.yaml'' + ]; + ports = [ + "3478:3478/udp" + ]; + volumes = let + server-config = (pkgs.formats.yaml {}).generate "netbird-server-config" { + server = { + listenAddress = ":80"; + exposedAddress = "https://${netbirdCfg.server.domain}:443"; + stunPorts = [ 3478 ]; + metricsPort = 9090; + healthCheckAddress = ":9000"; + logLevel = netbirdCfg.server.management.logLevel; + logFile = "console"; + authSecret = "__AUTH_SECRET__"; + dataDir = "/var/lib/netbird"; + auth = { + issuer = "https://${netbirdCfg.server.dashboard.domain}/oauth2"; + localAuthDisabled = !cfg.enableLocalAuth; + signKeyRefreshEnabled = true; + dashboardRedirectURIs = [ + "https://${netbirdCfg.server.dashboard.domain}/nb-auth" + "https://${netbirdCfg.server.dashboard.domain}/nb-silent-auth" + ]; + cliRedirectURIs = [ + "http://localhost:53000" + ]; + }; + reverseProxy.trustedHTTPProxies = [ + "172.18.0.2/32" + ]; + store = { + engine = "sqlite"; + encryptionKey = "__DATASTORE_ENC_KEY__"; + }; + }; + }; + in [ + "${server-config}:/etc/netbird/config.yaml.tmpl" + ]; + environmentFiles = [ + cfg.secrets.path + ]; + extraOptions = [ + ''--mount=type=volume,source=netbird_data,target=/var/lib/netbird,volume-driver=local'' + ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.netbird-grpc.rule" = "Host(`${netbirdCfg.server.signal.domain}`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`))"; + "traefik.http.routers.netbird-grpc.entrypoints" = "websecure"; + "traefik.http.routers.netbird-grpc.tls" = "true"; + "traefik.http.routers.netbird-grpc.tls.certresolver" = "letsencrypt"; + "traefik.http.routers.netbird-grpc.service" = "netbird-server-h2c"; + "traefik.http.routers.netbird-grpc.priority" = "100"; + "traefik.http.routers.netbird-backend.rule" = "Host(`${netbirdCfg.server.domain}`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`))"; + "traefik.http.routers.netbird-backend.entrypoints" = "websecure"; + "traefik.http.routers.netbird-backend.tls" = "true"; + "traefik.http.routers.netbird-backend.tls.certresolver" = "letsencrypt"; + "traefik.http.routers.netbird-backend.service" = "netbird-server"; + "traefik.http.routers.netbird-backend.priority" = "100"; + "traefik.http.services.netbird-server.loadbalancer.server.port" = "80"; + "traefik.http.services.netbird-server-h2c.loadbalancer.server.port" = "80"; + "traefik.http.services.netbird-server-h2c.loadbalancer.server.scheme" = "h2c"; + }; + dependsOn = [ + "traefik" + ]; + }; + netbird-proxy = { + image = "netbirdio/reverse-proxy:${serverVersion}"; + autoStart = true; + ports = [ + "51820:51820/udp" + ]; + networks = [ + "webproxy" + ]; + dependsOn = [ + "netbird-server" + ]; + environment = { + NB_PROXY_MANAGEMENT_ADDRESS="http://netbird-server:80"; + NB_PROXY_ALLOW_INSECURE="true"; + NB_PROXY_DOMAIN = cfg.proxy.domain; + NB_PROXY_ADDRESS = ":8443"; + NB_PROXY_CERTIFICATE_DIRECTORY = "/certs"; + NB_PROXY_ACME_CERTIFICATES = "true"; + NB_PROXY_ACME_CHALLENGE_TYPE = "tls-alpn-01"; + NB_PROXY_FORWARDED_PROTO = "https"; + NB_PROXY_PROXY_PROTOCOL = "true"; + NB_PROXY_TRUSTED_PROXIES = "172.18.0.2"; + }; + environmentFiles = [ + cfg.proxy.token-secret.path + ]; + extraOptions = [ + ''--mount=type=volume,source=netbird_proxy_certs,target=/certs,volume-driver=local'' + ]; + labels = { + "traefik.enable" = "true"; + "traefik.tcp.routers.proxy-passthrough.entrypoints" = "websecure"; + "traefik.tcp.routers.proxy-passthrough.rule" = "HostSNI(`*`)"; + "traefik.tcp.routers.proxy-passthrough.tls.passthrough" = "true"; + "traefik.tcp.routers.proxy-passthrough.service" = "proxy-tls"; + "traefik.tcp.routers.proxy-passthrough.priority" = "1"; + "traefik.tcp.services.proxy-tls.loadbalancer.server.port" = "8443"; + "traefik.tcp.services.proxy-tls.loadbalancer.serverstransport" = "pp-v2@file"; + }; + }; + }; + }; +} diff --git a/modules/traefik.nix b/modules/traefik.nix index 8bea41f..8888dac 100644 --- a/modules/traefik.nix +++ b/modules/traefik.nix @@ -76,6 +76,7 @@ in { ]; extraOptions = [ ''--mount=type=volume,source=certs,target=/certs,volume-driver=local'' + "--ip=172.18.0.2" "--add-host=host.docker.internal:host-gateway" "--health-cmd=wget --spider --quiet http://localhost:8080/ping" "--health-interval=10s" @@ -130,7 +131,5 @@ in { ''; }; - networking.firewall.extraCommands = "iptables -t nat -I PREROUTING -s 172.18.0.0/16 -d 172.18.0.0/16 -j MASQUERADE"; - }; }