ops-jrz1/configuration.nix
Dan bc81b4ec15 Rename learner to dev across codebase
- scripts/learner-*.sh → scripts/dev-*.sh
- docs/learner-*.md → docs/dev-*.md
- tests/test-learner-env.sh → tests/test-dev-env.sh
- Update all internal references

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 10:42:34 -08:00

219 lines
7.1 KiB
Nix

{ config, pkgs, beads, opencode, ... }:
let
# ==========================================================================
# Watchdog Scripts - Referenced directly by systemd, NOT in PATH
# ==========================================================================
killswitch = pkgs.writeShellApplication {
name = "killswitch";
runtimeInputs = with pkgs; [ procps systemd util-linux coreutils ];
text = builtins.readFile ./scripts/killswitch;
};
cpu-watchdog = pkgs.writeShellApplication {
name = "cpu-watchdog";
runtimeInputs = with pkgs; [ procps gawk coreutils util-linux killswitch ];
text = builtins.replaceStrings
[ "/usr/local/bin/killswitch" ]
[ "killswitch" ]
(builtins.readFile ./scripts/cpu-watchdog);
};
egress-watchdog = pkgs.writeShellApplication {
name = "egress-watchdog";
# Note: -e removed intentionally - grep returns 1 when no matches
bashOptions = [ "nounset" "pipefail" ];
runtimeInputs = with pkgs; [ systemd gnugrep coreutils util-linux killswitch gawk ];
text = builtins.replaceStrings
[ "/usr/local/bin/killswitch" ]
[ "killswitch" ]
(builtins.readFile ./scripts/egress-watchdog);
};
# ==========================================================================
# Admin Scripts - Added to systemPackages for interactive use
# ==========================================================================
dev-add = pkgs.writeShellApplication {
name = "dev-add";
runtimeInputs = with pkgs; [ shadow coreutils iproute2 gnugrep gawk ];
text = builtins.readFile ./scripts/dev-add.sh;
};
dev-remove = pkgs.writeShellApplication {
name = "dev-remove";
runtimeInputs = with pkgs; [ shadow coreutils gnutar procps findutils ];
text = builtins.readFile ./scripts/dev-remove.sh;
};
in
{
# Main NixOS configuration for ops-jrz1 server
# Imports host-specific configuration from hosts/ops-jrz1.nix
# Boot loader configuration (Legacy BIOS for Vultr VPS)
boot.loader.grub.enable = true;
boot.loader.grub.device = "/dev/vda"; # Install to MBR
# Network configuration
networking.useDHCP = false;
networking.interfaces.ens3.useDHCP = true;
# Time zone
time.timeZone = "UTC";
# Internationalization
i18n.defaultLocale = "en_US.UTF-8";
# System packages
environment.systemPackages = with pkgs; [
vim
git
htop
curl
tmux
# Dev environment
python3
uv
direnv
# AI coding tools (via flake inputs)
beads # Issue tracker (bd CLI)
opencode # AI coding agent (opencode CLI)
# For npm-based AI tools (gemini-cli, codex): users run npm install
nodejs_22
# Admin scripts (declarative deployment)
dev-add
dev-remove
];
# Add ~/.local/bin and /usr/local/bin to PATH for manually installed tools
environment.localBinInPath = true;
environment.shellInit = ''
export PATH="/usr/local/bin:$PATH"
'';
# Enable Nix flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# SSH configuration
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
};
};
# SSH authorized keys for root
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqHsgAuD/8LL6HN3fo7X1ywryQG393pyQ19a154bO+h delpad-2025"
];
# Firewall (will be configured for Matrix services)
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ]; # SSH, HTTP, HTTPS
# 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 = ''
# Log all new outbound connections from regular users
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS: " --log-level info
# Rate limit new outbound connections (30/min sustained, burst 60)
iptables -A OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 30/min --limit-burst 60 -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 REJECT
'';
# Clean up on stop
extraStopCommands = ''
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-j LOG --log-prefix "EGRESS: " --log-level info 2>/dev/null || true
iptables -D OUTPUT -m state --state NEW -m owner --uid-owner 1000:65534 \
-m limit --limit 30/min --limit-burst 60 -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 REJECT 2>/dev/null || true
'';
};
# ACME for Let's Encrypt certificates
security.acme = {
acceptTerms = true;
defaults.email = "dlei@duck.com";
};
# Allow deprecated olm library for Matrix bridges
# Note: olm is deprecated with known CVEs but required by mautrix bridges
# This is necessary for Matrix bridge functionality until alternatives are available
nixpkgs.config.permittedInsecurePackages = [
"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
};
# Per-user limits - create drop-in on activation
# (user-.slice is the template for user-XXXX.slice)
system.activationScripts.userSliceLimits = ''
mkdir -p /run/systemd/system/user-.slice.d
cat > /run/systemd/system/user-.slice.d/50-limits.conf << 'EOF'
[Slice]
MemoryMax=50%
TasksMax=200
CPUQuota=200%
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";
}