diff --git a/add-ons/authentik/blueprints.yaml b/add-ons/authentik/blueprints.yaml index fe2604b..813a9bc 100644 --- a/add-ons/authentik/blueprints.yaml +++ b/add-ons/authentik/blueprints.yaml @@ -99,3 +99,42 @@ data: name: Grafana provider: !KeyOf grafana-provider meta_launch_url: https://grafana.whyredfire.dev + wireguard-admin.yaml: | + version: 1 + metadata: + name: WireGuard admin + entries: + - model: authentik_providers_proxy.proxyprovider + id: wireguard-admin-provider + identifiers: + name: wireguard-admin + attrs: + authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]] + invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]] + external_host: https://wg-admin.whyredfire.dev + mode: forward_single + - model: authentik_core.application + id: wireguard-admin + identifiers: + slug: wireguard-admin + attrs: + name: WireGuard Admin + provider: !KeyOf wireguard-admin-provider + meta_launch_url: https://wg-admin.whyredfire.dev + # Issuing VPN clients is admin-only. + - model: authentik_policies.policybinding + identifiers: + order: 0 + target: !KeyOf wireguard-admin + attrs: + group: !Find [authentik_core.group, [name, admins]] + # This replaces the embedded outpost's provider list, so it also names the + # providers created in the UI. + - model: authentik_outposts.outpost + identifiers: + managed: goauthentik.io/outposts/embedded + attrs: + providers: + - !Find [authentik_providers_proxy.proxyprovider, [name, garage-ui]] + - !Find [authentik_providers_proxy.proxyprovider, [name, qbittorrent]] + - !KeyOf wireguard-admin-provider diff --git a/add-ons/authentik/referencegrant.yaml b/add-ons/authentik/referencegrant.yaml index bf1d8a0..eba9ffb 100644 --- a/add-ons/authentik/referencegrant.yaml +++ b/add-ons/authentik/referencegrant.yaml @@ -11,6 +11,9 @@ spec: - group: gateway.networking.k8s.io kind: HTTPRoute namespace: qbit + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespace: wireguard to: - group: "" kind: Service diff --git a/apps/wireguard/configmap.yaml b/apps/wireguard/configmap.yaml deleted file mode 100644 index b65af57..0000000 --- a/apps/wireguard/configmap.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -data: - wg0.conf: | - # Based on the image's /defaults/server.conf. The init container adds - # PrivateKey from the SealedSecret. After editing peers, bump - # wireguard.whyredfire.dev/config-revision on the Deployment to restart it. - [Interface] - Address = 10.13.13.1 - ListenPort = 51820 - MTU = 1280 - PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE - PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE - - [Peer] - # macbook - PublicKey = xuWFr3AHPeh/gRO5NiCGnwHcr0e/8InaroXcY8S5eC0= - AllowedIPs = 10.13.13.2/32 - - [Peer] - # phone - PublicKey = 8VBoG0Bpwv1UhAOethIgRJXmaNWPErSWkqcfdjGmhHs= - AllowedIPs = 10.13.13.3/32 -kind: ConfigMap -metadata: - name: wireguard - namespace: wireguard diff --git a/apps/wireguard/deployment.yaml b/apps/wireguard/deployment.yaml index 22d2b3b..aef3a4f 100644 --- a/apps/wireguard/deployment.yaml +++ b/apps/wireguard/deployment.yaml @@ -12,72 +12,67 @@ spec: type: Recreate template: metadata: - annotations: - wireguard.whyredfire.dev/config-revision: "1" labels: app.kubernetes.io/name: wireguard spec: containers: - - env: - - name: PUID - value: "1000" - - name: PGID - value: "1000" - - name: TZ - value: Etc/UTC - image: lscr.io/linuxserver/wireguard:1.0.20260223-r0-ls123 - livenessProbe: - exec: - command: - - wg - - show - - wg0 - initialDelaySeconds: 30 - periodSeconds: 30 - name: wireguard + - args: + - | + # The image points iptables at the legacy backend, which the + # node's kernel doesn't have. Use nftables, as the node does. + for b in iptables iptables-save iptables-restore ip6tables ip6tables-save ip6tables-restore; do + ln -sf xtables-nft-multi "/usr/sbin/$b" + done + exec /usr/bin/dumb-init node server/index.mjs + command: + - /bin/sh + - -c + env: + - name: DISABLE_IPV6 + value: "true" + - name: INIT_ENABLED + value: "true" + - name: INIT_ALLOWED_IPS + value: 192.168.2.0/24 + - name: INIT_DNS + value: 192.168.2.1 + image: ghcr.io/wg-easy/wg-easy:15.4.0 + name: wg-easy ports: - containerPort: 51820 name: wireguard protocol: UDP + - containerPort: 51821 + name: http securityContext: capabilities: add: - NET_ADMIN volumeMounts: - - mountPath: /config - name: config + - mountPath: /etc/wireguard + name: data initContainers: - args: - | - set -euo pipefail - umask 077 - mkdir -p /config/wg_confs - awk -v keyfile=/secret/private-key ' - { print } - /^\[Interface\]/ { getline key < keyfile; print "PrivateKey = " key } - ' /template/wg0.conf > /config/wg_confs/wg0.conf + # Rules in the pod's own network namespace, never the node's. + # VPN clients may reach the home LAN, but not this pod (the web + # UI), the pod network or the service network. The mangle table + # runs before wg-easy's own filter rules, so its per-client + # firewall can't open these up. + set -eu + iptables-nft -t mangle -A PREROUTING -i wg0 -m addrtype --dst-type LOCAL -j DROP + iptables-nft -t mangle -A PREROUTING -i wg0 -d 10.42.0.0/16 -j DROP + iptables-nft -t mangle -A PREROUTING -i wg0 -d 10.43.0.0/16 -j DROP command: - - /bin/bash + - /bin/sh - -c - image: lscr.io/linuxserver/wireguard:1.0.20260223-r0-ls123 - name: render-config - volumeMounts: - - mountPath: /config - name: config - - mountPath: /template - name: template - readOnly: true - - mountPath: /secret - name: secret - readOnly: true + image: ghcr.io/wg-easy/wg-easy:15.4.0 + name: firewall + securityContext: + capabilities: + add: + - NET_ADMIN volumes: - - emptyDir: - medium: Memory - name: config - - configMap: - name: wireguard - name: template - - name: secret - secret: - defaultMode: 256 - secretName: wireguard + - name: data + persistentVolumeClaim: + claimName: wireguard-data diff --git a/apps/wireguard/httproute.yaml b/apps/wireguard/httproute.yaml new file mode 100644 index 0000000..9c7a67d --- /dev/null +++ b/apps/wireguard/httproute.yaml @@ -0,0 +1,37 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + annotations: + argocd.argoproj.io/sync-wave: "2" + name: wireguard-admin + namespace: wireguard +spec: + hostnames: + - wg-admin.whyredfire.dev + parentRefs: + - kind: Gateway + name: traefik-gateway + namespace: traefik + sectionName: websecure + rules: + - backendRefs: + - name: authentik-server + namespace: authentik + port: 80 + matches: + - path: + type: PathPrefix + value: /outpost.goauthentik.io + - backendRefs: + - name: wireguard-admin + port: 51821 + filters: + - extensionRef: + group: traefik.io + kind: Middleware + name: authentik + type: ExtensionRef + matches: + - path: + type: PathPrefix + value: / diff --git a/apps/wireguard/kustomization.yaml b/apps/wireguard/kustomization.yaml index 9d38556..dbaae86 100644 --- a/apps/wireguard/kustomization.yaml +++ b/apps/wireguard/kustomization.yaml @@ -1,7 +1,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - sealed-secret.yaml - - configmap.yaml + - pvc.yaml - deployment.yaml - service.yaml + - service-admin.yaml + - middleware.yaml + - httproute.yaml diff --git a/apps/wireguard/middleware.yaml b/apps/wireguard/middleware.yaml new file mode 100644 index 0000000..9db9a90 --- /dev/null +++ b/apps/wireguard/middleware.yaml @@ -0,0 +1,17 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + name: authentik + namespace: wireguard +spec: + forwardAuth: + address: http://authentik-server.authentik.svc.cluster.local/outpost.goauthentik.io/auth/traefik + authResponseHeaders: + - X-authentik-username + - X-authentik-groups + - X-authentik-email + - X-authentik-name + - X-authentik-uid + trustForwardHeader: true diff --git a/apps/wireguard/pvc.yaml b/apps/wireguard/pvc.yaml new file mode 100644 index 0000000..b4a373a --- /dev/null +++ b/apps/wireguard/pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: wireguard-data + namespace: wireguard +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi diff --git a/apps/wireguard/sealed-secret.yaml b/apps/wireguard/sealed-secret.yaml deleted file mode 100644 index 0dd9d96..0000000 --- a/apps/wireguard/sealed-secret.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: bitnami.com/v1alpha1 -kind: SealedSecret -metadata: - name: wireguard - namespace: wireguard -spec: - encryptedData: - private-key: AgCyimGRHeoQAc4xVkr09bpRHYtZBG2Gjc1IkOM9D/p+f7n7/Va2hJixDL1ieVBXufEm485MYcLV74j1dG6q/Xyad+Y2VsAlWGIFlC+1rCO0V/dEKGOav8bxxhcMV56YUDgNR4ZtBOnN5aLgq2P/72CIbRpa9I4ofNUmoMV+Eh50hY9oVJgdmeb6Dx+03q5Zkf1LTJZMxnYi5r6bbzznT1bqQM9gZml4N2b3cBBVrtLIBlPFkHeyxhmS+0ZnQgXiu+oFEkAvXLKB44AmerJ6UFYjX5ly933UzOm2KsTR4pIGHRsj6vZvjCb6cJUxCAhFOn9Nq+B984TeNTomCglYVc3p5XRVAsSVL+MBT1EfNoz9qSec8sHiufnsgHG6cre6DDriv0HD+yfg8tCK7IX0HbETb2aQXgNNFvdnSL5S6rqbffctl/ivfj3RsZmJCcaxwrWR6khmn6nzOkqNeKSMjmpAxSTdAlve6EFFWqvFz4D0LpYpk/73kyaWvczuzV//cGlYBwVOBA9CPCyAexdrQ78wbzn0zaCdo7yKqTQtgZestXnyCICUyrXm+1WQzd8W/lGHtr/hV7D3H//RQAGxnlWERvWCZlYIF6xkz9eApvWl72gE4cwzcgme+1+EkyPgSmLgtyh78B/kucEZC2Qy2chGJEAlwJln02HxbJ0RX8Bo1ko+kxCHzZl12zA02A1yzQGzEt7Oya9K/wrhWX8plYFK4Zq811yHmVqJEeu5bseCSi+O0tHmnl5vafRFyjw= - template: - metadata: - name: wireguard - namespace: wireguard diff --git a/apps/wireguard/service-admin.yaml b/apps/wireguard/service-admin.yaml new file mode 100644 index 0000000..cbe8c0f --- /dev/null +++ b/apps/wireguard/service-admin.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: wireguard-admin + namespace: wireguard +spec: + ports: + - name: http + port: 51821 + targetPort: http + selector: + app.kubernetes.io/name: wireguard