diff --git a/modules/cloud/firezone/default.nix b/modules/cloud/firezone/default.nix new file mode 100644 index 0000000..3a5a2c4 --- /dev/null +++ b/modules/cloud/firezone/default.nix @@ -0,0 +1,97 @@ +{ pkgs, config, lib, ... }: + +with lib; +let + cfg = config.cloud.firezone; + + mkImage = + { imageName, imageDigest, ... }: "${imageName}@${imageDigest}"; + # If we can pullImage we can just do + # mkImage = pkgs.dockerTools.pullImage; + + images = { + postgresql = mkImage { + imageName = "postgres"; + finalImageTag = "15-alpine"; + imageDigest = "sha256:07ec36ad2d5ab9250f38c8ef749239b662cf15d03c9ddb7167422edbbdf71156"; + }; + firezone = mkImage { + imageName = "firezone/firezone"; + finalImageTag = "latest"; + imageDigest = "sha256:76d869f322998432a09e3f3366f9f5908fe8b2f2968c80b4a60a1a78f879482f"; + }; + }; +in +{ + options.cloud.firezone = { + enable = mkEnableOption "Enable authentik OAuth server"; + envFile = mkOption { + type = types.path; + description = "Path to an environment file that is generated by bin/gen_env"; + }; + httpPort = mkOption { + type = types.int; + description = "Exposed HTTP port"; + default = 51880; + }; + wireguardPort = mkOption { + type = types.int; + description = "Exposed Wireguard port"; + default = 51821; + }; + }; + + config = mkIf cfg.enable { + systemd.services.arion-firezone.serviceConfig.EnvironmentFile = cfg.envFile; + virtualisation.arion.projects.firezone.settings = { + services.postgres.service = { + image = images.postgresql; + restart = "unless-stopped"; + healthcheck = { + test = [ "CMD-SHELL" "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}" ]; + start_period = "20s"; + interval = "30s"; + retries = 5; + timeout = "5s"; + }; + volumes = [ "/var/lib/firezone/database:/var/lib/postgresql/data" ]; + environment = { + POSTGRES_USER = "postgres"; + POSTGRES_DB = "firezone"; + POSTGRES_PASSWORD = "\${DATABASE_PASSWORD}"; + }; + networks = [ "firezone-network" ]; + }; + services.firezone.out.service.networks.firezone-network = { + ipv4_address = "172.25.0.100"; + ipv6_address = "2001:3990:3990::99"; + }; + services.firezone.service = { + image = images.firezone; + restart = "unless-stopped"; + volumes = [ "/var/lib/firezone/data:/var/firezone" ]; + env_file = [ cfg.envFile ]; + ports = [ + "${toString cfg.httpPort}:13000" + "${toString cfg.wireguardPort}:51820/udp" + ]; + capabilities.NET_ADMIN = true; + capabilities.SYS_MODULE = true; + sysctls = { + "net.ipv6.conf.all.disable_ipv6" = 0; + "net.ipv4.ip_forward" = 1; + "net.ipv6.conf.all.forwarding" = 1; + }; + depends_on = [ "postgres" ]; + }; + networks.firezone-network = { + enable_ipv6 = true; + driver = "bridge"; + ipam.config = [ + { subnet = "172.25.0.0/16"; } + { subnet = "2001:3990:3990::/64"; gateway = "2001:3990:3990::1"; } + ]; + }; + }; + }; +} diff --git a/modules/cloud/firezone/gen_env b/modules/cloud/firezone/gen_env new file mode 100755 index 0000000..d7a21dd --- /dev/null +++ b/modules/cloud/firezone/gen_env @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +cat <<-EOF +VERSION=latest +EXTERNAL_URL=_CHANGE_ME_ +DEFAULT_ADMIN_EMAIL=_CHANGE_ME_ +DEFAULT_ADMIN_PASSWORD=$(openssl rand -base64 12) +GUARDIAN_SECRET_KEY=$(openssl rand -base64 48) +SECRET_KEY_BASE=$(openssl rand -base64 48) +LIVE_VIEW_SIGNING_SALT=$(openssl rand -base64 24) +COOKIE_SIGNING_SALT=$(openssl rand -base64 6) +COOKIE_ENCRYPTION_SALT=$(openssl rand -base64 6) +DATABASE_ENCRYPTION_KEY=$(openssl rand -base64 32) +DATABASE_PASSWORD=$(openssl rand -base64 12) + +# The ability to change the IPv4 and IPv6 address pool will be removed +# in a future Firezone release in order to reduce the possible combinations +# of network configurations we need to handle. +# +# Due to the above, we recommend not changing these unless absolutely +# necessary. +WIREGUARD_IPV4_NETWORK=100.64.0.0/10 +WIREGUARD_IPV4_ADDRESS=100.64.0.1 +WIREGUARD_IPV6_NETWORK=fd00::/106 +WIREGUARD_IPV6_ADDRESS=fd00::1 +EOF diff --git a/modules/cloud/traefik/config.nix b/modules/cloud/traefik/config.nix index 9f7b7ba..d3ff0b9 100644 --- a/modules/cloud/traefik/config.nix +++ b/modules/cloud/traefik/config.nix @@ -48,7 +48,7 @@ let description = "The port that the service is listening on"; }; entrypoints = mkOption { - type = listOf (enum [ "http" "https" "smtp-submission" "smtp-submission-ssl" "imap" ]); + type = listOf (enum [ "http" "https" "smtp-submission" "smtp-submission-ssl" "imap" "wireguard" ]); default = [ "https" ]; description = "The entrypoints that will serve the host"; }; @@ -58,7 +58,7 @@ let description = "The middlewares to be used with the host."; }; protocol = mkOption { - type = enum [ "http" "tcp" ]; + type = enum [ "http" "tcp" "udp" ]; default = "http"; description = "The protocol of the router and service"; }; @@ -82,18 +82,18 @@ let # Turns a host configuration into dynamic traefik configuration hostToConfig = name: host: { "${host.protocol}" = { - routers."${name}-router" = { + routers."${name}-router" = (if (host.protocol != "udp") then { rule = filterOfHost host; - entryPoints = host.entrypoints; tls = { certResolver = "le"; } // (if host.protocol == "tcp" then { passthrough = if (host ? tlsPassthrough) then host.tlsPassthrough else true; } else { }); + } else { }) // { + entryPoints = host.entrypoints; service = "${name}-service"; - } // ( if host.protocol == "http" then { middlewares = lists.imap0 (id: m: "${name}-middleware-${toString id}") host.middlewares; } else if host.middlewares == [ ] then { } - else abort "Cannot have middlewares on tcp routers" + else abort "Cannot have middlewares on non-http routers" ); services."${name}-service".loadBalancer.servers = [ ( diff --git a/modules/cloud/traefik/default.nix b/modules/cloud/traefik/default.nix index 87a6d2e..89a994c 100644 --- a/modules/cloud/traefik/default.nix +++ b/modules/cloud/traefik/default.nix @@ -61,6 +61,8 @@ in entrypoints.imap.address = ":993"; entrypoints.smtp-submission.address = ":587"; entrypoints.smtp-submission-ssl.address = ":465"; + ## Wireguard + entrypoints.wireguard.address = ":51820/udp"; # Logging # ------- @@ -84,5 +86,8 @@ in # Set up firewall to allow traefik traffic. config.networking.firewall.allowedTCPPorts = [ 80 443 993 587 465 ]; - config.networking.firewall.allowedUDPPorts = [ 443 ]; # QUIC + config.networking.firewall.allowedUDPPorts = [ + 443 # QUIC + 51820 # Wireguard + ]; } diff --git a/nki-personal-do/configuration.nix b/nki-personal-do/configuration.nix index 335e506..edaf2d3 100644 --- a/nki-personal-do/configuration.nix +++ b/nki-personal-do/configuration.nix @@ -4,6 +4,7 @@ # Set up cloud ../modules/cloud/authentik + ../modules/cloud/firezone ../modules/cloud/postgresql ../modules/cloud/traefik ../modules/cloud/bitwarden @@ -151,12 +152,29 @@ # Writefreely cloud.writefreely.enable = true; - # Authentik (running under docker-compose T_T) + # Authentik sops.secrets.authentik-env = { }; cloud.authentik.enable = true; cloud.authentik.envFile = config.sops.secrets.authentik-env.path; cloud.traefik.hosts.authentik = { host = "auth.dtth.ch"; port = config.cloud.authentik.port; }; + # Firezone + sops.secrets.firezone-env = { }; + cloud.firezone.enable = true; + cloud.firezone.envFile = config.sops.secrets.firezone-env.path; + cloud.traefik.hosts.firezone = { + host = "vpn.dtth.ch"; + port = config.cloud.firezone.httpPort; + localHost = "127.0.0.1"; + }; + cloud.traefik.hosts.firezone-vpn = { + host = "vpn.dtth.ch"; + port = config.cloud.firezone.wireguardPort; + entrypoints = [ "wireguard" ]; + protocol = "udp"; + }; + + # Outline sops.secrets.minio-secret-key = { }; sops.secrets.authentik-oidc-client-secret = { owner = "outline"; }; diff --git a/nki-personal-do/secrets/secrets.yaml b/nki-personal-do/secrets/secrets.yaml index 007d1c4..e76dee6 100644 --- a/nki-personal-do/secrets/secrets.yaml +++ b/nki-personal-do/secrets/secrets.yaml @@ -14,6 +14,7 @@ outline: heisenbridge: ENC[AES256_GCM,data:rJY7gpcOY8nODR3KlYW1rEs54mKxr+AjNBeg1/2vTG0Gzpuvjgbnn5UVJS+P8uej/P4HfeFtlQSFZCEy8cXcwvwq97ppVliCGL4GMLRWaFmop35feC8t2ovh79cy/vKC7drASeGvWYNUmGRjboPuKA8W5LARa0HVDPGDLIEMVgJfYry/YKR3gsGmLzU7Mx1yLO6M/EFOJQJc84bSuu+CPSZcyUVF4SSNBiaDU5/NazlqaA9KWL6Xzu1MD2LEYdEFkRfitNgYj2m2gLd9voyGV4cfaCqJvYjJPwuZeZUoqCpDnom2JoV29q/Yq/gmyumPgOvriGxLsYBqV14MaCcE6KXE2uLicD+I/5or1AxepVDVjG9NoSgho1HpLvpRhMSCeXLk9+U+ykH3QA+0M+VVu9pswMMVQifnTtXZRM6pWxOnRVAzGf2tGDo4jy36S7pHaRn7SJcrljjWLfwHuNiu7E2uZhMrkcCjnjcBA9Xrb3drDQYVHya7XcoD4wOBHBDvVZwhYkNdkS3oYkom8A==,iv:fO1onfon3EdSNC/LjN1aWxpHBYq5aa0F/h0V6gl88ac=,tag:NL9p2nhIlEqgOdvUDM19Dg==,type:str] matrix-discord-bridge: ENC[AES256_GCM,data:/rlSjD6inKfak7HKKghH5ays5RjKmb9czGsoIOYHyTZC4A5EMucCbfn8DL1gkYXgvRHJ+QglGX/BGo5ebaxSj6nF60+aW87UG31KggOt5kkMuWsPsjvrufoc5IlNfWnXIWmqf8cdC01hmHEp7biUpI8CcfEZiD9OkOxbZcRfYqW+ttnzplFniRBjGPVZfL5g4DBbuJen5MuOrrMDo5CT+78n,iv:r9VBbDCAAElisCaDehrB6PhJHsaaHjdrk3103lmBT7o=,tag:WoNMMfyMifsL56yWq3MUOg==,type:str] authentik-env: ENC[AES256_GCM,data:CjxTaqIcpBX7ea9L3tgJDELr8HBPJdxXsrOfhsiH4cXwCEzktsNKHjF7l95ZFgI5O08q4Vlbln5Dg4xPEx33nwUesEbQrT5d+n+2YaAxmm/WInrYzF+jB7HYTXASb3rY9PWgd2C3v+YPBkJetHlTUc/k19Q7lOQRNw==,iv:cG8Bi2eCsS+v94tSJBsqp+bjVLzXZvvwX1QVVSYExL8=,tag:VmbfcxCcfi3IpKjg3f8QPw==,type:str] +firezone-env: ENC[AES256_GCM,data:D83cSNjwtoZ8wVXTHoqclHGFfydhwKyna7q1LMhHGS2nieF2Yg9vWDSDqrlKy7NklKNqgQ9rzZwdAmnSrFDiO7y4Le1N5vsLcPrL7JmIjS6yyh5tpMw4jjZkwEvwtSx+9hI6ec5beE9vOGceO0hCmN8zr0Tt6Wr8gUGrvMACF8mkFSatbowZi4RGvCEOjnPue66LQXGbVcAZX8jP7/UuIiqujiVsOI0xa1VwbpeAtQ9Pu+9JuMycpR7OcnXv2ImRZYGYYL1Fd/peXYIrwZiASYb4Yx3TsC88I4br+dVATicsP/k0O8FZCRL53CIogt44thAy8a/Gba1k9By92agLUlnJ0EUBB17JoQ/Ad8MQuR/F4s39ANAqztMig9oRfQBuNXb0HrBHBAGfgXLCzTw3wK/jiGwpQF5EMapT7jsZJFEFRIQLgI/uXaiuDtqd1evUljmtRwF35+8iqx1Jid+fM/kJ+aqx2B244L/8kEeRxqewY2eL0K7Tr+2LP0YQVsNFmvN6eY7DiA4m4jYBDlzUsBSRBIfj8oEvQML/STmmf4YqB8bPVQEEtKFwdRooDPqdozTGpaZlkEjAemUvI+xxe/8ZDq44uPswN154389gDRWO3fGPKmeRHx9HkMH8/6kMHiUnYnooqGnQ3SQdp+4v,iv:N0XBePOw2DYzN/GSBlza+sa3KwAZOrjavB++zrmqMAg=,tag:z0BA512TIgIxtQ4z40Zatg==,type:str] sops: kms: [] gcp_kms: [] @@ -47,8 +48,8 @@ sops: by9kZFlTRVdCZFkxYTVVb0RIRk8zUlkKCqMw9oL9RaYBV5Hhy3o8Nm5xmGrPH8Sd hv36sxRFFNZT/DCKaHaSRbT3mfpBZSTXJt1dgl4nZe6whH54t/1KmA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-04-26T19:50:09Z" - mac: ENC[AES256_GCM,data:QSS+gJoTtSwaB/seeo4QHEjdmzQ+qdYwmDtKWKV44KZnudHQuNYTlklKBC7gzLncOIaoPgQ04ZSlL/J4RSI4gLLrNuf+DkxX8OSIOv44U8ynBP/yWObgCPz8XjS38Jl9ovhLAPXYb6GK3DGl4q01ghXSpvfVsjCpz8W7SAVkVSA=,iv:Po0fPtu+gznmPalCm77RG3WloTKtRIEHLAec5lTYvaE=,tag:ulfUHDy1UAmj6d/R4kO42A==,type:str] + lastmodified: "2023-04-26T22:43:56Z" + mac: ENC[AES256_GCM,data:dWWck84crzDOwD8SlMjWarWn1ObcV4m7HJiS3+rpKdvjl4jYS5Nq5CXHwIY3YCsq63nelqLEjo8koas1wlEq4JsSYNpM1jVX5tjQia8wPjue6F5RNJdamfuQcctNEfqS4Wxo9HaxgpWrdRzpvTHzWfUjVn1UGGTnGArx8YeMQQ8=,iv:7CVQ01GQOPoqSkTi/o8XksOctGMpUJN06SxJy/nk88U=,tag:xDQoA8gQVt1mUFIj0k1aig==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.7.3