From e08c4b27e292cc3685dddbfc85bfbd73978ad8e3 Mon Sep 17 00:00:00 2001 From: Lucas Thelen Date: Fri, 3 Oct 2025 18:13:26 +0000 Subject: [PATCH] Extensible DDNS --- CLAUDE.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 1 + services/site.nix | 25 +++++++++++++----- users.nix | 2 +- 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ace088e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,65 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +This is a NixOS homelab configuration using Nix flakes. The setup includes a self-hosted Jellyfin media server, personal website hosting, VPN access via Tailscale, and automated DNS management with AWS Route53. + +## Development Commands + +### System Management +- `sudo nixos-rebuild switch --flake .#homelab` - Apply configuration changes +- `sudo nixos-rebuild test --flake .#homelab` - Test configuration without making it default +- `nix flake update` - Update flake inputs (updates flake.lock) +- `nixfmt-classic *.nix` - Format Nix files using the installed formatter + +### Git Operations +- Configuration is version controlled - commit changes after testing +- Current branch: main +- Modified files: flake.nix, users.nix (check git status) + +## Architecture + +### Flake Structure +- `flake.nix` - Main flake definition with inputs (nixpkgs, agenix, personal site) +- `configuration.nix` - Default NixOS configuration (mostly commented out) +- `system.nix` - System-level settings (boot, power management) +- `packages.nix` - System packages (neovim, git, tools) +- `users.nix` - User configuration for lucas user with fish shell + +### Modular Organization +- `networking/` - Network configuration modules + - `host.nix` - Static IP, firewall, Avahi discovery + - `ssh.nix` - SSH daemon configuration + - `adblock.nix` - Ad blocking setup + - `vpn-host.nix` - Tailscale VPN with auto-connect +- `services/` - Service modules + - `jellyfin.nix` - Media server with Deluge torrent client + - `site.nix` - Nginx reverse proxy, ACME SSL, Route53 DDNS + +### Secret Management +- Uses agenix for encrypted secrets +- `secrets/aws.age` - AWS credentials for Route53 updates +- `tailscale.age` - Tailscale authentication key + +### Key Services +- **Jellyfin**: Media server on port 8096, proxied via jellyfin.per-aspera.space +- **Personal Website**: Static site from GitHub repo served at per-aspera.space +- **Deluge**: Torrent client with web interface +- **Route53 DDNS**: Automated IP updates every 5 minutes +- **Tailscale**: VPN access with firewall rules + +### Network Configuration +- Static IP: 192.168.0.10/24 +- Hostname: homelab +- Firewall: Configured per service (HTTP/HTTPS, Jellyfin, SSH via Tailscale) +- Custom nameservers: 205.171.3.25, 8.8.8.8 + +## Important Notes + +- System is configured as a server (no hibernation/suspend) +- Uses systemd-boot EFI bootloader +- Multimedia group for media file permissions at /data/media +- Fish shell is the default for the lucas user +- ACME certificates automatically managed for per-aspera.space domain \ No newline at end of file diff --git a/flake.nix b/flake.nix index 5d51fb2..58787fe 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,7 @@ environment.systemPackages = [ agenix.packages.x86_64-linux.default ]; age.secrets.tailscale.file = ./tailscale.age; age.secrets.aws.file = ./secrets/aws.age; + nixpkgs.config.allowUnfree = true; } ]; }; diff --git a/services/site.nix b/services/site.nix index c83862d..b3fb44d 100644 --- a/services/site.nix +++ b/services/site.nix @@ -1,13 +1,23 @@ -{ config, pkgs, inputs, ... }: +{ config, pkgs, inputs, lib, ... }: let domain = "per-aspera.space"; + hostedZoneId = "Z09728753LLLNSYFXIBIM"; + + # Configurable list of DNS records to update + dnsRecords = [ + domain + "jellyfin.${domain}" + # Add more records here as needed + # "api.${domain}" + # "mail.${domain}" + ]; + updateRoute53 = pkgs.writeShellScript "update-route53" '' #!/usr/bin/env bash set -euo pipefail - HOSTED_ZONE_ID="Z09728753LLLNSYFXIBIM" - DOMAIN="${domain}" - SUBDOMAIN="jellyfin.${domain}" + HOSTED_ZONE_ID="${hostedZoneId}" + DNS_RECORDS=(${lib.concatStringsSep " " (map lib.escapeShellArg dnsRecords)}) # Get current public IP CURRENT_IP=$(${pkgs.curl}/bin/curl -s https://ifconfig.me) @@ -46,9 +56,10 @@ let fi } - # Update both records - update_record "$DOMAIN" - update_record "$SUBDOMAIN" + # Update all configured records + for record in "''${DNS_RECORDS[@]}"; do + update_record "$record" + done ''; in { networking.firewall.allowedTCPPorts = [ 80 443 ]; diff --git a/users.nix b/users.nix index 8a3a5b6..59b43f5 100644 --- a/users.nix +++ b/users.nix @@ -7,7 +7,7 @@ shell = pkgs.fish; - packages = with pkgs; [ eza ]; + packages = with pkgs; [ eza claude-code ]; hashedPassword = "$6$X.mw03yY/VFjDThj$t1I68HZz6NBihZGhiJ6Ct8ZuOufX6ZX9pydnK4puTjT1XKfMO1FY5VL1DwywJHrXOEJtohV99TmrABfjdBQY21";