Initial commit

This commit is contained in:
Scooby Husky
2026-03-09 20:21:35 -05:00
commit aacb8eebbe
314 changed files with 21766 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-netbird
namespace: netbird
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16
instances: 2
resources:
requests:
memory: "256Mi"
cpu: "25m"
limits:
memory: "1Gi"
affinity:
topologyKey: kubernetes.io/hostname
storage:
size: 10Gi
storageClass: rook-ceph-block
primaryUpdateStrategy: unsupervised
postgresql:
parameters:
max_connections: "100"
shared_buffers: "256MB"
effective_cache_size: "768MB"
bootstrap:
initdb:
database: netbird
owner: app
monitoring:
enablePodMonitor: true
@@ -0,0 +1,104 @@
# Netbird API Exporter - Exposes peer status, groups, users as Prometheus metrics
# Uses the Netbird management API to collect data
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: netbird-api-exporter
namespace: netbird
labels:
app.kubernetes.io/name: netbird-api-exporter
app.kubernetes.io/component: exporter
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: netbird-api-exporter
template:
metadata:
labels:
app.kubernetes.io/name: netbird-api-exporter
app.kubernetes.io/component: exporter
spec:
securityContext:
runAsUser: 10000
runAsGroup: 10000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: exporter
image: ghcr.io/matanbaruch/netbird-api-exporter:v0.1.46
ports:
- containerPort: 8080
name: metrics
protocol: TCP
env:
- name: NETBIRD_API_URL
value: "http://netbird-management.netbird.svc.cluster.local:80"
- name: NETBIRD_API_TOKEN
valueFrom:
secretKeyRef:
name: netbird-api-token-secret
key: api-token
resources:
limits:
memory: 128Mi
requests:
cpu: 10m
memory: 32Mi
securityContext:
runAsUser: 10000
runAsGroup: 10000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
livenessProbe:
httpGet:
path: /metrics
port: metrics
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /metrics
port: metrics
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: netbird-api-exporter
namespace: netbird
labels:
app.kubernetes.io/name: netbird-api-exporter
app.kubernetes.io/component: exporter
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: metrics
protocol: TCP
name: metrics
selector:
app.kubernetes.io/name: netbird-api-exporter
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: netbird-api-exporter
namespace: netbird
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/name: netbird-api-exporter
endpoints:
- port: metrics
path: /metrics
interval: 30s
@@ -0,0 +1,21 @@
# ExternalSecret for Netbird API token (used by API exporter)
# Token is stored in Vault by scripts/setup-netbird-api-token.sh
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: netbird-api-token
namespace: netbird
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: netbird-api-token-secret
creationPolicy: Owner
data:
- secretKey: api-token
remoteRef:
key: netbird-api-token
property: api-token
@@ -0,0 +1,67 @@
---
# ExternalSecret to pull router setup key from Vault
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: netbird-router-setup-key
namespace: netbird
spec:
refreshInterval: "0"
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: netbird-router-setup-key
data:
- secretKey: setup-key
remoteRef:
key: netbird-router-setup-key
property: setup-key
---
# Shared cluster-wide NetBird router deployment (2 replicas)
# Routes traffic for all exposed K8s services through NetBird VPN
apiVersion: apps/v1
kind: Deployment
metadata:
name: netbird-cluster-router
namespace: netbird
labels:
app.kubernetes.io/name: netbird-cluster-router
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: netbird-cluster-router
template:
metadata:
labels:
app.kubernetes.io/name: netbird-cluster-router
annotations:
# Router pods must opt out of ambient mesh — ztunnel iptables rules
# interfere with WireGuard packet forwarding (DNS and TCP).
# AuthorizationPolicies use ipBlocks for pod CIDR instead.
ambient.istio.io/redirection: disabled
spec:
containers:
- name: netbird
image: netbirdio/netbird:0.66.0
imagePullPolicy: IfNotPresent
env:
- name: NB_SETUP_KEY
valueFrom:
secretKeyRef:
name: netbird-router-setup-key
key: setup-key
- name: NB_MANAGEMENT_URL
value: "https://netbird.kube.huskypup.net"
securityContext:
capabilities:
add:
- NET_ADMIN
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
memory: 128Mi
terminationGracePeriodSeconds: 30
@@ -0,0 +1,31 @@
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: netbird-oauth
namespace: netbird
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: netbird-oauth-secret
creationPolicy: Owner
data:
- secretKey: client-id
remoteRef:
key: netbird-oauth
property: client-id
- secretKey: client-secret
remoteRef:
key: netbird-oauth
property: client-secret
- secretKey: service-username
remoteRef:
key: netbird-oauth
property: service-username
- secretKey: service-password
remoteRef:
key: netbird-oauth
property: service-password
@@ -0,0 +1,59 @@
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: netbird
namespace: netbird
spec:
hosts:
- netbird.kube.huskypup.net
gateways:
- istio-system/edge
http:
# Management REST API
- match:
- uri:
prefix: /api
route:
- destination:
host: netbird-management.netbird.svc.cluster.local
port:
number: 80
# Management gRPC (listens on port 33073, separate from REST API on 80)
- match:
- uri:
prefix: /management.ManagementService/
route:
- destination:
host: netbird-management-grpc.netbird.svc.cluster.local
port:
number: 33073
# Signal gRPC
- match:
- uri:
prefix: /signalexchange.SignalExchange/
route:
- destination:
host: netbird-signal.netbird.svc.cluster.local
port:
number: 80
# Relay WebSocket
- match:
- uri:
prefix: /relay
route:
- destination:
host: netbird-relay.netbird.svc.cluster.local
port:
number: 33080
# Dashboard (all other paths go to dashboard nginx)
# No URI rewrite — the static export has .html and .txt files for every route.
# nginx try_files handles SPA fallback to /index.html.
- route:
- destination:
host: netbird-dashboard.netbird.svc.cluster.local
port:
number: 80
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: netbird-management-grpc
namespace: netbird
labels:
app.kubernetes.io/name: netbird-management
app.kubernetes.io/instance: netbird
spec:
type: ClusterIP
ports:
- name: grpc
port: 33073
targetPort: 33073
protocol: TCP
selector:
app.kubernetes.io/instance: netbird
app.kubernetes.io/name: netbird-management
@@ -0,0 +1,25 @@
---
# Override OidcTrustedDomains.js template to include Authentik authority domain
# The default template only includes $NETBIRD_MGMT_API_ENDPOINT, but @axa-fr/react-oidc
# needs the OIDC authority domain (auth.kube.huskypup.net) in trusted domains to allow
# discovery, token, and userinfo fetches.
apiVersion: v1
kind: ConfigMap
metadata:
name: netbird-oidc-trusted-domains
namespace: netbird
data:
OidcTrustedDomains.js.tmpl: |
const trustedDomains = {
default:["$NETBIRD_MGMT_API_ENDPOINT", "$AUTH_AUTHORITY", "https://auth.kube.huskypup.net"],
auth0:[]
};
OidcServiceWorker.js: |
// Minimal no-op service worker for @axa-fr/react-oidc
// Prevents 404 which can hang the OIDC initialization
self.addEventListener('install', function(event) {
self.skipWaiting();
});
self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim());
});
@@ -0,0 +1,51 @@
apiVersion: batch/v1
kind: Job
metadata:
name: netbird-presync
namespace: netbird
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
backoffLimit: 3
template:
spec:
serviceAccountName: argocd-hook-sa
containers:
- name: presync
image: bitnami/kubectl:1.31
command:
- /bin/bash
- -c
- |
set -euo pipefail
echo "=== Netbird PreSync ==="
# Set PodSecurity to privileged (cluster routers need NET_ADMIN)
kubectl label namespace netbird pod-security.kubernetes.io/enforce=privileged --overwrite 2>/dev/null || true
# Wait for CNPG cluster
echo "Waiting for Netbird PostgreSQL cluster..."
for i in $(seq 1 60); do
PHASE=$(kubectl -n netbird get cluster pg-netbird -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
if [ "$PHASE" = "Cluster in healthy state" ] || [ "$PHASE" = "Healthy" ]; then
echo " Netbird PostgreSQL cluster ready"
break
fi
echo " waiting for pg-netbird... (attempt $i/60, phase=$PHASE)"
sleep 5
done
# Wait for OAuth secret
echo "Waiting for netbird-oauth-secret..."
for i in $(seq 1 30); do
if kubectl -n netbird get secret netbird-oauth-secret >/dev/null 2>&1; then
echo " Netbird OAuth secret synced"
break
fi
echo " waiting for ExternalSecret sync... (attempt $i/30)"
sleep 2
done
echo "=== Netbird PreSync Complete ==="
restartPolicy: Never
@@ -0,0 +1,54 @@
# PodMonitors for Netbird native Prometheus metrics
# Management, Signal, and Relay servers expose metrics on port 9090
# when NB_METRICS_PORT is set
---
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: netbird-management
namespace: netbird
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/component: management
app.kubernetes.io/name: netbird
podMetricsEndpoints:
- port: "9090"
path: /metrics
interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: netbird-signal
namespace: netbird
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/component: signal
app.kubernetes.io/name: netbird
podMetricsEndpoints:
- port: "9090"
path: /metrics
interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: netbird-relay
namespace: netbird
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/component: relay
app.kubernetes.io/name: netbird
podMetricsEndpoints:
- port: "9090"
path: /metrics
interval: 30s
+272
View File
@@ -0,0 +1,272 @@
# Netbird Self-Hosted VPN - Zero Trust Network Access
# Full deployment: management, signal, relay, dashboard
# Chart: netbird/netbird (https://netbirdio.github.io/helms)
# Ensure service names are netbird-{component} (not netbird-netbird-{component})
fullnameOverride: netbird
management:
enabled: true
securityContext:
capabilities:
drop:
- ALL
resources:
limits:
memory: 256Mi
requests:
cpu: 25m
memory: 128Mi
persistentVolume:
enabled: true
size: 100Mi
storageClass: rook-ceph-block
# Management server config (management.json)
# Uses Authentik as external OIDC provider for SSO
image:
tag: 0.66.0
configmap: |-
{
"Stuns": [
{
"Proto": "udp",
"URI": "stun:stun.l.google.com:19302"
}
],
"Relay": {
"Addresses": ["rels://netbird.kube.huskypup.net:443"],
"CredentialsTTL": "24h",
"Secret": "zATjQ+YwNLvSBPD04R2+z7WzUhrwG1rkH3b/4Xsjvz0="
},
"Signal": {
"Proto": "https",
"URI": "netbird.kube.huskypup.net:443",
"AuthenticationEnabled": false
},
"Datadir": "/var/lib/netbird/",
"HttpConfig": {
"AuthIssuer": "https://auth.kube.huskypup.net/application/o/netbird/",
"AuthAudience": "{{.NETBIRD_AUTH_AUDIENCE}}",
"AuthKeysLocation": "https://auth.kube.huskypup.net/application/o/netbird/jwks/",
"OIDCConfigEndpoint": "https://auth.kube.huskypup.net/application/o/netbird/.well-known/openid-configuration",
"IdpSignKeyRefreshEnabled": false,
"AuthUserIDClaim": "sub",
"GroupsClaimName": "groups"
},
"IdpManagerConfig": {
"ManagerType": "authentik",
"ClientConfig": {
"Issuer": "https://auth.kube.huskypup.net/application/o/netbird/",
"TokenEndpoint": "https://auth.kube.huskypup.net/application/o/token/",
"ClientID": "{{.NETBIRD_IDP_MGMT_CLIENT_ID}}",
"GrantType": "client_credentials"
},
"ExtraConfig": {
"Username": "{{.NETBIRD_IDP_MANAGER_EXTRA_CONFIG_USERNAME}}",
"Password": "{{.NETBIRD_IDP_MANAGER_EXTRA_CONFIG_PASSWORD}}"
}
},
"DeviceAuthorizationFlow": {
"Provider": "hosted",
"ProviderConfig": {
"Audience": "{{.NETBIRD_AUTH_AUDIENCE}}",
"ClientID": "{{.NETBIRD_AUTH_AUDIENCE}}",
"ClientSecret": "",
"Domain": "auth.kube.huskypup.net",
"TokenEndpoint": "https://auth.kube.huskypup.net/application/o/token/",
"DeviceAuthEndpoint": "https://auth.kube.huskypup.net/application/o/device/",
"AuthorizationEndpoint": "https://auth.kube.huskypup.net/application/o/authorize/",
"Scope": "openid profile email offline_access goauthentik.io/api",
"UseIDToken": false,
"RedirectURLs": [
"http://localhost:53000",
"netbird://auth"
]
}
},
"PKCEAuthorizationFlow": {
"ProviderConfig": {
"Audience": "{{.NETBIRD_AUTH_AUDIENCE}}",
"ClientID": "{{.NETBIRD_AUTH_AUDIENCE}}",
"ClientSecret": "",
"TokenEndpoint": "https://auth.kube.huskypup.net/application/o/token/",
"DeviceAuthEndpoint": "https://auth.kube.huskypup.net/application/o/device/",
"AuthorizationEndpoint": "https://auth.kube.huskypup.net/application/o/authorize/",
"Scope": "openid profile email offline_access goauthentik.io/api",
"UseIDToken": false,
"RedirectURLs": [
"http://localhost:53000"
]
}
},
"StoreConfig": {
"Engine": "postgres"
},
"DataStoreEncryptionKey": "X3qwIBAfV5UjYCbPmat0jLSLYAnCOMtLdXdgV34UWMM=",
"EncryptionKey": "X3qwIBAfV5UjYCbPmat0jLSLYAnCOMtLdXdgV34UWMM="
}
# Simple key-value env vars (override management.json values)
env:
NETBIRD_STORE_ENGINE: postgres
NETBIRD_DOMAIN: netbird.kube.huskypup.net
NETBIRD_MGMT_API_ENDPOINT: "https://netbird.kube.huskypup.net:443"
NETBIRD_ENCRYPTION_KEY: "X3qwIBAfV5UjYCbPmat0jLSLYAnCOMtLdXdgV34UWMM="
NB_METRICS_PORT: "9090"
# OIDC scopes and redirect URIs
NETBIRD_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access goauthentik.io/api"
NETBIRD_AUTH_REDIRECT_URI: "/auth"
NETBIRD_AUTH_SILENT_REDIRECT_URI: "/silent-auth"
NETBIRD_AUTH_PKCE_DISABLE_PROMPT_LOGIN: "true"
# Complex env vars (valueFrom references)
envRaw:
- name: NETBIRD_STORE_ENGINE_POSTGRES_DSN
valueFrom:
secretKeyRef:
name: pg-netbird-app
key: uri
- name: NETBIRD_AUTH_AUDIENCE
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: client-id
- name: NETBIRD_IDP_MGMT_CLIENT_ID
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: client-id
- name: NETBIRD_IDP_MANAGER_EXTRA_CONFIG_USERNAME
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: service-username
- name: NETBIRD_IDP_MANAGER_EXTRA_CONFIG_PASSWORD
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: service-password
ingress:
enabled: false
signal:
enabled: true
securityContext:
capabilities:
drop:
- ALL
resources:
limits:
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi
env:
NB_METRICS_PORT: "9090"
ingress:
enabled: false
dashboard:
enabled: true
image:
tag: v2.33.0
resources:
limits:
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
# Fix envsubst bug: USE_AUTH0="false" becomes JS string "false" (truthy).
# The template wraps $$USE_AUTH0 in quotes, so "false" !== false in JS.
# This causes @axa-fr/react-oidc to use Auth0-specific endpoint paths
# instead of fetching from .well-known/openid-configuration.
# postStart waits for init_react_envs.sh to finish, then patches the JS.
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- |
sleep 3
# Fix auth0Auth: envsubst wraps "false" in quotes → JS truthy string.
# Must be boolean false for @axa-fr/react-oidc to use OIDC discovery.
find /usr/share/nginx/html -name '*.js' -exec sed -i 's/auth0Auth:"false"/auth0Auth:false/g' {} +
# Fix nginx try_files: SPA fallback to /index.html instead of =404.
# Next.js App Router RSC fetches .txt files; the default =404 breaks navigation.
sed -i 's|try_files $uri $uri.html $uri/ =404|try_files $uri $uri.html $uri/ /index.html|' /etc/nginx/http.d/default.conf
nginx -s reload
# Simple key-value env vars
env:
USE_AUTH0: "false"
NETBIRD_MGMT_API_ENDPOINT: "https://netbird.kube.huskypup.net:443"
NETBIRD_MGMT_GRPC_API_ENDPOINT: "https://netbird.kube.huskypup.net:443"
NETBIRD_TOKEN_SOURCE: "accessToken"
# Authentik OIDC (trailing slash must match issuer in OIDC discovery)
AUTH_AUTHORITY: "https://auth.kube.huskypup.net/application/o/netbird/"
AUTH_SUPPORTED_SCOPES: "openid profile email offline_access goauthentik.io/api"
AUTH_REDIRECT_URI: "/auth"
AUTH_SILENT_REDIRECT_URI: "/silent-auth"
# Complex env vars (valueFrom references)
envRaw:
- name: AUTH_CLIENT_ID
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: client-id
- name: AUTH_AUDIENCE
valueFrom:
secretKeyRef:
name: netbird-oauth-secret
key: client-id
# Mount corrected OidcTrustedDomains.js.tmpl that includes AUTH_AUTHORITY
# Default template only trusts NETBIRD_MGMT_API_ENDPOINT; @axa-fr/react-oidc
# needs the OIDC authority domain to allow discovery/token/userinfo fetches
volumes:
- name: oidc-trusted-domains
configMap:
name: netbird-oidc-trusted-domains
volumeMounts:
- name: oidc-trusted-domains
mountPath: /usr/share/nginx/html/OidcTrustedDomains.js.tmpl
subPath: OidcTrustedDomains.js.tmpl
ingress:
enabled: false
# Relay (TURN) for NAT traversal
relay:
enabled: true
securityContext:
capabilities:
drop:
- ALL
image:
tag: 0.66.0
resources:
limits:
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
env:
NB_LISTEN_ADDRESS: ":33080"
NB_EXPOSED_ADDRESS: "rels://netbird.kube.huskypup.net:443"
NB_AUTH_SECRET: "zATjQ+YwNLvSBPD04R2+z7WzUhrwG1rkH3b/4Xsjvz0="
NB_LOG_LEVEL: info
NB_METRICS_PORT: "9090"