feat: netbird

This commit is contained in:
danny 2026-01-08 14:21:53 +08:00
parent 53b83b3471
commit ea118b7995
64 changed files with 1088 additions and 665 deletions

View file

@ -67,6 +67,7 @@ in
"roundcube"
"grafana"
"crowdsec"
"netbird"
];
location = "${backupPath}/postgresql";
};

View file

@ -17,11 +17,9 @@ in
"maps.rspamd.com"
"cdn-hub.crowdsec.net"
"api.crowdsec.net"
"mx1.daccc.info"
"mx1.dnywe.com"
];
allowedIPs = [
"10.0.0.0/24"
"127.0.0.1"
# CrowdSec
"52.51.161.146"

View file

@ -3,5 +3,6 @@
./nginx.nix
./services.nix
./step-ca.nix
./wireguard.nix
];
}

View file

@ -64,14 +64,6 @@
locations."/".proxyPass = "http://10.0.0.130:8001/phone.html";
};
"ca.net.dn" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "https://10.0.0.1:8443/";
};
};
};
};
}

View file

@ -6,8 +6,9 @@
}:
let
inherit (config.systemConf) username security;
inherit (lib) concatStringsSep;
inherit (lib) concatStringsSep mkForce optionalString;
inherit (helper.nftables) mkElementsStatement;
netbirdCfg = config.services.netbird;
ethInterface = "enp0s31f6";
sshPorts = [ 30072 ];
@ -23,19 +24,16 @@ let
restrict = "10.0.0.128/25";
};
kube = {
ip = "10.10.0.1/24";
range = "10.10.0.0/24";
infra = {
ip = "10.10.0.2/32";
interface = "wg1";
port = 51821;
masterIP = "10.10.0.1";
masterHostname = "api-kube.${config.networking.domain}";
masterAPIServerPort = 6443;
range = "10.10.0.0/24";
};
allowedSSHIPs = concatStringsSep ", " [
"122.117.215.55"
"192.168.100.1/24"
"100.64.0.0/16"
personal.range
];
@ -168,6 +166,13 @@ let
];
in
{
systemConf.security.allowedIPs = [
"10.10.0.0/24"
"10.0.0.0/24"
];
services.resolved.enable = mkForce false;
networking = {
nat = {
enable = true;
@ -175,7 +180,6 @@ in
externalInterface = ethInterface;
internalInterfaces = [
personal.interface
kube.interface
];
};
@ -183,15 +187,12 @@ in
allowedUDPPorts = [
53
personal.port
kube.port
25565
kube.masterAPIServerPort
5359
];
allowedTCPPorts = sshPorts ++ [
53
25565
kube.masterAPIServerPort
5359
];
};
@ -235,9 +236,10 @@ in
tcp dport { ${sshPortsString} } jump ssh-filter
iifname { ${ethInterface}, ${personal.interface}, ${kube.interface} } udp dport { ${toString personal.port}, ${toString kube.port} } accept
iifname ${personal.interface} ip saddr ${personal.ip} jump wg-subnet
iifname ${kube.interface} ip saddr ${kube.ip} jump kube-filter
iifname { ${ethInterface}, ${personal.interface} } udp dport { ${toString personal.port} } accept
iifname ${infra.interface} ip saddr ${infra.range} accept
iifname ${personal.interface} ip saddr ${personal.range} jump wg-subnet
iifname ${netbirdCfg.clients.wt0.interface} accept
drop
}
@ -251,6 +253,11 @@ in
udp dport 53 accept
tcp dport 53 accept
# Allow UDP hole punching
${optionalString (
netbirdCfg.clients ? wt0
) ''udp sport ${toString netbirdCfg.clients.wt0.port} accept''}
meta skuid ${toString config.users.users.systemd-timesync.uid} accept
ct state vmap { invalid : drop, established : accept, related : accept }
@ -273,16 +280,11 @@ in
meta l4proto { icmp, ipv6-icmp } accept
iifname ${personal.interface} ip saddr ${personal.ip} jump wg-subnet
iifname ${kube.interface} ip saddr ${kube.ip} jump kube-filter
iifname ${infra.interface} ip saddr ${infra.ip} accept
counter
}
chain kube-filter {
ip saddr ${kube.ip} ip daddr ${kube.ip} accept
counter drop
}
chain wg-subnet {
ip saddr ${personal.full} accept
ip saddr ${personal.restrict} ip daddr ${personal.range} accept
@ -309,17 +311,8 @@ in
inherit (r) publicKey allowedIPs;
}) (fullRoute ++ meshRoute);
};
${kube.interface} = {
ips = [ kube.ip ];
listenPort = kube.port;
privateKeyFile = config.sops.secrets."wireguard/privateKey".path;
peers = [ ];
};
};
};
extraHosts = "${kube.masterIP} ${kube.masterHostname}";
};
services = {
@ -349,7 +342,7 @@ in
openssh = {
enable = true;
ports = sshPorts;
ports = mkForce sshPorts;
settings = {
PasswordAuthentication = false;
UseDns = false;
@ -385,9 +378,7 @@ in
pdns-recursor = {
enable = true;
forwardZones = {
"${config.networking.domain}." = "127.0.0.1:5359";
"pre7780.dn." = "127.0.0.1:5359";
"test.local." = "127.0.0.1:5359";
"dn." = "127.0.0.1:5359";
};
forwardZonesRecurse = {
# ==== Rspamd DNS ==== #
@ -514,7 +505,7 @@ in
"uptime.${config.networking.domain}" = {
enableACME = true;
forceSSL = true;
locations."/".proxyPass = "http://localhost:3001";
locations."/".proxyPass = "http://127.0.0.1:3001";
};
};

View file

@ -80,4 +80,12 @@ Bq-3sY8n13Dv0E6yx2hVIAlzLj3aE29LC4A2j81vW5MtpaM27lMpg.cwlqZ-8l1iZNeeS9.idRpRJ9zB
openFirewall = true;
intermediatePasswordFile = config.sops.secrets."step_ca/password".path;
};
services.nginx.virtualHosts."ca.net.dn" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "https://10.0.0.1:8443/";
};
};
}

