Consolidate scattered networking, environment, systemd keys

Fixes 3x statix W20 warnings. No functional change.
- networking: Moved firewall into main block
- environment: Consolidated systemPackages, localBinInPath, shellInit
- systemd: Consolidated slices, services, timers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dan 2026-01-08 12:49:57 -08:00
parent 278017efe3
commit ad1fbf1c8c

View file

@ -57,8 +57,42 @@ in
boot.loader.grub.device = "/dev/vda"; # Install to MBR
# Network configuration
networking.useDHCP = false;
networking.interfaces.ens3.useDHCP = true;
networking = {
useDHCP = false;
interfaces.ens3.useDHCP = true;
# Firewall
firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ]; # SSH, HTTP, HTTPS
allowedUDPPortRanges = [ { from = 60000; to = 60010; } ]; # mosh
# Egress controls for regular users
# UID range: 1000 (first regular user) to 65534 (nobody - excluded from controls)
# This covers all dev accounts while excluding system services
extraCommands = ''
# Rate limit new outbound connections (150/min sustained, burst 300)
# High enough for npm install, low enough to prevent abuse
# DROP instead of REJECT so apps back off naturally
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 150/min --limit-burst 300 -j ACCEPT
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS-LIMIT: " --log-level warning
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j DROP
'';
# Clean up on stop
extraStopCommands = ''
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 150/min --limit-burst 300 -j ACCEPT 2>/dev/null || true
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS-LIMIT: " --log-level warning 2>/dev/null || true
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j DROP 2>/dev/null || true
'';
};
};
# Time zone
time.timeZone = "UTC";
@ -66,50 +100,52 @@ in
# Internationalization
i18n.defaultLocale = "en_US.UTF-8";
# System packages
environment.systemPackages = with pkgs; [
vim
git
htop
curl
tmux
# Dev environment
python3
uv
direnv
zig
# AI coding tools (via flake inputs)
opencode # AI coding agent (opencode CLI)
# For JS-based AI tools (gemini-cli, claude-cli): users run bun/npm install
nodejs_22
bun
# Terminfo for modern terminals (ghostty, kitty, etc.)
pkgs-unstable.ghostty.terminfo
kitty.terminfo
# Classic Unix social tools
bsd-finger
ytalk
fortune
# Mobile shell - resilient SSH for spotty connections
mosh
# Admin scripts (declarative deployment)
dev-add
dev-remove
egress-status
];
# Environment configuration
environment = {
systemPackages = with pkgs; [
vim
git
htop
curl
tmux
# Dev environment
python3
uv
direnv
zig
# AI coding tools (via flake inputs)
opencode # AI coding agent (opencode CLI)
# For JS-based AI tools (gemini-cli, claude-cli): users run bun/npm install
nodejs_22
bun
# Terminfo for modern terminals (ghostty, kitty, etc.)
pkgs-unstable.ghostty.terminfo
kitty.terminfo
# Classic Unix social tools
bsd-finger
ytalk
fortune
# Mobile shell - resilient SSH for spotty connections
mosh
# Admin scripts (declarative deployment)
dev-add
dev-remove
egress-status
];
# Add ~/.local/bin and /usr/local/bin to PATH for manually installed tools
environment.localBinInPath = true;
environment.shellInit = ''
export PATH="/usr/local/bin:$PATH"
# Add ~/.local/bin and /usr/local/bin to PATH for manually installed tools
localBinInPath = true;
shellInit = ''
export PATH="/usr/local/bin:$PATH"
# Set COLORTERM for truecolor terminals (SSH doesn't forward this)
case "$TERM" in
xterm-ghostty|xterm-kitty|alacritty|xterm-256color)
export COLORTERM=truecolor
;;
esac
'';
# Set COLORTERM for truecolor terminals (SSH doesn't forward this)
case "$TERM" in
xterm-ghostty|xterm-kitty|alacritty|xterm-256color)
export COLORTERM=truecolor
;;
esac
'';
};
# Fortune on interactive shell login
programs.bash.interactiveShellInit = ''
@ -138,38 +174,6 @@ in
# Devs group for shared resources (Slack tokens, etc.)
users.groups.devs = {};
# Firewall (will be configured for Matrix services)
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ]; # SSH, HTTP, HTTPS
allowedUDPPortRanges = [ { from = 60000; to = 60010; } ]; # mosh
# Egress controls for regular users
# UID range: 1000 (first regular user) to 65534 (nobody - excluded from controls)
# This covers all dev accounts while excluding system services
extraCommands = ''
# Rate limit new outbound connections (150/min sustained, burst 300)
# High enough for npm install, low enough to prevent abuse
# DROP instead of REJECT so apps back off naturally
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 150/min --limit-burst 300 -j ACCEPT
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS-LIMIT: " --log-level warning
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j DROP
'';
# Clean up on stop
extraStopCommands = ''
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 150/min --limit-burst 300 -j ACCEPT 2>/dev/null || true
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS-LIMIT: " --log-level warning 2>/dev/null || true
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j DROP 2>/dev/null || true
'';
};
# ACME for Let's Encrypt certificates
security.acme = {
acceptTerms = true;
@ -182,11 +186,54 @@ in
"olm-3.2.16"
];
# Resource limits for user slices (prevent one user from crashing server)
systemd.slices."user".sliceConfig = {
MemoryMax = "80%"; # Users collectively can't exceed 80% RAM
TasksMax = 500; # Max 500 total processes across all users
CPUWeight = 100; # Fair sharing when contended, bursts allowed
# Systemd configuration - resource limits and watchdogs
systemd = {
# Resource limits for user slices (prevent one user from crashing server)
slices."user".sliceConfig = {
MemoryMax = "80%"; # Users collectively can't exceed 80% RAM
TasksMax = 500; # Max 500 total processes across all users
CPUWeight = 100; # Fair sharing when contended, bursts allowed
};
services = {
# CPU watchdog - detects sustained abuse, kills offending user
cpu-watchdog = {
description = "CPU abuse watchdog";
serviceConfig = {
Type = "oneshot";
ExecStart = "${cpu-watchdog}/bin/cpu-watchdog";
};
};
# Egress watchdog - detects users hitting rate limits, kills after 3 strikes
egress-watchdog = {
description = "Egress abuse watchdog";
serviceConfig = {
Type = "oneshot";
ExecStart = "${egress-watchdog}/bin/egress-watchdog";
};
};
};
timers = {
cpu-watchdog = {
description = "Run CPU watchdog every minute";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "1min";
};
};
egress-watchdog = {
description = "Run egress watchdog every minute";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "1min";
};
};
};
};
# Per-user limits - create drop-in on activation
@ -201,42 +248,6 @@ in
EOF
'';
# CPU watchdog - detects sustained abuse, kills offending user
systemd.services.cpu-watchdog = {
description = "CPU abuse watchdog";
serviceConfig = {
Type = "oneshot";
ExecStart = "${cpu-watchdog}/bin/cpu-watchdog";
};
};
systemd.timers.cpu-watchdog = {
description = "Run CPU watchdog every minute";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "1min";
};
};
# Egress watchdog - detects users hitting rate limits, kills after 3 strikes
systemd.services.egress-watchdog = {
description = "Egress abuse watchdog";
serviceConfig = {
Type = "oneshot";
ExecStart = "${egress-watchdog}/bin/egress-watchdog";
};
};
systemd.timers.egress-watchdog = {
description = "Run egress watchdog every minute";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "1min";
OnUnitActiveSec = "1min";
};
};
# This value determines the NixOS release compatibility
system.stateVersion = "24.05";
}