{ config, lib, pkgs, self, ... }: let contributors = import ../../../contributors.nix; identities = contributors.identities; linearGroups = contributors.groups.linear; stripNewline = value: lib.replaceStrings [ "\n" ] [ "" ] value; authentikPasswordSecretPath = identity: if identity ? authentikPasswordSecret then config.age.secrets.${identity.authentikPasswordSecret}.path else null; bootstrapUsers = lib.mapAttrsToList ( username: identity: { inherit username; name = identity.displayName; email = identity.canonicalEmail; isAdmin = identity.isAdmin or false; groups = lib.optionals (identity.isAdmin or false) [ linearGroups.owners ]; passwordFile = authentikPasswordSecretPath identity; } ) (lib.filterAttrs (_: identity: identity.bootstrapAuthentik or false) identities); headscaleBootstrapUsers = lib.mapAttrsToList ( username: identity: { name = username; displayName = identity.displayName; email = identity.canonicalEmail; } ) (lib.filterAttrs (_: identity: identity.bootstrapAuthentik or false) identities); forgeUnixUsernames = builtins.attrNames (lib.filterAttrs (_: identity: identity.forgeUnixUser or false) identities); forgeUnixUsers = lib.genAttrs forgeUnixUsernames (username: let identity = identities.${username}; sshKeys = lib.optional (identity ? sshPublicKeyPath) (stripNewline (builtins.readFile identity.sshPublicKeyPath)); in { isNormalUser = true; createHome = true; home = "/home/${username}"; shell = pkgs.bashInteractive; extraGroups = lib.optional (identity.isAdmin or false) "wheel"; openssh.authorizedKeys.keys = sshKeys; }); forgeUnixAdminUsernames = builtins.attrNames (lib.filterAttrs (_: identity: (identity.forgeUnixUser or false) && (identity.isAdmin or false)) identities); forgeAuthorizedKeys = map (username: builtins.readFile identities.${username}.sshPublicKeyPath) (builtins.attrNames (lib.filterAttrs (_: identity: identity.forgeAuthorized or false) identities)); forgejoNscMaterializeTokenCache = '' install -d -m 700 -o forgejo-nsc -g forgejo-nsc /var/lib/forgejo-nsc/.config/ns ${pkgs.python3}/bin/python3 - <<'PY' import json from pathlib import Path raw = Path("/var/lib/forgejo-nsc/nsc.token").read_text(encoding="utf-8").strip() if not raw: raise SystemExit("empty Namespace token") if raw.startswith("{"): data = json.loads(raw) if "bearer_token" not in data and "session_token" not in data: raise SystemExit("Namespace token JSON must contain bearer_token or session_token") else: data = {"session_token": raw} Path("/var/lib/forgejo-nsc/.config/ns/token.json").write_text( json.dumps(data) + "\n", encoding="utf-8", ) PY chown forgejo-nsc:forgejo-nsc /var/lib/forgejo-nsc/.config/ns/token.json chmod 600 /var/lib/forgejo-nsc/.config/ns/token.json ''; in { imports = [ ./hardware-configuration.nix ./disko-config.nix self.nixosModules.burrow-forge self.nixosModules.burrow-forge-runner self.nixosModules.burrow-forgejo-nsc self.nixosModules.burrow-authentik self.nixosModules.burrow-garage self.nixosModules.burrow-headscale self.nixosModules.burrow-nix-cache self.nixosModules.burrow-observability self.nixosModules.burrow-zulip self.nixosModules.burrow-openbao self.nixosModules.burrow-jitsi ]; system.stateVersion = "24.11"; time.timeZone = "America/Los_Angeles"; nix.settings.experimental-features = [ "nix-command" "flakes" ]; users.users = forgeUnixUsers; security.sudo.extraRules = lib.map (username: { users = [ username ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; }) forgeUnixAdminUsernames; environment.systemPackages = lib.optionals config.services.forgejo-nsc.enable [ self.packages.${pkgs.stdenv.hostPlatform.system}.nsc ]; age.identityPaths = [ "/var/lib/agenix/agenix.key" ]; age.secrets.burrowAuthentikEnv = { file = ../../../secrets/infra/authentik.env.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowHeadscaleOidcClientSecret = { file = ../../../secrets/infra/headscale-oidc-client-secret.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowForgejoOidcClientSecret = { file = ../../../secrets/infra/forgejo-oidc-client-secret.age; owner = "forgejo"; group = "forgejo"; mode = "0440"; }; age.secrets.burrowGrafanaAdminPassword = { file = ../../../secrets/infra/grafana-admin-password.age; owner = "grafana"; group = "grafana"; mode = "0400"; }; age.secrets.burrowGrafanaSecretKey = { file = ../../../secrets/infra/grafana-secret-key.age; owner = "grafana"; group = "grafana"; mode = "0400"; }; age.secrets.burrowGrafanaOidcClientSecret = { file = ../../../secrets/infra/grafana-oidc-client-secret.age; owner = "grafana"; group = "grafana"; mode = "0400"; }; age.secrets.burrowTailscaleOidcClientSecret = { file = ../../../secrets/infra/tailscale-oidc-client-secret.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowLinearScimToken = { file = ../../../secrets/infra/linear-scim-token.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowAuthentikGoogleClientId = { file = ../../../secrets/infra/authentik-google-client-id.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowAuthentikGoogleClientSecret = { file = ../../../secrets/infra/authentik-google-client-secret.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowAuthentikGoogleAccountMap = { file = ../../../secrets/infra/authentik-google-account-map.json.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowAuthentikGoogleWifClientSecret = { file = ../../../secrets/infra/authentik-google-wif-client-secret.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowAuthentikUiTestPassword = { file = ../../../secrets/infra/authentik-ui-test-password.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowForgejoNscToken = { file = ../../../secrets/infra/forgejo-nsc-token.age; owner = "forgejo-nsc"; group = "forgejo-nsc"; mode = "0400"; }; age.secrets.burrowForgejoNscDispatcherConfig = { file = ../../../secrets/infra/forgejo-nsc-dispatcher-config.age; owner = "forgejo-nsc"; group = "forgejo-nsc"; mode = "0400"; }; age.secrets.burrowForgejoNscAutoscalerConfig = { file = ../../../secrets/infra/forgejo-nsc-autoscaler-config.age; owner = "forgejo-nsc"; group = "forgejo-nsc"; mode = "0400"; }; age.secrets.burrowZulipPostgresPassword = { file = ../../../secrets/infra/zulip-postgres-password.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowZulipRabbitmqPassword = { file = ../../../secrets/infra/zulip-rabbitmq-password.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowZulipRedisPassword = { file = ../../../secrets/infra/zulip-redis-password.age; owner = "root"; group = "root"; mode = "0400"; }; age.secrets.burrowZulipSecretKey = { file = ../../../secrets/infra/zulip-secret-key.age; owner = "root"; group = "root"; mode = "0400"; }; networking.extraHosts = '' 127.0.0.1 burrow.net git.burrow.net auth.burrow.net ts.burrow.net chat.burrow.net graphs.burrow.net vault.burrow.net meet.burrow.net inbox.burrow.net nsc-autoscaler.burrow.net ::1 burrow.net git.burrow.net auth.burrow.net ts.burrow.net chat.burrow.net graphs.burrow.net vault.burrow.net meet.burrow.net inbox.burrow.net nsc-autoscaler.burrow.net ''; services.burrow.forge = { enable = true; contactEmail = identities.contact.canonicalEmail; adminUsername = "contact"; adminEmail = identities.contact.canonicalEmail; adminPasswordFile = "/var/lib/burrow/intake/forgejo_pass_contact_at_burrow_net.txt"; oidcAdminGroup = contributors.groups.admins; oidcRestrictedGroup = contributors.groups.users; oidcClientSecretFile = config.age.secrets.burrowForgejoOidcClientSecret.path; authorizedKeys = forgeAuthorizedKeys; }; services.burrow.forgeRunner = { enable = true; sshPrivateKeyFile = "/var/lib/burrow/intake/agent_at_burrow_net_ed25519"; labels = [ "self-hosted" "linux" "x86_64" "burrow-forge" ]; }; services.forgejo-nsc = { enable = true; nscPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.nsc; nscTokenFile = config.age.secrets.burrowForgejoNscToken.path; dispatcher = { package = self.packages.${pkgs.stdenv.hostPlatform.system}.forgejo-nsc-dispatcher; configFile = config.age.secrets.burrowForgejoNscDispatcherConfig.path; }; autoscaler = { enable = true; package = self.packages.${pkgs.stdenv.hostPlatform.system}.forgejo-nsc-autoscaler; configFile = config.age.secrets.burrowForgejoNscAutoscalerConfig.path; }; }; systemd.services.forgejo-nsc-dispatcher.preStart = lib.mkAfter forgejoNscMaterializeTokenCache; systemd.services.forgejo-nsc-autoscaler.preStart = lib.mkAfter forgejoNscMaterializeTokenCache; services.burrow.authentik = { enable = true; envFile = config.age.secrets.burrowAuthentikEnv.path; forgejoClientSecretFile = config.age.secrets.burrowForgejoOidcClientSecret.path; grafanaClientSecretFile = config.age.secrets.burrowGrafanaOidcClientSecret.path; grafanaAccessGroupName = contributors.groups.users; headscaleClientSecretFile = config.age.secrets.burrowHeadscaleOidcClientSecret.path; tailscaleClientSecretFile = config.age.secrets.burrowTailscaleOidcClientSecret.path; defaultExternalApplicationSlug = "tailscale"; googleClientIDFile = config.age.secrets.burrowAuthentikGoogleClientId.path; googleClientSecretFile = config.age.secrets.burrowAuthentikGoogleClientSecret.path; googleAccountMapFile = config.age.secrets.burrowAuthentikGoogleAccountMap.path; googleWifClientSecretFile = config.age.secrets.burrowAuthentikGoogleWifClientSecret.path; googleLoginMode = "redirect"; userGroupName = contributors.groups.users; adminGroupName = contributors.groups.admins; extraGroupNames = [ contributors.groups.automation ]; tailscaleAccessGroupName = contributors.groups.users; bootstrapUsers = bootstrapUsers; linearAcsUrl = "https://api.linear.app/auth/sso/d0ca13dc-ac41-4824-8aab-e0ca352fc3de/acs"; linearAudience = "https://auth.linear.app/sso/d0ca13dc-ac41-4824-8aab-e0ca352fc3de"; linearDefaultRelayState = "https://linear.app/auth/sso/d0ca13dc-ac41-4824-8aab-e0ca352fc3de"; linearScimUrl = "https://api.linear.app/auth/scim/d0ca13dc-ac41-4824-8aab-e0ca352fc3de"; linearScimTokenFile = config.age.secrets.burrowLinearScimToken.path; linearScimUserIdentifier = "email"; linearOwnerGroupName = linearGroups.owners; linearAdminGroupName = linearGroups.admins; linearGuestGroupName = linearGroups.guests; zulipAccessGroupName = contributors.groups.users; }; services.grafana = { enable = true; settings = { server = { domain = "graphs.burrow.net"; root_url = "https://graphs.burrow.net/"; http_addr = "127.0.0.1"; http_port = 3001; enforce_domain = true; }; analytics = { reporting_enabled = false; check_for_updates = false; check_for_plugin_updates = false; }; security = { admin_user = "admin"; admin_password = "$__file{${config.age.secrets.burrowGrafanaAdminPassword.path}}"; secret_key = "$__file{${config.age.secrets.burrowGrafanaSecretKey.path}}"; cookie_secure = true; cookie_samesite = "strict"; disable_gravatar = true; }; users = { allow_sign_up = false; auto_assign_org = true; auto_assign_org_role = "Viewer"; }; auth = { disable_login_form = false; oauth_auto_login = false; }; "auth.generic_oauth" = { enabled = true; name = "burrow.net"; allow_sign_up = true; auto_login = true; client_id = "graphs.burrow.net"; client_secret = "$__file{${config.age.secrets.burrowGrafanaOidcClientSecret.path}}"; scopes = "openid profile email groups"; auth_url = "https://auth.burrow.net/application/o/grafana/authorize/"; token_url = "https://auth.burrow.net/application/o/grafana/token/"; api_url = "https://auth.burrow.net/application/o/grafana/userinfo/"; role_attribute_path = "contains(groups[*], 'burrow-admins') && 'Admin' || 'Viewer'"; }; }; }; services.caddy.virtualHosts."graphs.burrow.net".extraConfig = '' encode gzip zstd reverse_proxy 127.0.0.1:3001 ''; services.caddy.virtualHosts."releases.burrow.net".extraConfig = '' encode gzip zstd rewrite * /burrow-net-releases{uri} reverse_proxy https://storage.googleapis.com { header_up Host storage.googleapis.com } ''; services.caddy.virtualHosts."packages.burrow.net".extraConfig = '' encode gzip zstd rewrite * /burrow-net-packages{uri} reverse_proxy https://storage.googleapis.com { header_up Host storage.googleapis.com } ''; services.burrow.headscale = { enable = true; oidcClientSecretFile = config.age.secrets.burrowHeadscaleOidcClientSecret.path; bootstrapUsers = headscaleBootstrapUsers; }; services.burrow.garage = { enable = true; enableApiProxy = true; enableWebProxy = true; }; services.burrow.nixCache.enable = true; services.burrow.observability.enable = true; services.burrow.zulip = { enable = true; administratorEmail = identities.contact.canonicalEmail; postgresPasswordFile = config.age.secrets.burrowZulipPostgresPassword.path; rabbitmqPasswordFile = config.age.secrets.burrowZulipRabbitmqPassword.path; redisPasswordFile = config.age.secrets.burrowZulipRedisPassword.path; secretKeyFile = config.age.secrets.burrowZulipSecretKey.path; }; }