feat: netbird

This commit is contained in:
2026-02-25 12:37:18 +01:00
parent f4321fced4
commit 719f19a4d1
13 changed files with 369 additions and 2 deletions

View File

@@ -28,6 +28,24 @@
n8n-env = {
file = ../../secrets/n8n-env.age;
};
netbird-auth-secret = {
file = ../../secrets/netbird-auth-secret.age;
};
netbird-db-password = {
file = ../../secrets/netbird-db-password.age;
};
netbird-encryption-key = {
file = ../../secrets/netbird-encryption-key.age;
};
netbird-dashboard-env = {
file = ../../secrets/netbird-dashboard-env.age;
};
netbird-server-env = {
file = ../../secrets/netbird-server-env.age;
};
netbird-proxy-env = {
file = ../../secrets/netbird-proxy-env.age;
};
outline-env = {
file = ../../secrets/outline-env.age;
owner = "outline";

View File

@@ -5,6 +5,7 @@
./librechat.nix
./litellm.nix
./librechat-dev.nix
./netbird.nix
./portainer.nix
./zammad-hr.nix
];

View File

@@ -13,7 +13,7 @@
in {
virtualisation.oci-containers = {
containers.meilisearch = {
image = "getmeili/meilisearch:v1.12.3";
image = "getmeili/meilisearch:v1.35.1";
autoStart = true;
volumes = ["librechat_meili:/meili_data"];
environment = {
@@ -39,7 +39,7 @@ in {
};
containers.mongodb = {
image = "mongo:7";
image = "mongo:8.0.17";
autoStart = true;
volumes = [
"librechat_mongo:/data/db"

View File

@@ -0,0 +1,238 @@
{
config,
lib,
pkgs,
...
}: let
serviceName = "netbird";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
domain = "v.az-gruppe.com";
proxyDomain = "p.az-gruppe.com";
ipBase = "10.89.0";
ipOffset = 50;
# Database configuration
dbName = "netbird";
dbUser = "netbird";
dbHost = "${ipBase}.1";
# NetBird config als Nix attribute set
netbirdConfig = {
server = {
listenAddress = ":80";
exposedAddress = "https://${domain}:443";
stunPorts = [3478];
metricsPort = 9090;
healthcheckAddress = ":9000";
logLevel = "info";
logFile = "console";
dataDir = "/var/lib/netbird";
auth = {
issuer = "https://${domain}/oauth2";
localAuthDisabled = true;
signKeyRefreshEnabled = true;
dashboardRedirectURIs = [
"https://${domain}/nb-auth"
"https://${domain}/nb-silent-auth"
];
cliRedirectURIs = ["http://localhost:53000/"];
};
reverseProxy = {
trustedHTTPProxies = ["${ipBase}.1/32"];
};
# Proxy Feature
proxy = {
enabled = true;
domain = proxyDomain;
};
store = {
engine = "postgres";
postgres = {
host = dbHost;
port = 5432;
database = dbName;
username = dbUser;
};
};
};
};
# YAML generieren
yamlFormat = pkgs.formats.yaml {};
configYamlBase = yamlFormat.generate "netbird-config-base.yaml" netbirdConfig;
# Script das Secrets zur Runtime injiziert
configGenScript = pkgs.writeShellScript "netbird-gen-config" ''
set -euo pipefail
AUTH_SECRET=$(cat "$1")
DB_PASSWORD=$(cat "$2")
ENCRYPTION_KEY=$(cat "$3")
${pkgs.yq-go}/bin/yq eval "
.server.authSecret = \"$AUTH_SECRET\" |
.server.store.encryptionKey = \"$ENCRYPTION_KEY\" |
.server.store.postgres.password = \"$DB_PASSWORD\"
" ${configYamlBase}
'';
in {
age.secrets."${serviceName}-auth-secret".file = ../../../../secrets/${serviceName}-auth-secret.age;
age.secrets."${serviceName}-db-password".file = ../../../../secrets/${serviceName}-db-password.age;
age.secrets."${serviceName}-encryption-key".file = ../../../../secrets/${serviceName}-encryption-key.age;
age.secrets."${serviceName}-dashboard-env".file = ../../../../secrets/${serviceName}-dashboard-env.age;
age.secrets."${serviceName}-server-env".file = ../../../../secrets/${serviceName}-server-env.age;
age.secrets."${serviceName}-proxy-env".file = ../../../../secrets/${serviceName}-proxy-env.age;
# Systemd oneshot Service der die Config generiert
systemd.services."${serviceName}-config" = {
description = "Generate NetBird config with secrets";
wantedBy = ["multi-user.target"];
before = ["podman-${serviceName}-server.service"];
requiredBy = ["podman-${serviceName}-server.service"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "netbird-write-config" ''
mkdir -p /var/lib/${serviceName}
${configGenScript} \
${config.age.secrets."${serviceName}-auth-secret".path} \
${config.age.secrets."${serviceName}-db-password".path} \
${config.age.secrets."${serviceName}-encryption-key".path} \
> /var/lib/${serviceName}/config.yaml
chmod 600 /var/lib/${serviceName}/config.yaml
'';
};
};
virtualisation.oci-containers.containers = {
"${serviceName}-dashboard" = {
image = "netbirdio/dashboard:latest";
autoStart = true;
environmentFiles = [config.age.secrets."${serviceName}-dashboard-env".path];
extraOptions = [
"--ip=${ipBase}.${toString ipOffset}"
"--network=web"
];
};
"${serviceName}-server" = {
image = "netbirdio/netbird-server:latest";
autoStart = true;
ports = ["3478:3478/udp"];
environmentFiles = [config.age.secrets."${serviceName}-server-env".path];
volumes = [
"${serviceName}_data:/var/lib/netbird"
"/var/lib/${serviceName}/config.yaml:/etc/netbird/config.yaml:ro"
];
cmd = ["--config" "/etc/netbird/config.yaml"];
extraOptions = [
"--ip=${ipBase}.${toString (ipOffset + 1)}"
"--network=web"
];
};
"${serviceName}-proxy" = {
image = "netbirdio/reverse-proxy:latest";
autoStart = true;
ports = ["51820:51820/udp"];
volumes = [
"${serviceName}_proxy_certs:/certs"
];
environmentFiles = [config.age.secrets."${serviceName}-proxy-env".path];
cmd = [
"--domain=p.az-gruppe.com"
"--mgmt=https://${domain}:443"
"--addr=:8443"
"--cert-dir=/certs"
"--acme-certs"
"--trusted-proxies=${ipBase}.1/32"
];
dependsOn = ["${serviceName}-server"];
extraOptions = [
"--ip=${ipBase}.${toString (ipOffset + 2)}"
"--network=web"
];
};
};
services.traefik.dynamicConfigOptions = {
# HTTP Services und Routers
http = {
services = {
"${serviceName}-dashboard".loadBalancer.servers = [
{url = "http://${ipBase}.${toString ipOffset}:80/";}
];
"${serviceName}-server".loadBalancer.servers = [
{url = "http://${ipBase}.${toString (ipOffset + 1)}:80/";}
];
"${serviceName}-server-h2c".loadBalancer.servers = [
{url = "h2c://${ipBase}.${toString (ipOffset + 1)}:80";}
];
};
routers = {
# gRPC (Signal + Management)
"${serviceName}-grpc" = {
rule = "Host(`${domain}`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))";
entrypoints = "websecure";
tls.certResolver = "ionos";
service = "${serviceName}-server-h2c";
priority = 100;
};
# Backend (relay, WebSocket, API, OAuth2)
"${serviceName}-backend" = {
rule = "Host(`${domain}`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`))";
entrypoints = "websecure";
tls.certResolver = "ionos";
service = "${serviceName}-server";
priority = 100;
};
# Dashboard (catch-all, niedrigste Priorität)
"${serviceName}-dashboard" = {
rule = "Host(`${domain}`)";
entrypoints = "websecure";
tls.certResolver = "ionos";
service = "${serviceName}-dashboard";
priority = 1;
};
};
};
# TCP für Proxy TLS Passthrough
tcp = {
services."${serviceName}-proxy-tls".loadBalancer.servers = [
{address = "${ipBase}.${toString (ipOffset + 2)}:8443";}
];
routers."${serviceName}-proxy-passthrough" = {
entryPoints = ["websecure"];
rule = "HostSNI(`*`)";
service = "${serviceName}-proxy-tls";
priority = 1;
tls.passthrough = true;
};
};
# ServersTransport für Proxy Protocol v2 (optional)
serversTransports."pp-v2" = {
proxyProtocol.version = 2;
};
};
networking.firewall.allowedUDPPorts = [
3478 # STUN
51820 # WireGuard für Proxy
];
}

View File

@@ -117,6 +117,7 @@
host zammad_hr zammad_hr 10.89.0.0/24 scram-sha-256
host postgres zammad_hr 10.89.0.0/24 scram-sha-256
host litellm litellm 10.89.0.0/24 scram-sha-256
host netbird netbird 10.89.0.0/24 scram-sha-256
# Deny all other connections
local all all reject

View File

@@ -37,4 +37,6 @@
clean.extraArgs = "--keep-since 4d --keep 3";
flake = "/home/m3tam3re/p/nixos/nixos-config";
};
services.netbird.enable = true;
environment.systemPackages = [pkgs.netbird-ui];
}

View File

@@ -24,6 +24,12 @@ in {
"secrets/metabase-env.age".publicKeys = systems ++ users;
"secrets/n8n-env.age".publicKeys = systems ++ users;
"secrets/n8n-db.age".publicKeys = systems ++ users;
"secrets/netbird-auth-secret.age".publicKeys = systems ++ users;
"secrets/netbird-db-password.age".publicKeys = systems ++ users;
"secrets/netbird-encryption-key.age".publicKeys = systems ++ users;
"secrets/netbird-dashboard-env.age".publicKeys = systems ++ users;
"secrets/netbird-server-env.age".publicKeys = systems ++ users;
"secrets/netbird-proxy-env.age".publicKeys = systems ++ users;
"secrets/outline-env.age".publicKeys = systems ++ users;
"secrets/pgadmin-pw.age".publicKeys = systems ++ users;
"secrets/vaultwarden-env.age".publicKeys = systems ++ users;

View File

@@ -0,0 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyBBWFRu
VnVLVS9TUERVakRiRWpsZEdycGFjVm54cjE5S2Vsb3MwMUg1cnpBClBRVlBNNzJm
MVZqSitVM1lJRDJSZzQvSnFKai96M3BNclROaDBYeGZTa2sKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIHhLYnNjT0NpOVZjN3lkWHVlMHFhNzZtZHF1MktFZ3JIM3FBc2t6
MG9YbEUKTk5OTnRnbFd1ZkJyRzEwTlFSRTZ4cGpyVGxGTjZHbWwvYU1mblRqcVFT
YwotPiBzc2gtZWQyNTUxOSBDU015aGcgbUtYWFJyZnA4MXVMWm9BVkt4a0syMnEr
RkhrWEJORXFKSThjM01UcEREcwpVakd4bWFsVC83dk1nSmlyTnp4L2hKRzg0bCts
bDc4TCtqOEVZcXhtdXE0Ci0+IDlxUCotZ3JlYXNlID5jYDErUzYgfiBLT2gtfHQK
Z0pyOVhzRHUrUkhadTVFWXFQOHRubnZ5ZUhoQlpyS0cvWjFvaDhVSkNiRmNDQ2Ns
VzBvSTA3cVlnTEthcStkTApBVHN1OU1weklsTVN2YjRuL2dEVVpVVVh1VkhwM25H
RFVLVFphd1lZSERtWXdCcXk0Qm5zOEhpdE9OakcKLS0tIEcrQnBlOXBDUmQ4cHN6
THNFSUdnVU9FWVQrYytzVXc4bEpBR0VjZGdZRDAKkiXpoQm8u1EINUnqsbjsqrns
xtW5vTUwq/Akr1Af7p3jjy559kDX2x/9vxONB4wErSecs8SYEpf7RIpW6BPPap1R
IxgKkJf66xU6otE=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,24 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyBiN2th
NHBrckc1dVRuQ09jSjlISTJNZjhpVTRLZ2ZCWE9HZGV4Y1JsbkRvCk4yRmhKNTA3
bUNvVWw1bEVzd2ZaRys4YmVTSFZlRGNxbzVlMFJQSDFwY0kKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIGx6QmtoOHVJbGF0MnF2UTZBNFRvc0ZJREQycmZVUVExTXd4Ujh6
VHk1ekkKQXFDbGVWdjIyYmtHVnNuQ0NEdE5pNXFCZTRNbnFJT0hNL0tDNkJCb1dl
OAotPiBzc2gtZWQyNTUxOSBDU015aGcgeHRHc3pzallXZWNEVzlpQXdwaWJFdzBu
ejk3YThKOUtkNVZNSm9DTkVHUQpCbU5Uc284ejNnYUw1eDJFczBUdVNEZm8yT2FB
Q2FNdWJBSENaWEd4K2VFCi0+IG5qMC8/LWdyZWFzZQorby9YaE5Pb0R5L0ZpMm1T
eGVQSnNHQk1hNzRTeHRKajFtYW9SYnhJRms0RW1aSTNjeFBLanFVcmFPQkMwSUh2
Ckl0THZKam9vTm1Vd0N4QStjdXRLTDlPaHNPRFYxZzRYdjBzCi0tLSBBWmhqZ2ZU
M0NwN2NlVzFJYlp2ekNFUVduZGFreGFyMVpnNlE0d0xuVlFVCvxgcB2GrVv2RFmk
qKsRR1oa54CDDm9137v95ADygoI8fgHp0G288mqZWfkbzyKM2ZJ4GzPSQDUS29zx
5kfkZ/rdVwFvjZ09TjtvlA/LRHc/LEh1AAF3CPQuisszY8P0WXJAftHJOX9xtu17
Ett/GdqIfo0VIairWDWEa63TUkOggz17Tl2/NQsDfYFFxC//m6CNzMwvTTgRHBCn
QRoS+jvHfJ9kz4oKyk9SWezWrMA6KBCTIDcXyY5uSq9Otk2MOUo6rnTMQjweUjft
Xc/n+YJDcKlU0qF1HXs4Peqy5HkwKOBhYvzggqnKGfCANv6Dv3H9O9SKfENPAUA8
bERYjNBvUx/blWcjVYhDxKNhAtKKusGEIKUyS+JcFJngVq+YogGtIwtZpC4Vj1jr
iiMdmgCGV6w07lhJCl79v90S98rBxXDn81Wn6wXyDkOJZaHuXg07MC/BODOrz9fI
gzffRc7CtJ4UwtG6aA27EQZm+XhCC483wc+XTj7INgT446KgEAo4GbSc5SMnWl09
FIuQf/TKLPw9G4FRQfSfg6MugkdbLY11xNhxaz35iy6zVkZsDyQqYtBF0Votjv1I
ELwypqLVE8DA32nrKwjIbwBRTb1f4AffrnHtFuRGp/Agmzl2hy2cm5oGXNj3lrn3
Lv4X5ezHkCvv9kmtjoY=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,13 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyA2TDFu
ZjZSYnlTTzJrcHZnUmh2Zjd0VVpCaGZwdEcxd3BrWG9aUzEyb3kwCnlWU3VRODhL
TUgxUHJCaGZidjFFNlJGZW5jQ2JUM2o5M1V2djBBQWxvSDgKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIHRmdnlhcS9xWitYclZ3RFk2ckNuZmlJL1UrN2VreVNEZU9Nck8z
VFlEMTQKN2k3b2ROaEUwUkM5MVF4OUJGeEthSFIzMUhURGN4V0RQYklTU3h0cWVJ
OAotPiBzc2gtZWQyNTUxOSBDU015aGcgaEgvVXFENkc5a2doL01ZNFpDTFZTdnRP
R3NCZ2VZNFZhaU1ZYmx5VjdCSQpFM3RDbUd0Q21FQU45S3A3TzRXWWFDZGVRbGxE
dTZqRkc2RFRQRjdnSFZNCi0+IHdYK1VfLWdyZWFzZSBHIiB4XHhfVDdEPiBabVIK
aDk2Vmw0WFlpUzM0ZjZDOWdQZFFGcnFnaWxOd20rZ1FhYnMKLS0tIDVWalUzMnpI
NW5SQlhDYUdDKzdzcHhlSlkxbStnTlRaeDh2anlZaXJLQ0UKjkkEkuFG/p2ut2dU
LYDkEauu1BkM4lGSTxCx6d84t93YN9fPi47ea4gqQ5wt4Hc=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,16 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyBMbE82
V3RjazZESmw3WTJxMmpTT0Mxc2MyNjZCb1JsWVBPb3NDcnVsaVM4ClhZR1FVaVBV
YUVNS1I2ZnA3ZGxDNnk2bkM5T3N1bGVIYlhUMm1kYmFubVkKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIGhFbFpQdzBVVVljWWJjSFhMMjgxU2Vza1lvaGRzOGhTV0t3QmlL
alVDeWcKdkh4VHZ2MDVvOFBDeUsyT3hmNW1PQ3ZSekFRMTZYc2pvUW5BcTZDbGs5
SQotPiBzc2gtZWQyNTUxOSBDU015aGcgSHV0U0RwMnVDQmM2Z3R3bWdocUU2Nzhq
VmEyMVhZeWxHdU9GMzFYRXdHUQpWR3dZOUgwTmVCRkdZUWdsdmp2Nko4Y01WTVZM
WEo2VFNSYkk2c3k1eGRJCi0+IF1EaS1ncmVhc2UgY1peMSMgISBXOi9yWVkKdDJs
bVM5cVBJSk9Tb1pOWFNJRkNqaCtLR2ZjSDJBVFY1WlFBbmJvUW85VTVpa1ExdC92
KzVoSnMveHlKVG5nNApyci9tQ29mM0dxdmYyWG1yU01YMkhpRFNIYkFUNU1ZVk5D
emRRQlR3Q0xVeURWWUVoZmliOEF6T0gxSXJUNFkKLS0tIHA2cWZvckZXdlozM045
b2RDVjFJdVdiM2trWThqblJVNHVBKys3bUxPdVEKuNuRpP7VTZf42Iig7jCXXOZl
Sm/leNBVC0MPJNXG9tHCtGXqHveKCsxm7eQDAi4/wfpW2yFua+twhcaoBlchhw5i
dNr0LpCXHWzubJ8=
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyB5Vm83
Q25wZHRIUDdzYzlZV2NmMGxtMG5OQ1FHeDk2d0FCa2kvbkwzUkRBCklTa1dvVjdj
djFJS2IyaXZ2bkNJQ1N2V1owUFNQZzhkMFpGUnlXTElHeWsKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIGlTWWRSbEdvdzBlaFJoTldWSlBRdkxlYytjaWhpRWoyWDY3a1lY
TjBXRU0KRDI5cUxiWXNNaXE0SlR5SVZFNXZzWEo5UzR5dno2MGxNdUM5d0o2Wisv
VQotPiBzc2gtZWQyNTUxOSBDU015aGcgcmYrRjhSNCs0YVBhb3Q5bnpnTStFTzBi
NGtvaHdaL0thenRlUWg4RURuOAowQk5IaWR3LzdtcUZtZ2NCZ204Zm95bTBQMEs2
RTgyeXo0UEs5WkU3WEx3Ci0+IF8wS2d9Qy1ncmVhc2UgYiBVKXEvTAo5NjhBL1Bz
SDdHQWd6SlhRT09vaHA2QlNPZUNzWWRQRDZYSytaRzhFZVEKLS0tIDM2U1pYNC9t
WGZXMTBHSjZwM1h3OG42SFdUVzBlVVRQYkNSdjFXaTQya0UKAHFlX2IsCKZW2U+i
j5sAaM9cDTygDzexTHjdkuUSL3Vn6eGzn3Bd/XEmiCwK2J1s3nniRjbdskQRRDcX
nk0BfbXpPc1HTORQAGPOezfgACA5QRyYbSRu+1lIjopHhaMxDSev83UJr5dtT4Sm
GwI8LnKAxh6M3UFFLOzWoHvnIxf+2PxYH8T+7TIiAmlt6ShD
-----END AGE ENCRYPTED FILE-----

View File

@@ -0,0 +1,17 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyB6TGVV
dnR2U0tVRzArd1RrOUFYbGZ6OUxSNm55VHpQRUdQdWNQRXd0akZnCnRMVFlncHQ0
ckxWUFByaDhPZy93cWd3SG1oWFA5bVdLcno3aDBGbzZsUFEKLT4gc3NoLWVkMjU1
MTkgU3JIYXFBIGFWanFoWEd3TUNjNHRtWlE4eUgvVlF4UkNNZTZCdy9Fd3dQYTh3
VmRpMmsKQzlsQnNGSFVQbWsrNVREUVdkc3VpZnVRcWdEQWhVT3llb3hYOW9INDFP
SQotPiBzc2gtZWQyNTUxOSBDU015aGcgdkVSSjhFSGl0Q1hRM3d5bG1DemtlVjNw
azhhOExneEt3K2NNZmhVZWgxYwpPaWxxOTZNT0M5MDM5by9YS3YzSHFEaTJaTmxD
VVZvNWltWjVLSTFjK1ZFCi0+IGctZ3JlYXNlIDE6PTU3OEwsClNoNmNnZFRsS1NJ
M0xmbjUrYXNnMEN2RGZkSnhLMmloOU5HWVk0MXplQ2s5dWkwQkRDa2NMTnVmVGU2
c0N2NUIKVFJoK1pZdkxvb1RqQVlmWXZPaU1Pb3JLZEJLQUZnb1E2elR3U2pabTdV
aW1xV05FYmhKQzNXeCtoWkZRRHBZCi0tLSBJUWkxemh0NU1ZeG10NmxOZDRueldT
MkhnT2swVzdpOVVhUTczeVNRVGNNCi+SFPbDPVKJYOMjy6cdkt9x1xXIap19hPQa
k2aLDsspt8vDwGmMcy0gdx4toTTJqJciguzHuOlmsfDodPgG7Mxz3u0w3BZU6mEh
3G4LH5Z82vPlDF5xpcI9CRlEb3oq1jjSTdHeOFmNQ8EQhA2n3mvESslz642jI443
hKEAnKKCpXWqg2gyOc0=
-----END AGE ENCRYPTED FILE-----