init
This commit is contained in:
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Plaintext secrets — NEVER commit
|
||||||
|
secrets.yaml
|
||||||
|
user-data.yaml
|
||||||
|
|
||||||
|
# Built ISOs
|
||||||
|
*.iso
|
||||||
|
|
||||||
|
# Editor junk
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
5
.sops.yaml
Normal file
5
.sops.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
creation_rules:
|
||||||
|
- path_regex: secrets\.sops\.yaml$
|
||||||
|
age:
|
||||||
|
- age17k7e9a8w95eu73uts6nr0fuww94kl5chrwgg0xudgmum03hv45sq9yuf4c
|
||||||
|
|
||||||
146
build-iso.sh
Normal file
146
build-iso.sh
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build-iso.sh — decrypt secrets → envsubst template → build autoinstall ISO
|
||||||
|
#
|
||||||
|
# DEPENDENCIES:
|
||||||
|
# sudo apt install sops age xorriso gettext-base
|
||||||
|
# OR macOS: brew install sops age xorriso gettext
|
||||||
|
#
|
||||||
|
# FIRST TIME SETUP:
|
||||||
|
# 1. age-keygen -o ~/.config/sops/age/keys.txt
|
||||||
|
# 2. Paste the public key (age1...) into .sops.yaml
|
||||||
|
# 3. Fill in secrets.yaml, then encrypt:
|
||||||
|
# sops -e secrets.yaml > secrets.sops.yaml && rm secrets.yaml
|
||||||
|
# 4. git add .sops.yaml secrets.sops.yaml user-data.tmpl build-iso.sh .gitignore scripts/
|
||||||
|
#
|
||||||
|
# USAGE:
|
||||||
|
# ./build-iso.sh
|
||||||
|
# ./build-iso.sh --ubuntu-iso ~/Downloads/ubuntu-24.04-live-server-amd64.iso
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
UBUNTU_ISO="${UBUNTU_ISO:-}"
|
||||||
|
UBUNTU_VERSION="24.04"
|
||||||
|
UBUNTU_ISO_URL="https://releases.ubuntu.com/${UBUNTU_VERSION}/ubuntu-${UBUNTU_VERSION}-live-server-amd64.iso"
|
||||||
|
WORK_DIR="$(mktemp -d /tmp/autoinstall-build.XXXXXX)"
|
||||||
|
OUTPUT_ISO="autoinstall-$(date +%Y%m%d-%H%M).iso"
|
||||||
|
SOPS_FILE="secrets.sops.yaml"
|
||||||
|
TEMPLATE_FILE="user-data.tmpl"
|
||||||
|
RENDERED_FILE="user-data.yaml"
|
||||||
|
POST_INSTALL_SCRIPT="scripts/post-install.sh"
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[✗]${NC} $*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--ubuntu-iso)
|
||||||
|
UBUNTU_ISO="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*) error "Unknown argument: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
info "Cleaning up..."
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
if [[ -f "$RENDERED_FILE" ]]; then
|
||||||
|
rm -f "$RENDERED_FILE"
|
||||||
|
info "Deleted plaintext $RENDERED_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
for cmd in sops envsubst xorriso; do
|
||||||
|
command -v "$cmd" &>/dev/null || error "'$cmd' not found. Install it first."
|
||||||
|
done
|
||||||
|
[[ -f "$SOPS_FILE" ]] || error "Secrets file '$SOPS_FILE' not found."
|
||||||
|
[[ -f "$TEMPLATE_FILE" ]] || error "Template '$TEMPLATE_FILE' not found."
|
||||||
|
[[ -f "$POST_INSTALL_SCRIPT" ]] || error "Post-install script '$POST_INSTALL_SCRIPT' not found."
|
||||||
|
[[ -f ".sops.yaml" ]] || error ".sops.yaml not found in current directory."
|
||||||
|
|
||||||
|
export SOPS_AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-$HOME/.config/sops/age/keys.txt}"
|
||||||
|
[[ -f "$SOPS_AGE_KEY_FILE" ]] || error "age key not found at $SOPS_AGE_KEY_FILE"
|
||||||
|
|
||||||
|
# ── Decrypt secrets → render template ─────────────────────────────────────────
|
||||||
|
info "Decrypting secrets and rendering template..."
|
||||||
|
sops exec-env "$SOPS_FILE" "envsubst < $TEMPLATE_FILE > $RENDERED_FILE"
|
||||||
|
|
||||||
|
if grep -qE '\$\{[A-Z_]+\}' "$RENDERED_FILE"; then
|
||||||
|
warn "Some variables were NOT substituted:"
|
||||||
|
grep -oE '\$\{[A-Z_]+\}' "$RENDERED_FILE" | sort -u | sed 's/^/ /'
|
||||||
|
error "Add the missing keys to secrets.yaml and re-encrypt."
|
||||||
|
fi
|
||||||
|
info "Template rendered."
|
||||||
|
|
||||||
|
# ── Get Ubuntu ISO ─────────────────────────────────────────────────────────────
|
||||||
|
if [[ -z "$UBUNTU_ISO" ]]; then
|
||||||
|
UBUNTU_ISO="ubuntu-${UBUNTU_VERSION}-live-server-amd64.iso"
|
||||||
|
if [[ ! -f "$UBUNTU_ISO" ]]; then
|
||||||
|
info "Downloading Ubuntu ${UBUNTU_VERSION} server ISO..."
|
||||||
|
curl -L --progress-bar -o "$UBUNTU_ISO" "$UBUNTU_ISO_URL"
|
||||||
|
else
|
||||||
|
info "Found cached ISO: $UBUNTU_ISO"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
[[ -f "$UBUNTU_ISO" ]] || error "ISO not found: $UBUNTU_ISO"
|
||||||
|
|
||||||
|
# ── Extract ISO ────────────────────────────────────────────────────────────────
|
||||||
|
info "Extracting ISO..."
|
||||||
|
xorriso -osirrox on -indev "$UBUNTU_ISO" -extract / "$WORK_DIR/iso" 2>/dev/null
|
||||||
|
chmod -R u+w "$WORK_DIR/iso"
|
||||||
|
|
||||||
|
# ── Inject autoinstall files ───────────────────────────────────────────────────
|
||||||
|
info "Injecting autoinstall config and post-install script..."
|
||||||
|
NOCLOUD_DIR="$WORK_DIR/iso/nocloud"
|
||||||
|
mkdir -p "$NOCLOUD_DIR"
|
||||||
|
cp "$RENDERED_FILE" "$NOCLOUD_DIR/user-data"
|
||||||
|
cp "$POST_INSTALL_SCRIPT" "$NOCLOUD_DIR/post-install.sh"
|
||||||
|
touch "$NOCLOUD_DIR/meta-data"
|
||||||
|
|
||||||
|
# The installer runs in a live env where the ISO is mounted at /cdrom.
|
||||||
|
# post-install.sh lands at /cdrom/nocloud/post-install.sh but curtin in-target
|
||||||
|
# runs inside the installed system chroot, so we copy it to /target first.
|
||||||
|
# We add a prep command to late-commands via a second write — easier to just
|
||||||
|
# add a copy step before the main script call in user-data. We handle it here
|
||||||
|
# by prepending a cp command as an additional late-command note:
|
||||||
|
# (already handled in user-data.tmpl with: curtin in-target -- bash /post-install.sh
|
||||||
|
# which works because the file is written to /target by the nocloud datasource)
|
||||||
|
|
||||||
|
# ── Patch GRUB ────────────────────────────────────────────────────────────────
|
||||||
|
GRUB_CFG="$WORK_DIR/iso/boot/grub/grub.cfg"
|
||||||
|
if [[ -f "$GRUB_CFG" ]]; then
|
||||||
|
info "Patching GRUB for unattended boot..."
|
||||||
|
sed -i 's|linux\s*/casper/vmlinuz\(.*\)|linux /casper/vmlinuz\1 autoinstall ds=nocloud;s=/cdrom/nocloud/|' "$GRUB_CFG"
|
||||||
|
sed -i 's/set timeout=.*/set timeout=0/' "$GRUB_CFG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Repack ISO ─────────────────────────────────────────────────────────────────
|
||||||
|
info "Repacking ISO → $OUTPUT_ISO ..."
|
||||||
|
xorriso -as mkisofs \
|
||||||
|
-r -V "Ubuntu-AutoInstall" -o "$OUTPUT_ISO" \
|
||||||
|
-J -joliet-long \
|
||||||
|
-b boot/grub/i386-pc/eltorito.img -c boot.catalog \
|
||||||
|
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||||
|
--grub2-boot-info \
|
||||||
|
--grub2-mbr "$WORK_DIR/iso/boot/grub/i386-pc/boot_hybrid.img" \
|
||||||
|
-eltorito-alt-boot \
|
||||||
|
-e --interval:appended_partition_2:all:: -no-emul-boot \
|
||||||
|
-append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b "$WORK_DIR/iso/boot/grub/efi.img" \
|
||||||
|
-iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
|
||||||
|
"$WORK_DIR/iso" 2>/dev/null
|
||||||
|
|
||||||
|
info "Done! ✓"
|
||||||
|
echo ""
|
||||||
|
echo -e " Output: ${GREEN}${OUTPUT_ISO}${NC}"
|
||||||
|
echo -e " Flash: sudo dd if=${OUTPUT_ISO} of=/dev/sdX bs=4M status=progress oflag=sync"
|
||||||
|
echo ""
|
||||||
|
warn "Plaintext user-data.yaml deleted. secrets.yaml never hit disk."
|
||||||
75
scripts/post-install.sh
Executable file
75
scripts/post-install.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# scripts/post-install.sh
|
||||||
|
# Runs inside the installed system after base Ubuntu install.
|
||||||
|
# Called by autoinstall late-commands as: curtin in-target -- bash /post-install.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[✗]${NC} $*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── DOCKER ──────────────────────────────────────────────────────────────────
|
||||||
|
info "Installing Docker..."
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg |
|
||||||
|
gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
|
||||||
|
https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||||
|
>/etc/apt/sources.list.d/docker.list
|
||||||
|
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
|
||||||
|
usermod -aG docker alfoldi
|
||||||
|
systemctl enable docker
|
||||||
|
info "Docker installed."
|
||||||
|
|
||||||
|
# ─── HIMMELBLAU (Azure Entra ID) ─────────────────────────────────────────────
|
||||||
|
info "Installing Himmelblau..."
|
||||||
|
curl -fsSL https://packages.himmelblau-idm.org/stable/himmelblau.list |
|
||||||
|
tee /etc/apt/sources.list.d/himmelblau.list
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y himmelblau himmelblau-sshd-config
|
||||||
|
info "Himmelblau installed."
|
||||||
|
|
||||||
|
# ─── NIX + HOME-MANAGER ──────────────────────────────────────────────────────
|
||||||
|
info "Installing Nix (single-user) for alfoldi..."
|
||||||
|
sudo -u alfoldi bash -c \
|
||||||
|
"curl -L https://nixos.org/nix/install | sh -s -- --no-daemon"
|
||||||
|
|
||||||
|
info "Adding home-manager channel..."
|
||||||
|
sudo -u alfoldi bash -c "
|
||||||
|
source /home/alfoldi/.nix-profile/etc/profile.d/nix.sh
|
||||||
|
nix-channel --add https://github.com/nix-community/home-manager/archive/release-24.05.tar.gz home-manager
|
||||||
|
nix-channel --update
|
||||||
|
"
|
||||||
|
|
||||||
|
info "Installing home-manager..."
|
||||||
|
sudo -u alfoldi bash -c "
|
||||||
|
source /home/alfoldi/.nix-profile/etc/profile.d/nix.sh
|
||||||
|
nix-shell '<home-manager>' -A install
|
||||||
|
"
|
||||||
|
info "Nix + home-manager installed."
|
||||||
|
|
||||||
|
# ─── SSH ─────────────────────────────────────────────────────────────────────
|
||||||
|
info "Enabling SSH..."
|
||||||
|
systemctl enable ssh
|
||||||
|
|
||||||
|
# ─── SUDO (passwordless for alfoldi) ─────────────────────────────────────────
|
||||||
|
info "Configuring sudoers..."
|
||||||
|
echo 'alfoldi ALL=(ALL) NOPASSWD:ALL' >/etc/sudoers.d/alfoldi
|
||||||
|
chmod 440 /etc/sudoers.d/alfoldi
|
||||||
|
|
||||||
|
info "Post-install complete ✓"
|
||||||
102
user-data.tmpl
Normal file
102
user-data.tmpl
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# vim: set filetype=yaml :
|
||||||
|
|
||||||
|
#cloud-config
|
||||||
|
# user-data.tmpl — plaintext template, safe to commit to git
|
||||||
|
# Secrets are injected at build time via envsubst from secrets.sops.yaml
|
||||||
|
autoinstall:
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
# ─── LOCALE & KEYBOARD ─────────────────────────────────────────────────────
|
||||||
|
locale: en_US.UTF-8
|
||||||
|
keyboard:
|
||||||
|
layout: us
|
||||||
|
|
||||||
|
# ─── NETWORK ───────────────────────────────────────────────────────────────
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
any-eth:
|
||||||
|
match:
|
||||||
|
name: "en*"
|
||||||
|
dhcp4: true
|
||||||
|
wifis:
|
||||||
|
wlp0s20f3:
|
||||||
|
dhcp4: true
|
||||||
|
access-points:
|
||||||
|
"house":
|
||||||
|
password: "${WIFI_HOUSE_PASSWORD}"
|
||||||
|
"house5":
|
||||||
|
password: "${WIFI_HOUSE_PASSWORD}"
|
||||||
|
"NOKIA":
|
||||||
|
auth:
|
||||||
|
key-management: eap
|
||||||
|
eap-method: tls
|
||||||
|
identity: "host/alfoldi.ipa.nsn-net.net"
|
||||||
|
ca-certificate: /nokia/vpn/NOKIA_Root_CA.crt
|
||||||
|
client-certificate: /nokia/vpn/alfoldi.ipa.nsn-net.net.crt
|
||||||
|
client-key: /nokia/vpn/alfoldi.ipa.nsn-net.net.key
|
||||||
|
client-key-password: "${NOKIA_WIFI_KEY_PASSWORD}"
|
||||||
|
|
||||||
|
# ─── DISK LAYOUT: LVM on LUKS ──────────────────────────────────────────────
|
||||||
|
storage:
|
||||||
|
layout:
|
||||||
|
name: lvm
|
||||||
|
match:
|
||||||
|
path: /dev/ nvme0n1
|
||||||
|
sizing-policy: all
|
||||||
|
encrypted: true
|
||||||
|
password: "${LUKS_PASSPHRASE}"
|
||||||
|
|
||||||
|
# ─── IDENTITY ──────────────────────────────────────────────────────────────
|
||||||
|
identity:
|
||||||
|
hostname: nokia
|
||||||
|
username: alfoldi
|
||||||
|
password: "${USER_PASSWORD_HASH}"
|
||||||
|
|
||||||
|
# ─── SSH ───────────────────────────────────────────────────────────────────
|
||||||
|
ssh:
|
||||||
|
install-server: true
|
||||||
|
allow-pw: false
|
||||||
|
authorized-keys:
|
||||||
|
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICgcJfi0dZotMWa8zQvxXduM76GmQfoPvMU5FjIFZCAa alfonzso@gmail.com"
|
||||||
|
|
||||||
|
# ─── CERT FILES ────────────────────────────────────────────────────────────
|
||||||
|
write_files:
|
||||||
|
- path: /nokia/vpn/NOKIA_Root_CA.crt
|
||||||
|
permissions: '0600'
|
||||||
|
owner: root:root
|
||||||
|
encoding: b64
|
||||||
|
content: "${NOKIA_CA_CERT_B64}"
|
||||||
|
|
||||||
|
- path: /nokia/vpn/alfoldi.ipa.nsn-net.net.crt
|
||||||
|
permissions: '0600'
|
||||||
|
owner: root:root
|
||||||
|
encoding: b64
|
||||||
|
content: "${NOKIA_CLIENT_CERT_B64}"
|
||||||
|
|
||||||
|
- path: /nokia/vpn/alfoldi.ipa.nsn-net.net.key
|
||||||
|
permissions: '0600'
|
||||||
|
owner: root:root
|
||||||
|
encoding: b64
|
||||||
|
content: "${NOKIA_CLIENT_KEY_B64}"
|
||||||
|
|
||||||
|
# ─── PACKAGES ──────────────────────────────────────────────────────────────
|
||||||
|
packages:
|
||||||
|
- git
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- vim
|
||||||
|
- build-essential
|
||||||
|
- python3
|
||||||
|
- python3-pip
|
||||||
|
- python3-venv
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
- lsb-release
|
||||||
|
|
||||||
|
late-commands:
|
||||||
|
- curtin in-target -- bash /post-install.sh
|
||||||
|
|
||||||
|
updates: security
|
||||||
|
shutdown: reboot
|
||||||
Reference in New Issue
Block a user