View file

@ -0,0 +1,5 @@
{ config, ... }:
{
sops.secrets."wireguard/wg1.conf" = { };
networking.wg-quick.interfaces.wg1.configFile = config.sops.secrets."wireguard/wg1.conf".path;
}

View file

@ -5,7 +5,12 @@
...
}:
let
inherit (lib) mkOption types concatStringsSep;
inherit (lib)
mkOption
types
concatStringsSep
unique
;
cfg = config.systemConf.security;
in
{
@ -14,6 +19,7 @@ in
type = with types; listOf str;
description = "Domains that allowed to query dns.";
default = [ ];
apply = v: unique v;
};
rules = {
setName = mkOption {

View file

@ -4,6 +4,8 @@
extraAllowList = [
"10.0.0.0/24"
"122.117.215.55"
# Netbird
"100.104.0.0/16"
];
})
];

View file

@ -3,11 +3,15 @@
imports = [
./actual-budget.nix
./bitwarden.nix
# ./docmost.nix
./minecraft-server.nix
./mail-server.nix
./nextcloud.nix
./paperless-ngx.nix
./metrics.nix
./forgejo.nix
./keycloak.nix
./netbird.nix
./hideTTY.nix
# (import ../../../modules/opencloud.nix {
# fqdn = "opencloud.net.dn";
# envFile = config.sops.secrets."opencloud".path;

View file

@ -0,0 +1,72 @@
{ lib, config, ... }:
let
cfg = config.services.forgejo;
srv = cfg.settings.server;
domain = "git.dnywe.com";
mailServer = "mx1.net.dn";
forgejoOwner = {
owner = "forgejo";
mode = "400";
};
in
{
sops.secrets = {
"forgejo/mailer/password" = forgejoOwner;
"forgejo/server/secretKey" = forgejoOwner;
};
networking.firewall.allowedTCPPorts = [ srv.HTTP_PORT ];
services.postgresqlBackup.databases = [ cfg.database.name ];
systemd.services.forgejo.preStart =
let
adminCmd = "${lib.getExe cfg.package} admin user";
pwd = config.sops.secrets."forgejo/mailer/password";
user = "forgejo";
in
''
${adminCmd} create --admin --email "noreply@${srv.DOMAIN}" --username ${user} --password "$(tr -d '\n' < ${pwd.path})" || true
'';
services.openssh.settings.AllowUsers = [ cfg.user ];
services.forgejo = {
enable = true;
database.type = "postgres";
lfs.enable = true;
settings = {
server = {
DOMAIN = domain;
ROOT_URL = "https://${srv.DOMAIN}";
HTTP_PORT = 32006;
SSH_PORT = lib.head config.services.openssh.ports;
# ==== OpenID Connect ==== #
ENABLE_OPENID_SIGNIN = true;
WHITELISTED_URIS = "https://${config.services.keycloak.settings.hostname}/*";
};
services.DISABLE_REGISTRATION = true;
actions = {
ENABLE = true;
DEFAULT_ACTION_URL = "github";
};
mailer = {
ENABLED = true;
SMTP_ADDR = mailServer;
SMTP_PORT = 587;
FROM = "noreply@${srv.DOMAIN}";
USER = "noreply@${srv.DOMAIN}";
};
};
secrets = {
mailer.PASSWD = config.sops.secrets."forgejo/mailer/password".path;
server.SECRET_KEY = config.sops.secrets."forgejo/server/secretKey".path;
};
};
}

View file

@ -0,0 +1,13 @@
{ ... }:
{
systemd.services.hideTTY = {
description = "Auto turn off monitor ";
wantedBy = [ "multi-user.target" ];
script = ''
echo 1 > /sys/class/graphics/fb0/blank
'';
serviceConfig = {
Type = "oneshot";
};
};
}

View file

@ -0,0 +1,17 @@
# NOTE: This is keycloak partial overwrite for `mail-server.nix`.
{ lib, config, ... }:
let
inherit (lib) mkForce;
domain = "dnywe.com";
cfg = config.services.keycloak;
in
{
services.keycloak = {
settings = {
hostname = mkForce "login.${domain}";
};
};
# Disable nginx reverse proxy
services.nginx.virtualHosts."${cfg.settings.hostname}" = mkForce { };
}

View file

@ -1,9 +1,25 @@
{ config, lib, ... }:
{
config,
lib,
...
}:
let
inherit (lib) mkForce;
inherit (config.systemConf) username;
in
{
systemConf.security.allowedDomains = [
"registry-1.docker.io"
"auth.docker.io"
"login.docker.com"
"auth.docker.com"
"production.cloudflare.docker.com"
"docker-images-prod.6aa30f8b08e16409b46e0173d6de2f56.r2.cloudflarestorage"
"api.docker.com"
"cdn.segment.com"
"api.segment.io"
];
mail-server =
let
domain = "net.dn";
@ -81,29 +97,16 @@ in
};
};
services.openldap.settings.attrs.olcLogLevel = mkForce "config";
services.postfix.settings.main = {
# internal_mail_filter_classes = [ "bounce" ];
virtualisation.oci-containers.containers.phpLDAPadmin = {
environment = {
LDAP_ALLOW_GUEST = "true";
LOG_LEVEL = "debug";
LDAP_LOGGING = "true";
};
};
services.rspamd = {
locals."logging.conf".text = ''
level = "debug";
'';
locals."settings.conf".text = ''
bounce {
id = "bounce";
priority = high;
ip = "127.0.0.1";
selector = 'smtp_from.regexp("/^$/").last';
apply {
BOUNCE = -25.0;
}
symbols [ "BOUNCE" ]
}
'';
services.openldap.settings = {
attrs.olcLogLevel = mkForce "config";
# children."cn=schema".includes = extraSchemas;
};
}

View file

@ -63,7 +63,7 @@ in
job_name = "powerdns_recursor";
static_configs = [
{
targets = [ "localhost:${toString config.services.pdns-recursor.api.port}" ];
targets = [ "127.0.0.1:${toString config.services.pdns-recursor.api.port}" ];
labels = {
machine = "${hostName}";
};
@ -87,7 +87,7 @@ in
static_configs = [
{
targets = [
"localhost:${toString config.services.crowdsec.settings.general.prometheus.listen_port}"
"127.0.0.1:${toString config.services.crowdsec.settings.general.prometheus.listen_port}"
];
labels = {
machine = "${hostName}";

View file

@ -0,0 +1,40 @@
{ pkgs, ... }:
let
modpack = pkgs.fetchPackwizModpack {
url = "https://git.dnywe.com/dachxy/shader-retired-modpack/raw/branch/main/pack.toml";
packHash = "sha256-NPMS8j5NXbtbsso8R4s4lhx5L7rQJdek62G2Im3JdmM=";
};
in
{
systemConf.security.allowedDomains = [
"api.mojang.com"
"textures.minecraft.net"
"session.minecraft.net"
"login.microsoftonline.com"
];
services.minecraft-servers = {
enable = true;
eula = true;
};
services.minecraft-servers.servers.shader-retired = {
enable = true;
autoStart = true;
openFirewall = true;
package = pkgs.fabric-server;
symlinks = {
"mods" = "${modpack}/mods";
};
serverProperties = {
server-port = 25565;
difficulty = 3;
gamemode = "survival";
max-player = 20;
modt = "Bro!!!!";
accepts-flight = true;
accepts-transfers = true;
hardcore = false;
};
};
}

View file

@ -0,0 +1,119 @@
{ config, lib, ... }:
let
inherit (lib) mkForce;
domain = "dnywe.com";
# Virtual Domain
vDomain = "vnet.dn";
proxyIP = "10.10.0.1";
cfg = config.services.netbird;
srv = cfg.server;
# TODO: Change realm to master
realm = "netbird";
in
{
sops.secrets."netbird/wt0-setupKey" = {
owner = cfg.clients.wt0.user.name;
mode = "400";
};
systemConf.security.allowedDomains = [
"login.dnywe.com"
"pkgs.netbird.io"
"${srv.domain}"
];
imports = [
(import ../../../modules/netbird-server.nix {
inherit realm vDomain;
domain = "netbird.${domain}";
oidcURL = "https://${config.services.keycloak.settings.hostname}";
enableNginx = false;
oidcType = "keycloak";
})
];
services.netbird = {
ui.enable = mkForce false;
clients.wt0 = {
port = 51830;
openFirewall = true;
autoStart = true;
environment = {
NB_MANAGEMENT_URL = "https://${srv.domain}";
};
login = {
enable = true;
setupKeyFile = config.sops.secrets."netbird/wt0-setupKey".path;
};
};
server.management = {
disableSingleAccountMode = false;
singleAccountModeDomain = vDomain;
metricsPort = 32009;
turnDomain = mkForce "coturn.${domain}";
extraOptions = [ "--user-delete-from-idp" ];
};
server.coturn.enable = mkForce false;
};
networking.firewall.allowedTCPPorts = [ 32011 ];
# ==== Proxy By Caddy & CDN ==== #
services.nginx.appendHttpConfig = ''
set_real_ip_from ${proxyIP};
real_ip_header X-Forwarded-For;
real_ip_recursive on;
'';
services.nginx.virtualHosts."netbird.local" = {
locations = {
"/" = {
root = cfg.server.dashboard.finalDrv;
tryFiles = "$uri $uri.html $uri/ =404";
};
"/404.html".extraConfig = ''
internal;
'';
"/api" = {
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
proxyPass = "http://127.0.0.1:${builtins.toString srv.management.port}";
};
"/management.ManagementService/".extraConfig = ''
client_body_timeout 1d;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_pass grpc://127.0.0.1:${builtins.toString srv.management.port};
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
'';
"/signalexchange.SignalExchange/".extraConfig = ''
client_body_timeout 1d;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_pass grpc://127.0.0.1:${builtins.toString srv.signal.port};
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
'';
};
extraConfig = ''
error_page 404 /404.html;
'';
};
}

View file

@ -1,19 +1,156 @@
{ config, ... }:
{
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkDefault mkAfter;
inherit (config.sops) secrets;
spreedCfg = config.services.nextcloud-spreed-signaling;
nextcloudCfg = config.services.nextcloud;
turnDomain = "coturn.dnywe.com";
domain = "net.dn";
in
{
sops.secrets = {
"nextcloud/smtpPassword" = {
owner = "nextcloud";
group = "nextcloud";
};
"nextcloud/adminPassword" = { };
"nextcloud/whiteboard" = {
owner = "nextcloud";
};
"nextcloud/spreed/turnPassword" = {
key = "netbird/coturn/password";
owner = spreedCfg.user;
};
"nextcloud/spreed/turnSecret" = {
key = "netbird/oidc/secret";
owner = spreedCfg.user;
};
"nextcloud/spreed/hashkey" = {
owner = spreedCfg.user;
};
"nextcloud/spreed/blockkey" = {
owner = spreedCfg.user;
};
"nextcloud/spreed/internalsecret" = {
owner = spreedCfg.user;
};
"nextcloud/spreed/backendsecret" = {
owner = spreedCfg.user;
};
};
imports = [
(import ../../../modules/nextcloud.nix {
hostname = "nextcloud.net.dn";
adminpassFile = config.sops.secrets."nextcloud/adminPassword".path;
hostname = "nextcloud.${domain}";
adminpassFile = secrets."nextcloud/adminPassword".path;
trusted-proxies = [ "10.0.0.0/24" ];
whiteboardSecrets = [
config.sops.secrets."nextcloud/whiteboard".path
secrets."nextcloud/whiteboard".path
];
})
];
services.nextcloud = {
extraApps = {
inherit (config.services.nextcloud.package.packages.apps) music;
inherit (config.services.nextcloud.package.packages.apps) music spreed;
user_migration = pkgs.fetchNextcloudApp {
url = "https://github.com/nextcloud-releases/user_migration/releases/download/v9.0.0/user_migration-v9.0.0.tar.gz";
sha256 = "sha256-WiEEAazuj8kh5o+URs22uoNWANXcXQYLTaoABMU6rFo=";
license = "agpl3Plus";
};
cospend = pkgs.fetchNextcloudApp {
url = "https://github.com/julien-nc/cospend-nc/releases/download/v3.2.0/cospend-3.2.0.tar.gz";
sha256 = "sha256-mclcZDNmvpYX/2q7azyiTLSCiTYvk7ILeqtb/8+0ADQ=";
license = "agpl3Plus";
};
};
appstoreEnable = false;
settings = {
mail_smtpauth = true;
mail_smtphost = "mx1.${domain}";
mail_smtpname = "nextcloud";
mail_smtpmode = "smtp";
mail_smtpauthtype = "LOGIN";
mail_domain = "net.dn";
mail_smtpport = 465;
mail_smtpsecure = "ssl";
mail_from_address = "nextcloud";
};
secrets = {
mail_smtppassword = secrets."nextcloud/smtpPassword".path;
};
};
# ==== Nextcloud Talk ==== #
services.nextcloud-spreed-signaling = {
enable = true;
configureNginx = true;
hostName = "talk.${domain}";
backends.default = {
urls = [ "https://${nextcloudCfg.hostName}" ];
secretFile = secrets."nextcloud/spreed/backendsecret".path;
};
settings = {
http.listen = "127.0.0.1:31008";
turn = {
servers = [ "turn:${turnDomain}:3478?transport=udp" ];
secretFile = secrets."nextcloud/spreed/turnPassword".path;
apikeyFile = secrets."nextcloud/spreed/turnSecret".path;
};
clients.internalsecretFile = secrets."nextcloud/spreed/internalsecret".path;
sessions = {
hashkeyFile = secrets."nextcloud/spreed/hashkey".path;
blockkeyFile = secrets."nextcloud/spreed/blockkey".path;
};
nats.url = [ "nats://127.0.0.1:4222" ];
};
};
services.nats = mkIf nextcloudCfg.enable {
enable = true;
settings = {
host = "127.0.0.1";
};
};
services.nginx.virtualHosts.${spreedCfg.hostName} = {
enableACME = true;
forceSSL = true;
};
# ==== Secruity ==== #
services.fail2ban = {
jails = {
nextcloud.settings = {
backend = "systemd";
journalmatch = "SYSLOG_IDENTIFIER=Nextcloud";
enabled = true;
port = 443;
protocol = "tcp";
filter = "nextcloud";
maxretry = 3;
bantime = 86400;
findtime = 43200;
};
};
};
environment.etc = {
"fail2ban/filter.d/nextcloud.local".text = mkDefault (mkAfter ''
[Definition]
failregex = ^.*"remoteAddr":"(?P<host><HOST>)".*"message":"Login failed:
^.*"remoteAddr":"(?P<host><HOST>)".*"message":"Two-factor challenge failed:
^.*"remoteAddr":"(?P<host><HOST>)".*"message":"Trusted domain error
'');
};
}

File diff suppressed because one or more lines are too long

View file

@ -5,10 +5,6 @@ in
{
sops.secrets = {
"wireguard/privateKey" = { };
"nextcloud/adminPassword" = { };
"nextcloud/whiteboard" = {
owner = "nextcloud";
};
"step_ca/password" = { };
vaultwarden = { };
"oauth/password" = { };