diff --git a/system/disko-config.nix b/extras/disko-config.nix similarity index 100% rename from system/disko-config.nix rename to extras/disko-config.nix diff --git a/home/home.nix b/home/home.nix index ffad220..00ccbeb 100644 --- a/home/home.nix +++ b/home/home.nix @@ -26,6 +26,25 @@ netcat neovim firefox - ]; + ] + ++ ( + with lib; let + # this function extracts the base file name from a path. + basename = path: lib.lists.last (lib.strings.splitString "/" (toString path)); + + files = lib.filesystem.listFilesRecursive ./scripts; + in + # for each script found, create a derivation installed in $PATH + lib.lists.forEach files ( + file: let + scriptName = strings.removeSuffix ".sh" (basename file); + in + pkgs.writeScriptBin + # (basename file) # the new package's name + scriptName + (builtins.readFile file) + ) + ); + } diff --git a/home/scripts/lxc/lab-create.sh b/home/scripts/lxc/lab-create.sh new file mode 100644 index 0000000..03591fc --- /dev/null +++ b/home/scripts/lxc/lab-create.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +USER="$1" +KEYFILE="$2" +CONTAINER="lxc-${USER}" + +echo "Creating LXC container ${CONTAINER}..." +lxc-create -n "$CONTAINER" -t download -- -d ubuntu -r noble -a amd64 + +# start it +lxc-start -n "$CONTAINER" + +# wait for networking +sleep 5 + +# set root password, install SSH, inject key +lxc-attach -n "$CONTAINER" -- bash -c " + apt-get update && apt-get install -y openssh-server + mkdir -p /root/.ssh + chmod 700 /root/.ssh + sed -i 's/#PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config + systemctl enable ssh + systemctl restart ssh +" + +# push the key in +cat "$KEYFILE" | lxc-attach -n "$CONTAINER" -- tee /root/.ssh/authorized_keys > /dev/null +lxc-attach -n "$CONTAINER" -- chmod 600 /root/.ssh/authorized_keys + +# auto-start on boot +echo "lxc.start.auto = 1" >> "/var/lib/lxc/${CONTAINER}/config" + +# get container IP +CONTAINER_IP=$(lxc-info -n "$CONTAINER" -iH | head -1) + +# create host user that maps to this container +useradd -m -s /bin/bash -G labmates "$USER" 2>/dev/null || true +mkdir -p "/home/${USER}/.ssh" +cp "$KEYFILE" "/home/${USER}/.ssh/authorized_keys" +chown -R "${USER}:${USER}" "/home/${USER}/.ssh" +chmod 700 "/home/${USER}/.ssh" + +# store mapping +echo "$CONTAINER" > "/home/${USER}/.lxc-container" + +echo "Done. ${USER} SSH -> root@${CONTAINER} (${CONTAINER_IP})" diff --git a/home/scripts/lxc/lab-delete.sh b/home/scripts/lxc/lab-delete.sh new file mode 100644 index 0000000..6ab68b7 --- /dev/null +++ b/home/scripts/lxc/lab-delete.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +USER="$1" +CONTAINER=$(cat "/home/${USER}/.lxc-container" 2>/dev/null) +# [[ -n "$CONTAINER" ]] && lxc-stop -n "$CONTAINER" 2>/dev/null; lxc-destroy -n "$CONTAINER" +if [[ -n "$CONTAINER" ]]; then + lxc-stop -n "$CONTAINER" 2>/dev/null + lxc-destroy -n "$CONTAINER" +fi +userdel -r "$USER" 2>/dev/null || true +echo "Removed ${USER} and ${CONTAINER}" diff --git a/home/scripts/lxc/lab-list.sh b/home/scripts/lxc/lab-list.sh new file mode 100644 index 0000000..e3f7510 --- /dev/null +++ b/home/scripts/lxc/lab-list.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +printf "%-15s %-20s %-16s %s\n" "USER" "CONTAINER" "IP" "STATE" +for f in /home/*/.lxc-container; do + [[ -f "$f" ]] || continue + U=$(basename "$(dirname "$f")") + C=$(cat "$f") + IP=$(lxc-info -n "$C" -iH 2>/dev/null | head -1) + STATE=$(lxc-info -n "$C" -sH 2>/dev/null) + printf "%-15s %-20s %-16s %s\n" "$U" "$C" "${IP:-n/a}" "${STATE:-n/a}" +done diff --git a/home/scripts/lxc/lxc-login.sh b/home/scripts/lxc/lxc-login.sh new file mode 100644 index 0000000..a12b23f --- /dev/null +++ b/home/scripts/lxc/lxc-login.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# CONTAINER=$(cat "/home/${USER}/.lxc-container" 2>/dev/null) +CONTAINER=$(cat "/home/$(whoami)/.lxc-container" 2>/dev/null) + +if [[ -z "$CONTAINER" ]]; then + echo "No container assigned. Contact admin." + exit 1 +fi + +# ensure running +lxc-start -n "$CONTAINER" 2>/dev/null || true + +if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then + exec lxc-attach -n "$CONTAINER" -- bash -c "$SSH_ORIGINAL_COMMAND" +else + exec lxc-attach -n "$CONTAINER" -- login -f root +fi diff --git a/hosts/s1901/hardware-configuration.nix b/hosts/s1901/hardware-configuration.nix index 0a085ba..dffaabc 100644 --- a/hosts/s1901/hardware-configuration.nix +++ b/hosts/s1901/hardware-configuration.nix @@ -13,10 +13,10 @@ boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; - # fileSystems."/" = - # { device = "/dev/disk/by-uuid/05ca9d56-3506-43c5-b9ec-be928b782996"; - # fsType = "ext4"; - # }; + fileSystems."/" = + { device = "/dev/disk/by-uuid/05ca9d56-3506-43c5-b9ec-be928b782996"; + fsType = "ext4"; + }; # fileSystems."/home/synchronous/.agenix/agenix.d" = # { device = "none"; diff --git a/system/lxc-default.conf b/system/lxc-default.conf new file mode 100644 index 0000000..48b6b92 --- /dev/null +++ b/system/lxc-default.conf @@ -0,0 +1,5 @@ +lxc.net.0.type = veth +lxc.net.0.link = br0 +lxc.net.0.flags = up +lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx +lxc.apparmor.profile = unconfined diff --git a/system/lxc.nix b/system/lxc.nix new file mode 100644 index 0000000..2bda659 --- /dev/null +++ b/system/lxc.nix @@ -0,0 +1,68 @@ +# system/lxc.nix +{ config, pkgs, ... }: { + virtualisation.lxc = { + enable = true; + lxcfs.enable = true; + }; + + networking.bridges.br0.interfaces = []; + networking.interfaces.br0.ipv4.addresses = [{ + address = "10.100.0.1"; + prefixLength = 24; + }]; + + # wildcard masquerade — no need to specify external interface per host + networking.nat.enable = false; + networking.nftables.enable = true; + networking.nftables.tables.lab-nat = { + family = "ip"; + content = '' + chain postrouting { + type nat hook postrouting priority 100; + ip saddr 10.100.0.0/24 oifname != "br0" masquerade + } + ''; + }; + + # IP forwarding + boot.kernel.sysctl."net.ipv4.ip_forward" = 1; + + # DHCP for containers on the bridge + services.dnsmasq = { + enable = true; + settings = { + interface = "br0"; + bind-interfaces = true; + dhcp-range = "10.100.0.10,10.100.0.200,24h"; + dhcp-option = [ "3,10.100.0.1" "6,8.8.8.8,1.1.1.1" ]; + }; + }; + + # labmates get dropped into their container + services.openssh.extraConfig = '' + Match Group labmates + ForceCommand /usr/local/bin/lxc-login + AllowTcpForwarding no + X11Forwarding no + ''; + + users.groups.labmates = {}; + + environment.systemPackages = with pkgs; [ + lxc + lxc-templates + debootstrap + bridge-utils + ]; + + environment.etc."lxc/default.conf".text = '' + lxc.net.0.type = veth + lxc.net.0.link = br0 + lxc.net.0.flags = up + lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx + lxc.apparmor.profile = unconfined + ''; + + environment.etc."local/bin/lxc-login".source = ../home/scripts/lxc/lxc-login.sh; + environment.etc."local/bin/lxc-login".mode = "0755"; +} diff --git a/system/system.nix b/system/system.nix index c992124..2555ef7 100644 --- a/system/system.nix +++ b/system/system.nix @@ -5,7 +5,8 @@ }: { imports = [ ./ssh.nix - ./disko-config.nix + ./lxc.nix + # ./disko-config.nix ]; # Set your time zone.