diff --git a/flake.nix b/flake.nix index 314b3ba..d0d795f 100644 --- a/flake.nix +++ b/flake.nix @@ -48,6 +48,18 @@ ./modules/common/tty.nix ]; }; + + # reverse-proxy - VPS edge server with Nginx reverse proxy + reverse-proxy = nixpkgs.lib.nixosSystem { + inherit system specialArgs; + modules = [ + ./machines/reverse-proxy/configuration.nix + ./machines/reverse-proxy/gandicloud.nix + ./modules/common/nix.nix + ./modules/common/base.nix + ./modules/common/tty.nix + ]; + }; }; # Custom packages for the home lab @@ -70,6 +82,7 @@ echo "Available configurations:" echo " - congenital-optimist (Threadripper workstation)" echo " - sleeper-service (Xeon file server)" + echo " - reverse-proxy (VPS edge server)" echo "" echo "Build with: nixos-rebuild build --flake .#" echo "Switch with: nixos-rebuild switch --flake .#" diff --git a/machines/congenital-optimist/configuration.nix b/machines/congenital-optimist/configuration.nix index b511564..8b2bc00 100644 --- a/machines/congenital-optimist/configuration.nix +++ b/machines/congenital-optimist/configuration.nix @@ -9,6 +9,9 @@ ./hardware-configuration.nix ../../modules/network/network-congenital-optimist.nix + # Security modules + ../../modules/security/ssh-keys.nix + # System modules ../../modules/system/fonts.nix ../../modules/system/applications.nix diff --git a/machines/congenital-optimist/hardware-configuration.nix b/machines/congenital-optimist/hardware-configuration.nix index 19be198..bcd75a7 100644 --- a/machines/congenital-optimist/hardware-configuration.nix +++ b/machines/congenital-optimist/hardware-configuration.nix @@ -47,7 +47,7 @@ fsType = "zfs"; }; fileSystems."/mnt/storage/media" = - { device = "files:/mnt/storage"; + { device = "sleeper-service:/mnt/storage"; fsType = "nfs"; options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ]; }; diff --git a/machines/reverse-proxy/configuration.nix b/machines/reverse-proxy/configuration.nix new file mode 100644 index 0000000..7242398 --- /dev/null +++ b/machines/reverse-proxy/configuration.nix @@ -0,0 +1,92 @@ +{ pkgs, configs, lib, ... }: +let + Host = "vps1.tail807ea.ts.net"; +in +{ + imports = [ + ../../modules/common/base.nix + ../../modules/users/sma.nix + ../../modules/security/ssh-keys.nix + ]; + + environment.systemPackages = with pkgs; [ + neovim curl htop bottom fastfetch + tailscale git + ]; + + # Hostname configuration + networking.hostName = "reverse-proxy"; + + # DMZ-specific firewall configuration - very restrictive + networking.firewall = { + enable = true; + # Only allow HTTP/HTTPS from external network + allowedTCPPorts = [ 80 443 ]; + allowedUDPPorts = [ ]; + # SSH only allowed on Tailscale interface (DMZ security) + interfaces.tailscale0.allowedTCPPorts = [ 22 ]; + # Explicitly block all other traffic + rejectPackets = true; + }; + + # Security services + services.fail2ban = { + enable = true; + # Extra aggressive settings for DMZ + bantime = "24h"; + maxretry = 3; + }; + + # Tailscale for secure management access + services.tailscale.enable = true; + + # SSH configuration - ONLY accessible via Tailscale (DMZ security) + services.openssh = { + enable = true; + settings = { + PermitRootLogin = lib.mkForce "no"; + PasswordAuthentication = false; + PubkeyAuthentication = true; + AuthenticationMethods = "publickey"; + MaxAuthTries = 3; + ClientAliveInterval = 300; + ClientAliveCountMax = 2; + }; + listenAddresses = [ + { + addr = "100.96.189.104"; # Tailscale IP only + port = 22; + } + ]; + }; + + # nginx reverse proxy + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts = { + "git.geokkjer.eu" = { + addSSL = true; + enableACME = true; + locations."/".proxyPass = "http://apps:3000"; + }; + #"geokkjer.eu" = { + # default = true; + # forceSSL = true; + # enableACME = true; + # locations."/".proxyPass = "/var/wwww/homepage/"; + #}; + }; + }; + # acme let's encrypt + security.acme = { + acceptTerms = true; + defaults = { + email = "geir@geokkjer.eu"; + }; + }; +} \ No newline at end of file diff --git a/machines/reverse-proxy/gandicloud.nix b/machines/reverse-proxy/gandicloud.nix new file mode 100644 index 0000000..226ba6d --- /dev/null +++ b/machines/reverse-proxy/gandicloud.nix @@ -0,0 +1,44 @@ +# This is the configuration required to run NixOS on GandiCloud. +{ lib, modulesPath, ... }: +{ + imports = [ + (modulesPath + "/virtualisation/openstack-config.nix") + ]; + config = { + boot.initrd.kernelModules = [ + "xen-blkfront" "xen-tpmfront" "xen-kbdfront" "xen-fbfront" + "xen-netfront" "xen-pcifront" "xen-scsifront" + ]; + + # Show debug kernel message on boot then reduce loglevel once booted + boot.consoleLogLevel = 7; + boot.kernel.sysctl."kernel.printk" = "4 4 1 7"; + + # For "openstack console log show" + boot.kernelParams = [ "console=ttyS0" ]; + systemd.services."serial-getty@ttyS0" = { + enable = true; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Restart = "always"; + }; + + # The device exposed by Xen + boot.loader.grub.device = lib.mkForce "/dev/xvda"; + + # This is to get a prompt via the "openstack console url show" command + systemd.services."getty@tty1" = { + enable = lib.mkForce true; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Restart = "always"; + }; + + # This is required to get an IPv6 address on our infrastructure + networking.tempAddresses = "disabled"; + + nix.extraOptions = '' + experimental-features = nix-command flakes + ''; + + system.stateVersion = "23.05"; + }; +} \ No newline at end of file diff --git a/machines/sleeper-service/configuration.nix b/machines/sleeper-service/configuration.nix index c12f2e6..e5616e9 100644 --- a/machines/sleeper-service/configuration.nix +++ b/machines/sleeper-service/configuration.nix @@ -2,6 +2,17 @@ imports = [ ./hardware-configuration.nix ../../modules/network/network-sleeper-service.nix + + # Security modules + ../../modules/security/ssh-keys.nix + + # Services + ../../modules/services/nfs.nix + ../../modules/system/transmission.nix + + # User modules + ../../modules/users/geir.nix + ../../modules/users/sma.nix ]; # Boot configuration @@ -40,20 +51,10 @@ tree ]; - # Users - users.users.geir = { - isNormalUser = true; - extraGroups = [ "wheel" "networkmanager" ]; - shell = pkgs.zsh; - openssh.authorizedKeys.keys = [ - # Add SSH public keys here - ]; - }; - programs.zsh.enable = true; # Firewall configuration - networking.firewall.allowedTCPPorts = [ 22 ]; + networking.firewall.allowedTCPPorts = [ 22 9091 ]; # SSH and Transmission RPC system.stateVersion = "25.05"; } \ No newline at end of file diff --git a/modules/network/network-sleeper-service.nix b/modules/network/network-sleeper-service.nix index e83d3e0..9614a24 100644 --- a/modules/network/network-sleeper-service.nix +++ b/modules/network/network-sleeper-service.nix @@ -30,7 +30,10 @@ }; # Network gateway and DNS (based on nmap discovery) - defaultGateway = "10.0.0.138"; # Discovered router at lan.home + defaultGateway = { + address = "10.0.0.138"; # Discovered router at lan.home + interface = "enp0s25"; # Main ethernet interface + }; nameservers = [ "10.0.0.14" "10.0.0.138" "8.8.8.8" ]; # Pi-hole, router, Google DNS fallback # Additional firewall ports for file server services diff --git a/modules/security/ssh-keys.nix b/modules/security/ssh-keys.nix new file mode 100644 index 0000000..26f70d6 --- /dev/null +++ b/modules/security/ssh-keys.nix @@ -0,0 +1,81 @@ +# SSH Key Management Module +# Two-key strategy: admin (sma) and development (geir) +{ config, pkgs, lib, ... }: + +{ + # Global SSH daemon configuration + services.openssh = { + enable = true; + settings = { + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + PermitRootLogin = "no"; + PubkeyAuthentication = true; + }; + + # Use modern, secure algorithms only + extraConfig = '' + PubkeyAcceptedKeyTypes ssh-ed25519,ssh-ed25519-cert-v01@openssh.com + KexAlgorithms curve25519-sha256@libssh.org + Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com + MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com + ''; + }; + + # SSH client configuration + programs.ssh = { + startAgent = true; + extraConfig = '' + # Default to development key for daily use + Host * + IdentityFile ~/.ssh/id_ed25519_dev + AddKeysToAgent yes + ServerAliveInterval 60 + ServerAliveCountMax 3 + + # Admin access to servers (use sma user) + Host admin-* *.admin + User sma + IdentityFile ~/.ssh/id_ed25519_admin + + # Git services (use geir user with dev key) + Host git.* github.com gitlab.com + User git + IdentityFile ~/.ssh/id_ed25519_dev + + # Home lab servers (geir user for development access) + Host sleeper-service sleeper-service.home 10.0.0.8 + User geir + IdentityFile ~/.ssh/id_ed25519_dev + + Host grey-area grey-area.home 10.0.0.11 + User geir + IdentityFile ~/.ssh/id_ed25519_dev + + Host reverse-proxy reverse-proxy.home 10.0.0.12 + User geir + IdentityFile ~/.ssh/id_ed25519_dev + + # Admin access to servers (when needed) + Host admin-sleeper sleeper-service.admin + Hostname 10.0.0.8 + User sma + IdentityFile ~/.ssh/id_ed25519_admin + + Host admin-grey grey-area.admin + Hostname 10.0.0.11 + User sma + IdentityFile ~/.ssh/id_ed25519_admin + + Host admin-reverse reverse-proxy.admin + Hostname 10.0.0.12 + User sma + IdentityFile ~/.ssh/id_ed25519_admin + + # Tailscale network + Host 100.* *.tail* + User geir + IdentityFile ~/.ssh/id_ed25519_dev + ''; + }; +} diff --git a/modules/services/nfs.nix b/modules/services/nfs.nix new file mode 100644 index 0000000..3be6bac --- /dev/null +++ b/modules/services/nfs.nix @@ -0,0 +1,31 @@ +# NFS Server Configuration +# Network File System server for home lab storage +{ config, pkgs, ... }: + +{ + # NFS server configuration + services.nfs.server = { + enable = true; + # Export the storage directory + exports = '' + /mnt/storage 10.0.0.0/24(rw,sync,no_subtree_check,no_root_squash) + ''; + # Create exports on startup + createMountPoints = true; + }; + + # Ensure the storage directory exists + systemd.tmpfiles.rules = [ + "d /mnt/storage 0755 geir users -" + "d /mnt/storage/media 0755 geir users -" + "d /mnt/storage/downloads 0755 geir users -" + "d /mnt/storage/backups 0755 geir users -" + ]; + + # Required packages for NFS + environment.systemPackages = with pkgs; [ + nfs-utils + ]; + + # Firewall rules are already configured in network module +} diff --git a/modules/system/transmission.nix b/modules/system/transmission.nix index fe00573..329f892 100644 --- a/modules/system/transmission.nix +++ b/modules/system/transmission.nix @@ -5,16 +5,13 @@ enable = true; user = "geir"; group = "users"; - #home = "/mnt/storage/"; settings.rpc-port = 9091; settings.rpc-bind-address = "0.0.0.0"; - #openRPCPort = true; downloadDirPermissions = "770"; settings = { - download-dir = "/mnt/storage"; - #rpc-whitelist-enabled = true; + download-dir = "/mnt/storage/downloads"; rpc-whitelist = "127.0.0.1,10.0.0.*,100.*.*.*"; - rpc-host-whitelist = "congenital-optimist,localhost"; + rpc-host-whitelist = "sleeper-service,localhost"; }; }; } diff --git a/modules/users/geir.nix b/modules/users/geir.nix index f4118a9..aeba29a 100644 --- a/modules/users/geir.nix +++ b/modules/users/geir.nix @@ -21,6 +21,14 @@ shell = pkgs.zsh; + # SSH access with development keys + openssh.authorizedKeys.keys = [ + # Current key (keep for continuity during transition) + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHeOvTHIw+hZOAiWkIrz9t11UeGwxAMx7jN/1IIdgq7O geokkjer@gmail.com" + # New development key + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHukJK0Kc1YexvzF8PdqaqWNZdVffGoM6ePPMecrU6dM geir@geokkjer.eu-dev" + ]; + # User-specific packages packages = with pkgs; [ # Browsers & Communication diff --git a/modules/users/sma.nix b/modules/users/sma.nix index a5223ae..9673839 100644 --- a/modules/users/sma.nix +++ b/modules/users/sma.nix @@ -23,8 +23,8 @@ # SSH key-based authentication only (no password login) openssh.authorizedKeys.keys = [ - # Add SSH public key here when ready - # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... sma@home-lab" + # Admin key for server administration + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPgzKS1N7+7+N1/8U8++1pl4hapDm6TOy0QhrfrYA8mz geir@geokkjer.eu-admin" ]; # Essential admin packages @@ -103,9 +103,9 @@ "connections" = "ss -tuln"; # Git for infrastructure - "lab" = "cd /home/geir/Home-lab"; - "lab-status" = "cd /home/geir/Home-lab && git status"; - "lab-pull" = "cd /home/geir/Home-lab && git pull"; + "homelab" = "cd /home/geir/Home-lab"; + "homelab-status" = "cd /home/geir/Home-lab && git status"; + "homelab-pull" = "cd /home/geir/Home-lab && git pull"; # Security "audit-users" = "cat /etc/passwd | grep -E '/bin/(bash|zsh|fish)'"; diff --git a/plan.md b/plan.md index d8ec657..ee317d5 100644 --- a/plan.md +++ b/plan.md @@ -266,18 +266,19 @@ Home-lab/ - **10.0.0.8**: sleeper-service (Intel Xeon file server - rename from files.home) - **10.0.0.11**: grey-area (planned application server) - **10.0.0.12**: reverse-proxy (planned edge server) - - **10.0.0.14**: pi.hole (Pi-hole DNS/ad-blocker) - - **10.0.0.90**: wordpresserver.home (existing WordPress server) - - **10.0.0.117**: webdev.home (existing web development server) - - **10.0.0.138**: lan.home (router/gateway) + - **10.0.0.14**: pi.hole (Pi-hole DNS/ad-blocker) maybe move to nixos + - **10.0.0.90**: wordpresserver.home (existing WordPress server) to be deleted, incus container + - **10.0.0.117**: webdev.home (existing web development server) to be deleted, incus container + - **10.0.0.138**: lan.home (router/gateway/dhcp) - **Tailscale Network (100.x.x.x/10)**: - **100.109.28.53**: congenital-optimist (current machine) - **100.119.86.92**: apps (active server) (rename to grey area) - **100.114.185.71**: arlaptop (laptop) (Arch Linux with plans to migrate to NixOS) - - **100.81.15.84**: files (file server) + - **100.81.15.84**: files (file server rename to sleeper-service ) - **100.103.143.108**: pihole (DNS server) - **100.96.189.104**: vps1 (external VPS) (rename to reverse proxy) - - **100.126.202.40**: wordpresserver (WordPress) + - **100.126.202.40**: wordpresserver (WordPress) to be deleted + - remind user to update tailsce or find a way to use the cli to do this - [ ] **VLAN planning**: Consider network segmentation for different service types - [ ] **DNS configuration**: Plan local DNS resolution for internal services @@ -377,9 +378,18 @@ Home-lab/ ### 5.3 Security & Networking - [x] **systemd-networkd migration**: Completed for sleeper-service with static IP configuration +- [x] **SSH key management centralization**: Implemented two-key strategy + - **Admin key** (`geir@geokkjer.eu-admin`): For sma user, server administration access + - **Development key** (`geir@geokkjer.eu-dev`): For geir user, git services, daily development + - **NixOS module**: `modules/security/ssh-keys.nix` centralizes key management + - **SSH client config**: Updated with role-based host patterns and key selection + - **Security benefits**: Principle of least privilege, limited blast radius if compromised + - **Usage examples**: + - `ssh geir@sleeper-service.home` - Uses dev key automatically + - `ssh admin-sleeper` - Uses admin key for sma user access + - `git clone git@github.com:user/repo` - Uses dev key for git operations - [ ] VPN configuration (Tailscale expansion) - [ ] Firewall rules standardization across machines -- [ ] SSH key management centralization - [ ] Certificate management (Let's Encrypt) - [ ] Network segmentation planning (VLANs for services vs. user devices) - [ ] DNS infrastructure (local DNS server for service discovery) diff --git a/scripts/setup-ssh-keys.sh b/scripts/setup-ssh-keys.sh new file mode 100755 index 0000000..f0b830b --- /dev/null +++ b/scripts/setup-ssh-keys.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# SSH Key Generation Script - Two-Key Strategy +# Generates admin and development SSH keys + +set -euo pipefail + +SSH_DIR="$HOME/.ssh" +HOSTNAME="$(hostname)" +EMAIL_BASE="geir@geokkjer.eu" + +echo "🔑 Setting up SSH keys for $HOSTNAME" +echo "📧 Using email base: $EMAIL_BASE" + +# Create SSH directory if it doesn't exist +mkdir -p "$SSH_DIR" +chmod 700 "$SSH_DIR" + +# Backup existing key if it exists +if [[ -f "$SSH_DIR/id_ed25519" ]]; then + echo "💾 Backing up existing key to id_ed25519.backup" + cp "$SSH_DIR/id_ed25519" "$SSH_DIR/id_ed25519.backup" + cp "$SSH_DIR/id_ed25519.pub" "$SSH_DIR/id_ed25519.pub.backup" +fi + +echo "" +echo "🔐 Generating two SSH keys:" +echo " 1. Admin key (for sma user, server administration)" +echo " 2. Development key (for geir user, git, daily use)" +echo "" + +# Generate admin key +ADMIN_KEY="$SSH_DIR/id_ed25519_admin" +if [[ ! -f "$ADMIN_KEY" ]]; then + echo "🔐 Generating admin key..." + ssh-keygen -t ed25519 -f "$ADMIN_KEY" -C "$EMAIL_BASE-admin" -N "" + chmod 600 "$ADMIN_KEY" + chmod 644 "$ADMIN_KEY.pub" + echo "✅ Generated: $ADMIN_KEY" +else + echo "⏭️ Admin key already exists" +fi + +# Generate development key +DEV_KEY="$SSH_DIR/id_ed25519_dev" +if [[ ! -f "$DEV_KEY" ]]; then + echo "🔐 Generating development key..." + ssh-keygen -t ed25519 -f "$DEV_KEY" -C "$EMAIL_BASE-dev" -N "" + chmod 600 "$DEV_KEY" + chmod 644 "$DEV_KEY.pub" + echo "✅ Generated: $DEV_KEY" +else + echo "⏭️ Development key already exists" +fi + +echo "" +echo "🎯 Next steps:" +echo "1. Add these public keys to your NixOS configuration:" +echo "2. Deploy updated configuration to target servers" +echo "3. Test SSH access with both keys" +echo "4. Update external Git services with new development key" +echo "" + +echo "📋 Public keys to add to NixOS configuration:" +echo "" + +if [[ -f "$ADMIN_KEY.pub" ]]; then + echo "# Admin key (add to security.ssh-keys.admin in modules/security/ssh-keys.nix)" + echo "\"$(cat "$ADMIN_KEY.pub")\"" + echo "" +fi + +if [[ -f "$DEV_KEY.pub" ]]; then + echo "# Development key (add to security.ssh-keys.development in modules/security/ssh-keys.nix)" + echo "\"$(cat "$DEV_KEY.pub")\"" + echo "" +fi + +echo "💡 Usage examples:" +echo " ssh geir@sleeper-service.home # Uses dev key automatically" +echo " ssh admin-sleeper # Uses admin key for sma user" +echo " git clone git@github.com:user/repo # Uses dev key for git" +echo "" + +echo "🔄 To update your Git remotes with the new key:" +echo " # Add new key to GitHub/GitLab first, then:" +echo " ssh -T git@github.com # Test the connection" +echo "" + +echo "✅ SSH key setup complete!"