feat: add outbound firewall to server & add helix and zellij

This commit is contained in:
danny 2025-10-22 16:24:33 +08:00
parent d273765b99
commit 7fcf26263a
23 changed files with 769 additions and 145 deletions

View file

@ -9,7 +9,6 @@ let
port = 51820;
in
{
services.netbird = {
server = {
enable = true;

View file

@ -57,6 +57,7 @@ let
};
};
};
# Node
vm-2 = {
ip = "192.168.0.7";

View file

@ -8,6 +8,7 @@ in
home-manager.users."${username}" = {
imports = [
../../../../home/presets/basic.nix
./expr
./wm
# Bitwarden client

View file

@ -0,0 +1,7 @@
{ ... }:
{
imports = [
./helix.nix
../../../../../home/user/zellij.nix
];
}

View file

@ -0,0 +1,82 @@
{
osConfig,
lib,
pkgs,
...
}:
{
programs.helix = {
enable = true;
extraPackages = with pkgs; [
nixd
bash-language-server
docker-language-server
fish-lsp
typescript-language-server
superhtml
hyprls
jq-lsp
vscode-json-languageserver
texlab # Latex
lua-language-server
marksman # Markdown
clang-tools # Clangd
intelephense # Php
ruff # Python
rust-analyzer
vscode-css-languageserver
systemd-lsp
taplo
vue-language-server
yaml-language-server
zls
];
settings = {
editor.cursor-shape = {
normal = "block";
insert = "bar";
select = "underline";
};
keys.normal = {
space.space = "file_picker";
G = "goto_file_end";
D = "kill_to_line_end";
V = [ "extend_to_line_bounds" ];
"$" = "goto_line_end";
"^" = "goto_line_start";
x = "delete_selection";
esc = [
"collapse_selection"
"keep_primary_selection"
];
space.w = ":w";
space.q = ":q";
};
};
languages.language = [
{
name = "nix";
auto-format = true;
}
];
languages.language-server = {
nixd = {
command = "nixd";
args = [ "--semantic-tokens=true" ];
config.nixd =
let
myFlake = ''(builtins.getFlake "/etc/nixos")'';
nixosOpts = "${myFlake}.nixosConfigurations.${osConfig.networking.hostName}.options";
in
{
nixpkgs.expr = "import ${myFlake}.inputs.nixpkgs { }";
formatting.command = [ "${lib.getExe pkgs.nixfmt}" ];
options = {
nixos.expr = nixosOpts;
home-manager.expr = "${nixosOpts}.home-manager.users.type.getSubOptions []";
};
};
};
};
};
}

View file

@ -15,6 +15,29 @@ in
inherit hostname username;
domain = "net.dn";
hyprland.enable = false;
security = {
allowedDomains = [
"github.com"
"cache.nixos.org"
"hyprland.cachix.org"
"maps.rspamd.com"
"cdn-hub.crowdsec.net"
"api.crowdsec.net"
];
allowedIPs = [
"10.0.0.0/24"
"127.0.0.1"
];
allowedIPv6 = [
"ff02::/16"
"fe80::/10"
"::1"
];
sourceIPs = [
"10.0.0.1"
"192.168.100.0/24"
];
};
};
imports = [
@ -25,6 +48,7 @@ in
./security
./services
./sops
./options
];
environment.systemPackages = with pkgs; [

View file

@ -1,14 +1,17 @@
{
config,
lib,
helper,
...
}:
let
inherit (config.systemConf) username;
inherit (config.systemConf) username security;
inherit (lib) concatStringsSep;
inherit (helper.nftables) mkElementsStatement;
ethInterface = "enp0s31f6";
sshPorts = [ 30072 ];
sshPortsString = builtins.concatStringsSep ", " (builtins.map (p: builtins.toString p) sshPorts);
sshPortsString = concatStringsSep ", " (map (p: toString p) sshPorts);
personal = {
inherit (config.networking) domain;
@ -30,7 +33,7 @@ let
masterAPIServerPort = 6443;
};
allowedSSHIPs = builtins.concatStringsSep ", " [
allowedSSHIPs = concatStringsSep ", " [
"122.117.215.55"
"192.168.100.1/24"
personal.range
@ -189,63 +192,104 @@ in
nftables = {
enable = true;
ruleset = ''
table inet wg-filter {
chain input {
type filter hook input priority 0; policy drop;
tables = {
filter = {
family = "inet";
content = ''
set restrict_source_ips {
type ipv4_addr
flags interval
${mkElementsStatement security.sourceIPs}
}
iif lo accept
set ${security.rules.setName} {
type ipv4_addr
flags interval
${mkElementsStatement security.allowedIPs}
}
meta nftrace set 1
meta l4proto { icmp, ipv6-icmp } accept
set ${security.rules.setNameV6} {
type ipv6_addr
flags interval
${mkElementsStatement security.allowedIPv6}
}
ct state vmap { invalid : drop, established : accept, related : accept }
chain input {
type filter hook input priority 0; policy drop;
udp dport 53 accept
tcp dport 53 accept
iif lo accept
tcp dport { ${sshPortsString} } jump ssh-filter
meta nftrace set 1
meta l4proto { icmp, ipv6-icmp } accept
iifname { ${ethInterface}, ${personal.interface}, ${kube.interface} } udp dport { ${builtins.toString personal.port}, ${builtins.toString kube.port} } accept
iifname ${personal.interface} ip saddr ${personal.ip} jump wg-subnet
iifname ${kube.interface} ip saddr ${kube.ip} jump kube-filter
ct state vmap { invalid : drop, established : accept, related : accept }
counter reject
}
udp dport 53 accept
tcp dport 53 accept
chain ssh-filter {
ip saddr { ${allowedSSHIPs} } accept
counter reject
}
tcp dport { ${sshPortsString} } jump ssh-filter
chain forward {
type filter hook forward priority 0; policy drop;
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
meta l4proto { icmp, ipv6-icmp } accept
drop
}
iifname ${personal.interface} ip saddr ${personal.ip} jump wg-subnet
iifname ${kube.interface} ip saddr ${kube.ip} jump kube-filter
chain output {
type filter hook output priority 0; policy drop;
counter
}
iif lo accept
chain kube-filter {
ip saddr ${kube.ip} ip daddr ${kube.ip} accept
counter drop
}
# Allow DNS qeury
udp dport 53 accept
tcp dport 53 accept
chain wg-subnet {
ip saddr ${personal.full} accept
ip saddr ${personal.restrict} ip daddr ${personal.range} accept
counter drop
}
meta skuid ${toString config.users.users.systemd-timesync.uid} accept
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname ${ethInterface} masquerade
}
}
'';
ct state vmap { invalid : drop, established : accept, related : accept }
ip saddr != @restrict_source_ips accept
ip daddr @${security.rules.setName} accept
ip6 daddr @${security.rules.setNameV6} accept
counter log prefix "OUTPUT-DROP: " flags all drop
}
chain ssh-filter {
ip saddr { ${allowedSSHIPs} } accept
counter reject
}
chain forward {
type filter hook forward priority 0; policy drop;
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
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
counter drop
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname ${ethInterface} masquerade
}
'';
};
};
};
wireguard = {
@ -323,7 +367,11 @@ in
webserver-port=8081
local-port=5359
dnsupdate=yes
primary=yes
secondary=no
allow-dnsupdate-from=10.0.0.0/24
allow-axfr-ips=10.0.0.0/24
also-notify=10.0.0.148:53
'';
secretFile = config.sops.secrets.powerdns.path;
};
@ -333,6 +381,7 @@ in
forwardZones = {
"${config.networking.domain}." = "127.0.0.1:5359";
"pre7780.dn." = "127.0.0.1:5359";
"test.local." = "127.0.0.1:5359";
};
forwardZonesRecurse = {
"." = "8.8.8.8";
@ -342,9 +391,6 @@ in
"127.0.0.0/8"
"10.0.0.0/24"
"192.168.100.0/24"
"::1/128"
"fc00::/7"
"fe80::/10"
];
yaml-settings = {
webservice.webserver = true;

View file

@ -0,0 +1,5 @@
{
imports = [
./network.nix
];
}

View file

@ -0,0 +1,91 @@
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types concatStringsSep;
cfg = config.systemConf.security;
in
{
options.systemConf.security = {
allowedDomains = mkOption {
type = with types; listOf str;
description = "Domains that allowed to query dns.";
default = [ ];
};
rules = {
setName = mkOption {
type = with types; str;
default = "allowed_output_ips";
readOnly = true;
};
setNameV6 = mkOption {
type = with types; str;
default = "allowed_output_ipv6";
readOnly = true;
};
};
dnsIPs = mkOption {
type = with types; listOf str;
description = "External DNS server to use";
default = [ "8.8.8.8" ];
};
allowedIPs = mkOption {
type = with types; listOf str;
description = "IPv4 that allowed to request.";
default = [ ];
};
allowedIPv6 = mkOption {
type = with types; listOf str;
description = "IPv6 that allowed to request.";
default = [ ];
};
sourceIPs = mkOption {
type = with types; listOf str;
description = "Source IPs to restrict.";
default = [ ];
};
};
config = {
environment.systemPackages = with pkgs; [
ipset
];
systemd.timers.fetch-allowed-domains = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = 10;
OnUnitActiveSec = 60;
};
};
systemd.services.fetch-allowed-domains = {
path = with pkgs; [
nftables
dig.dnsutils
];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${pkgs.writeShellScript "fetch-allowed-domains" ''
DOMAINS=(${toString (map (x: ''"${x}"'') cfg.allowedDomains)})
SETNAME="inet filter ${cfg.rules.setName}"
nft flush set $SETNAME
nft add element $SETNAME { ${concatStringsSep "," cfg.allowedIPs} }
for domain in "''${DOMAINS[@]}"; do
ips=$(dig +short A $domain | grep -E '^[0-9.]+$')
for ip in $ips; do
nft add element $SETNAME { $ip }
echo "Added $ip for $domain"
done
done
''}";
Type = "oneshot";
};
};
};
}

View file

@ -36,7 +36,7 @@ in
adminAccountFile = config.sops.secrets."oauth/adminEnv".path;
};
ldap = {
filter = "(&(objectClass=inetOrgPerson)(objectClass=mailRoutingObject)(uid=%{user | username}))";
filter = "(&(objectClass=inetOrgPerson)(objectClass=inetMailRoutingObject)(uid=%{user | username}))";
extraAuthConf = ''
auth_username_format = %{user | lower}
fields {

View file

@ -1,10 +1,12 @@
{
pkgs,
config,
lib,
...
}:
let
inherit (config.systemConf) username;
inherit (lib) mkForce;
caskaydia = {
name = "CaskaydiaCove Nerd Font Mono";
@ -71,6 +73,10 @@ in
enable = true;
transparentBackground = true;
};
helix = {
enable = true;
transparent = mkForce true;
};
};
};
}

View file

@ -4,6 +4,8 @@
{
virtualisation = {
docker.enable = true;
docker.daemon.settings = {
};
spiceUSBRedirection.enable = true;
};
}