From eded7180dc8897b3e5147423abf581168c8ca57e Mon Sep 17 00:00:00 2001 From: phg Date: Mon, 11 May 2026 19:13:11 +0200 Subject: [PATCH] Refactor Debian 13 Trixie Packer templates for LUKS support - Removed obsolete variable files: variables-common.pkr.hcl and variables.pkr.hcl. - Updated debian-trixie.pkr.hcl to include local values for LUKS configuration. - Modified boot command to include LUKS arguments based on the enable_luks variable. - Enhanced initial-setup.sh to support LUKS detection and resizing. - Replaced preseed.cfg with preseed.cfg.pkrtpl for dynamic LUKS configuration. - Added enable_luks variable to control LUKS encryption during image build. - Introduced luks.pkrvars.hcl for LUKS-specific variable settings. - Updated mise.toml to support new variable file argument for Packer builds. --- README.md | 35 +- _scripts/unlock-luks-after-install.py | 55 +- .../credentials.auto.pkrvars.hcl | 1 - debian/13-trixie-luks/debian-trixie.pkr.hcl | 223 ----- .../files/90-initial-login-setup.sh | 25 - debian/13-trixie-luks/files/99-pve.cfg | 1 - debian/13-trixie-luks/files/debian.sources | 17 - debian/13-trixie-luks/files/initial-setup.sh | 766 ------------------ .../scripts/crowdsec-configuration.sh | 8 - .../scripts/crowdsec-repo-setup.sh | 370 --------- debian/13-trixie-luks/scripts/tailscale.sh | 726 ----------------- .../13-trixie-luks/variables-common.pkr.hcl | 1 - debian/13-trixie-luks/variables.pkr.hcl | 67 -- debian/13-trixie/debian-trixie.pkr.hcl | 59 +- debian/13-trixie/files/initial-setup.sh | 179 +++- debian/13-trixie/http/preseed.cfg | 161 ---- .../http/preseed.cfg.pkrtpl} | 13 +- debian/13-trixie/variables.pkr.hcl | 6 +- debian/13-trixie/variants/luks.pkrvars.hcl | 2 + mise.toml | 11 +- 20 files changed, 281 insertions(+), 2445 deletions(-) delete mode 120000 debian/13-trixie-luks/credentials.auto.pkrvars.hcl delete mode 100644 debian/13-trixie-luks/debian-trixie.pkr.hcl delete mode 100644 debian/13-trixie-luks/files/90-initial-login-setup.sh delete mode 100644 debian/13-trixie-luks/files/99-pve.cfg delete mode 100644 debian/13-trixie-luks/files/debian.sources delete mode 100644 debian/13-trixie-luks/files/initial-setup.sh delete mode 100644 debian/13-trixie-luks/scripts/crowdsec-configuration.sh delete mode 100644 debian/13-trixie-luks/scripts/crowdsec-repo-setup.sh delete mode 100644 debian/13-trixie-luks/scripts/tailscale.sh delete mode 120000 debian/13-trixie-luks/variables-common.pkr.hcl delete mode 100644 debian/13-trixie-luks/variables.pkr.hcl delete mode 100644 debian/13-trixie/http/preseed.cfg rename debian/{13-trixie-luks/http/preseed.cfg => 13-trixie/http/preseed.cfg.pkrtpl} (94%) create mode 100644 debian/13-trixie/variants/luks.pkrvars.hcl diff --git a/README.md b/README.md index 3c7a2f4..13b8302 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ | Template ID | OS | Version | Path | LUKS encrypted? | Mac Address | IP Address | | :---------- | :--------------------------------------------------------------------------------------------- | :-------- | :---------------------- | :-------------- | :---------------- | :---------------------- | -| 65000 | Debian logo Debian | 13-trixie | `debian/13-trixie-luks` | ✅ | BC:24:11:00:13:37 | 192.168.9.29 (via DHCP) | +| 65000 | Debian logo Debian | 13-trixie | `debian/13-trixie` | optional | BC:24:11:00:13:37 | 192.168.9.29 (via DHCP) | ## Repository structure @@ -28,14 +28,14 @@ Name ├──  _scripts/ Support scripts for building templates. │ └──  unlock-luks-after-install.py* Unlocks the LUKS encrypted Disk on the 1st Boot after installation. ├──  debian/ Debian template definitions and assets (Packer templates, cloud-init/KS files, provisioning files). -│ └──  13-trixie-luks/ Template definition and assets for the Trixie template. +│ └──  13-trixie/ Template definition and assets for the Trixie template. │ ├──  files/ Files used for the file provisioner. │ │ ├── 󱁻 99-pve.cfg Configures the data sources for cloud-init. │ │ └──  debian.sources Debian package sources. │ ├──  http/ Files that Packer provides during build via http. -│ │ ├── 󱁻 ks.cfg Kickstart configuration. -│ │ ├── 󰡯 meta-data cloud-init configuration. -│ │ └── 󰡯 user-data cloud-init configuration. +│ │ └── 󱁻 preseed.cfg.pkrtpl Debian preseed template. +│ ├──  variants/ Packer var-files for optional image variants. +│ │ └──  luks.pkrvars.hcl Enables LUKS encryption. │ ├──  credentials.auto.pkrvars.hcl -> ../../credentials.auto.pkrvars.hcl Local secrets for Packer (API token, endpoints) used at build time. │ ├──  debian-trixie.pkr.hcl The build template. │ ├──  variables-common.pkr.hcl -> ../../variables-common.pkr.hcl Shared Packer variables used by templates. @@ -88,12 +88,27 @@ To build a template run: mise run build ``` -## Build LUKS encrypted Templates - -To build a template run: +For Debian 13 Trixie without LUKS: ```shell -mise run build-luks +mise run build debian/13-trixie +``` + +## Build LUKS encrypted Templates + +Debian LUKS builds use the same template directory and enable encryption through +`debian/13-trixie/variants/luks.pkrvars.hcl`. + +To build a LUKS encrypted Debian 13 Trixie template run: + +```shell +mise run build-luks debian/13-trixie +``` + +The equivalent direct Packer command is: + +```shell +packer build -var-file=debian/13-trixie/variants/luks.pkrvars.hcl debian/13-trixie ``` ## Setup new templates @@ -104,4 +119,4 @@ Run: mise run setup ``` -E.g. `mise run setup debian 13-trixie` or `mise run setup debian 13-trixie-luks`. +E.g. `mise run setup debian 13-trixie`. diff --git a/_scripts/unlock-luks-after-install.py b/_scripts/unlock-luks-after-install.py index d544da0..04b0c13 100755 --- a/_scripts/unlock-luks-after-install.py +++ b/_scripts/unlock-luks-after-install.py @@ -32,11 +32,24 @@ def load_hcl(path: Path) -> dict: return hcl2.load(handle) -def get_variable_default(hcl_data: dict, name: str) -> str | None: - for variable_block in hcl_data.get("variable", []): - if name in variable_block: - return variable_block[name].get("default") - return None +def resolve_input_path(path: str, script_root: Path) -> Path: + resolved = Path(path) + if not resolved.is_absolute(): + resolved = script_root / resolved + return resolved + + +def merge_values(*hcl_data_items: dict) -> dict: + values = {} + for hcl_data in hcl_data_items: + for variable_block in hcl_data.get("variable", []): + for name, body in variable_block.items(): + if isinstance(body, dict) and "default" in body: + values[name] = body["default"] + for name, value in hcl_data.items(): + if name != "variable": + values[name] = value + return values def main() -> int: @@ -55,29 +68,32 @@ def main() -> int: default=45, help="Seconds to wait before sending the LUKS password (default: 45).", ) + parser.add_argument( + "--var-file", + action="append", + default=[], + help="Path to an HCL var-file passed through to the Packer build.", + ) args = parser.parse_args() script_root = Path(__file__).resolve().parents[1] variables_common_path = script_root / "variables-common.pkr.hcl" credentials_path = script_root / "credentials.auto.pkrvars.hcl" - vars_dir = Path(args.template) - if not vars_dir.is_absolute(): - vars_dir = script_root / vars_dir + vars_dir = resolve_input_path(args.template, script_root) variables_path = vars_dir / "variables.pkr.hcl" + var_file_paths = [resolve_input_path(var_file, script_root) for var_file in args.var_file] variables_common = load_hcl(variables_common_path) credentials = load_hcl(credentials_path) variables = load_hcl(variables_path) + var_files = [load_hcl(var_file_path) for var_file_path in var_file_paths] + values = merge_values(variables_common, variables, credentials, *var_files) - proxmox_api_url = get_variable_default(variables_common, "proxmox_api_url") - proxmox_skip_tls_verify = ( - get_variable_default(variables_common, "proxmox_skip_tls_verify") or False - ) - default_luks_passphrase = get_variable_default( - variables_common, "default_luks_passphrase" - ) - proxmox_node = get_variable_default(variables, "proxmox_node") - template_vm_id = get_variable_default(variables, "template_vm_id") + proxmox_api_url = values.get("proxmox_api_url") + proxmox_skip_tls_verify = values.get("proxmox_skip_tls_verify", False) + default_luks_passphrase = values.get("default_luks_passphrase") + proxmox_node = values.get("proxmox_node") + template_vm_id = values.get("template_vm_id") _ = ( proxmox_api_url, @@ -149,7 +165,10 @@ def main() -> int: log(f"Listening for POST on /install_finished at port {port}") - build_cmd = ["mise", "build", args.template, "-i", str(port)] + build_cmd = ["mise", "run", "build", args.template] + for var_file_path in var_file_paths: + build_cmd.extend(["--var-file", str(var_file_path)]) + build_cmd.extend(["-i", str(port)]) build_proc = subprocess.Popen( build_cmd, stdout=subprocess.PIPE, diff --git a/debian/13-trixie-luks/credentials.auto.pkrvars.hcl b/debian/13-trixie-luks/credentials.auto.pkrvars.hcl deleted file mode 120000 index 2704ed7..0000000 --- a/debian/13-trixie-luks/credentials.auto.pkrvars.hcl +++ /dev/null @@ -1 +0,0 @@ -../../credentials.auto.pkrvars.hcl \ No newline at end of file diff --git a/debian/13-trixie-luks/debian-trixie.pkr.hcl b/debian/13-trixie-luks/debian-trixie.pkr.hcl deleted file mode 100644 index a4fa2ae..0000000 --- a/debian/13-trixie-luks/debian-trixie.pkr.hcl +++ /dev/null @@ -1,223 +0,0 @@ -packer { - required_plugins { - proxmox = { - version = "~> 1" - source = "github.com/hashicorp/proxmox" - } - } -} - -source "proxmox-iso" "debian-13-trixie-luks" { - # Proxmox Connection Settings - proxmox_url = "${var.proxmox_api_url}" - username = "${var.proxmox_api_token_id}" - token = "${var.proxmox_api_token_secret}" - - # Skip TLS Verification - insecure_skip_tls_verify = "${var.proxmox_skip_tls_verify}" - - # VM General Settings - node = "${var.proxmox_node}" - vm_id = "${var.template_vm_id}" - vm_name = "debian-13-trixie-luks-${local.timestamp}" - template_description = "Debian 13 Trixie, LUKS encrypted, built with Packer on ${local.timestamp}\n\nLUKS default passphrase: `${var.default_luks_passphrase}`" - os = "l26" - qemu_agent = true - - # VM Hardware Settings - machine = "q35" - cpu_type = "${var.template_cpu_type}" - cores = 2 - memory = 2048 - ballooning_minimum = 2048 - bios = "ovmf" - scsi_controller = "virtio-scsi-single" - disks { - disk_size = "30G" - format = "qcow2" - storage_pool = "${var.disk_storage_pool}" - type = "scsi" # VirtIO-SCSI better maintained as virtio-blk has been deprecated in Proxmox VE 7.4+ and may cause issues with newer Linux kernels - io_thread = true - ssd = true - } - - efi_config { - efi_storage_pool = "${var.disk_storage_pool}" - pre_enrolled_keys = true - efi_format = "raw" - efi_type = "4m" - } - - serials = [ - "socket" - ] - - # Download ISO - boot_iso { - type = "scsi" - iso_url = "${var.iso_url}" - unmount = true - iso_storage_pool = "${var.iso_storage_pool}" - iso_checksum = "${var.iso_checksum}" - } - - # VM Network Settings - network_adapters { - model = "virtio" - mac_address = "${var.mac_address}" - bridge = "${var.network_bridge}" - firewall = "true" - } - - # VM Cloud-Init Settings - cloud_init = true - cloud_init_storage_pool = "${var.disk_storage_pool}" - cloud_init_disk_type = "scsi" - - # PACKER Boot Commands - boot = "order=scsi0;scsi1" - boot_wait = "10s" - communicator = "ssh" - boot_command = [ - "c", - "linux /install.amd/vmlinuz auto-install/enable=true priority=critical ", - "DEBIAN_FRONTEND=text ", - "console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0 ", - "passwd/root-password='${var.default_root_passphrase}' ", - "passwd/root-password-again='${var.default_root_passphrase}' ", - "partman-crypto/passphrase='${var.default_luks_passphrase}' ", - "partman-crypto/passphrase-again='${var.default_luks_passphrase}' ", - "INSTALL_FINISHED_INFORM_URL='http://{{ .HTTPIP }}:${var.install_finished_inform_port}/install_finished' ", - "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg noprompt", - "initrd /install.amd/initrd.gz", - "DEBCONF_DEBUG=5", - "boot" - ] - - # PACKER Autoinstall Settings - http_directory = "debian/13-trixie-luks/http" - http_interface = "${var.source_proxmox_http_interface}" - - # SSH Settings - ssh_username = "root" - ssh_password = "${var.default_root_passphrase}" - ssh_timeout = "20m" - ssh_pty = true -} - -build { - name = "debian-13-trixie-luks-image" - sources = ["source.proxmox-iso.debian-13-trixie-luks"] - - # Install dependencies and default packages - provisioner "shell" { - inline = [ - "export DEBIAN_FRONTEND=noninteractive", - "apt-get update", - "apt-get install -y age apt-transport-https aria2 bat bc bmon btop ca-certificates curl duf eza fastfetch fzf gdisk git gnupg htop iftop iotop iperf jq lsof magic-wormhole mosh mtr ncdu parted progress pv ripgrep rsync smartmontools socat sudo tmux usbutils vim wget yq zsh zstd" - ] - } - - # Install Tailscale - provisioner "shell" { - script = "debian/13-trixie-luks/scripts/tailscale.sh" - } - - # Setup CrowdSec Repo - provisioner "shell" { - script = "debian/13-trixie-luks/scripts/crowdsec-repo-setup.sh" - } - - # Install CrowdSec - provisioner "shell" { - inline = [ - "apt-get install -y crowdsec", - "apt-get install -y crowdsec-firewall-bouncer-iptables" - ] - } - - # Configure CrowdSec - provisioner "shell" { - script = "debian/13-trixie-luks/scripts/crowdsec-configuration.sh" - } - - # Provisioning the VM Template for Cloud-Init Integration in Proxmox #2 - provisioner "file" { - source = "debian/13-trixie-luks/files/99-pve.cfg" - destination = "/tmp/99-pve.cfg" - } - - # Provisioning the VM Template for Cloud-Init Integration in Proxmox #3 - provisioner "shell" { - inline = ["sudo cp /tmp/99-pve.cfg /etc/cloud/cloud.cfg.d/99-pve.cfg"] - } - - # Remove CD-ROM entries from APT sources list - provisioner "shell" { - inline = ["sed -i '/cdrom/d' /etc/apt/sources.list"] - } - - # Add custom APT sources list - provisioner "file" { - source = "debian/13-trixie-luks/files/debian.sources" - destination = "/etc/apt/sources.list.d/debian.sources" - } - - provisioner "file" { - source = "debian/13-trixie-luks/files/90-initial-login-setup.sh" - destination = "/etc/profile.d/90-initial-login-setup.sh" - } - - provisioner "file" { - source = "debian/13-trixie-luks/files/initial-setup.sh" - destination = "/usr/local/bin/initial-setup.sh" - } - - provisioner "shell" { - inline = [ - "chmod +x /usr/local/bin/initial-setup.sh" - ] - } - - # Install Clevis - provisioner "shell" { - inline = [ - "apt-get update", - "apt-get install -y clevis clevis-luks clevis-initramfs" - ] - } - - # Setup Serial Console for xterm.js in Proxmox VE - provisioner "shell" { - inline = [ - "sed -i 's/#\\?GRUB_CMDLINE_LINUX=.*\"/GRUB_CMDLINE_LINUX=\"console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0\"/' /etc/default/grub", - "sed -i 's/#\\?GRUB_TERMINAL=.*/GRUB_TERMINAL=\"serial console\"/' /etc/default/grub", - "sed -i 's/#\\?GRUB_SERIAL_COMMAND=.*/GRUB_SERIAL_COMMAND=\"serial --speed=115200\"/' /etc/default/grub", - "update-grub" - ] - } - - # Provisioning the VM Template for Cloud-Init Integration in Proxmox #1 - provisioner "shell" { - inline = [ - "rm /etc/ssh/ssh_host_*", - "truncate -s 0 /etc/machine-id", - "apt -y autoremove --purge 2> /dev/null", - "apt -y clean 2> /dev/null", - "apt -y autoclean 2> /dev/null", - "rm -rf /var/cache/apt/archives /var/lib/apt/lists/*", - "cloud-init clean", - "rm -f /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg", - "sync" - ] - } - - # Remove temporary settings and configuration for packer build - provisioner "shell" { - inline = [ - "sed -i 's/^#\\?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config", - "sed -i 's/^#\\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config", - "passwd -dl root" - ] - } -} diff --git a/debian/13-trixie-luks/files/90-initial-login-setup.sh b/debian/13-trixie-luks/files/90-initial-login-setup.sh deleted file mode 100644 index 0ec65be..0000000 --- a/debian/13-trixie-luks/files/90-initial-login-setup.sh +++ /dev/null @@ -1,25 +0,0 @@ -#! /bin/sed 2,5!d;s/^#.// -# This script must be sourced from within a shell -# and not executed. For instance with: -# -# . /usr/local/bin/initial-setup.sh - -# Only run in interactive shells -case $- in - *i*) ;; - *) return ;; -esac - -if [ "$EUID" -ne 0 ]; then - if ! command -v sudo >/dev/null 2>&1 || ! sudo -n true >/dev/null 2>&1; then - echo "Error: must be root or have sudo privileges to run initial login setup." >&2 - return - fi -fi - -SENTINEL="/var/lib/initial-login-setup.done" - -if [ ! -f "$SENTINEL" ] && [ -x /usr/local/bin/initial-setup.sh ]; then - sudo touch "$SENTINEL" - sudo /usr/local/bin/initial-setup.sh -fi diff --git a/debian/13-trixie-luks/files/99-pve.cfg b/debian/13-trixie-luks/files/99-pve.cfg deleted file mode 100644 index a78672c..0000000 --- a/debian/13-trixie-luks/files/99-pve.cfg +++ /dev/null @@ -1 +0,0 @@ -datasource_list: [ConfigDrive, NoCloud] \ No newline at end of file diff --git a/debian/13-trixie-luks/files/debian.sources b/debian/13-trixie-luks/files/debian.sources deleted file mode 100644 index 95cb2db..0000000 --- a/debian/13-trixie-luks/files/debian.sources +++ /dev/null @@ -1,17 +0,0 @@ -Types: deb -URIs: http://ftp.de.debian.org/debian/ -Suites: trixie -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg - -Types: deb -URIs: http://ftp.de.debian.org/debian/ -Suites: trixie-updates -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg - -Types: deb -URIs: http://security.debian.org/ -Suites: trixie-security -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg diff --git a/debian/13-trixie-luks/files/initial-setup.sh b/debian/13-trixie-luks/files/initial-setup.sh deleted file mode 100644 index 1a2c426..0000000 --- a/debian/13-trixie-luks/files/initial-setup.sh +++ /dev/null @@ -1,766 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -SCRIPT_NAME="$(basename "$0")" -TASK_INDEX=0 -TASK_TOTAL=0 -TEMP_FILES=() -STORAGE_DETECTED=0 - -ROOT_SOURCE="" -VG_NAME="" -LV_NAME="" -PV_NAME="" -LUKS_DEV="" -LUKS_NAME="" -LUKS_PART="" -DISK_DEV="" -PART_NUM="" - -if [ -t 1 ]; then - BOLD="$(tput bold 2>/dev/null || true)" - DIM="$(tput dim 2>/dev/null || true)" - RED="$(tput setaf 1 2>/dev/null || true)" - GREEN="$(tput setaf 2 2>/dev/null || true)" - YELLOW="$(tput setaf 3 2>/dev/null || true)" - BLUE="$(tput setaf 4 2>/dev/null || true)" - RESET="$(tput sgr0 2>/dev/null || true)" -else - BOLD="" - DIM="" - RED="" - GREEN="" - YELLOW="" - BLUE="" - RESET="" -fi - -die() { - echo "${RED}Error:${RESET} $*" >&2 - exit 1 -} - -log_info() { - echo "${BLUE}INFO:${RESET} $*" -} - -log_ok() { - echo "${GREEN}OK:${RESET} $*" -} - -log_warn() { - echo "${YELLOW}WARN:${RESET} $*" -} - -section() { - local title="$1" - echo - echo "${BOLD}${title}${RESET}" - echo "${DIM}------------------------------------------------------------${RESET}" -} - -add_temp_file() { - TEMP_FILES+=("$1") -} - -cleanup() { - local file - for file in "${TEMP_FILES[@]:-}"; do - [ -f "$file" ] && rm -f "$file" - done -} -trap cleanup EXIT - -ensure_tty() { - if [ ! -t 0 ] || [ ! -t 1 ]; then - die "This setup must run interactively in a TTY." - fi -} - -ensure_root() { - if [ "${EUID:-$(id -u)}" -ne 0 ]; then - if command -v sudo >/dev/null 2>&1; then - log_info "Re-running with sudo..." - exec sudo -E "$0" "$@" - fi - die "Must be root or have sudo privileges to run this setup." - fi -} - -require_cmd() { - local cmd="$1" - if ! command -v "$cmd" >/dev/null 2>&1; then - log_warn "Missing command: $cmd" - return 1 - fi - return 0 -} - -prompt_input() { - local label="$1" - local default="${2:-}" - local value="" - if [ -n "$default" ]; then - read -r -p "${label} [${default}]: " value /dev/tty - if [ -n "$value" ]; then - printf '%s' "$value" - return 0 - fi - log_warn "Value cannot be empty." - done -} - -prompt_secret_confirm() { - local label="$1" - local confirm_label="$2" - local a="" - local b="" - while true; do - read -r -s -p "${label}: " a /dev/tty - read -r -s -p "${confirm_label}: " b /dev/tty - if [ -z "$a" ]; then - log_warn "Value cannot be empty." - continue - fi - if [ "$a" != "$b" ]; then - log_warn "Values do not match. Please try again." - continue - fi - printf '%s' "$a" - return 0 - done -} - -confirm() { - local label="$1" - local default="${2:-yes}" - local prompt="" - local answer="" - - if [ "$default" = "yes" ]; then - prompt="[Y/n]" - else - prompt="[y/N]" - fi - - while true; do - read -r -p "${label} ${prompt} " answer /dev/null 2>&1; then - numfmt --to=iec --suffix=B "$bytes" - else - awk -v b="$bytes" 'BEGIN { - split("B KiB MiB GiB TiB", u, " "); - i=1; - while (b>=1024 && i<5) { b/=1024; i++; } - printf "%.1f %s", b, u[i]; - }' - fi -} - -mib_to_human() { - local mib="$1" - awk -v m="$mib" 'BEGIN { printf "%.1f GiB", m/1024 }' -} - -add_task() { - TASK_TITLES+=("$1") - TASK_FUNCS+=("$2") - TASK_TOTAL=$((TASK_TOTAL + 1)) -} - -run_tasks() { - local i - local title - local func - for i in "${!TASK_FUNCS[@]}"; do - TASK_INDEX=$((TASK_INDEX + 1)) - title="${TASK_TITLES[$i]}" - func="${TASK_FUNCS[$i]}" - section "Task ${TASK_INDEX}/${TASK_TOTAL}: ${title}" - "$func" - done -} - -lsblk_attr() { - local path="$1" - local attr="$2" - lsblk -dn -o "$attr" "$path" 2>/dev/null | head -n1 | xargs || true -} - -infer_disk_part_from_partition() { - local part_path="$1" - local resolved - local base - - resolved="$(readlink -f "$part_path" 2>/dev/null || printf '%s' "$part_path")" - base="$(basename "$resolved")" - - if [[ "$base" =~ ^(nvme[0-9]+n[0-9]+)p([0-9]+)$ ]]; then - printf '/dev/%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" - return 0 - fi - if [[ "$base" =~ ^(mmcblk[0-9]+)p([0-9]+)$ ]]; then - printf '/dev/%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" - return 0 - fi - if [[ "$base" =~ ^(md[0-9]+)p([0-9]+)$ ]]; then - printf '/dev/%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" - return 0 - fi - if [[ "$base" =~ ^((sd|vd|xvd|hd)[a-z]+)([0-9]+)$ ]]; then - printf '/dev/%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[3]}" - return 0 - fi - return 1 -} - -resolve_luks_backing_partition() { - local mapper_path="$1" - local mapper_name="$2" - local mapper_name_alt="$3" - local part="" - - if command -v cryptsetup >/dev/null 2>&1; then - part="$(cryptsetup status "$mapper_name" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" - if [ -z "$part" ] && [ -n "$mapper_name_alt" ] && [ "$mapper_name_alt" != "$mapper_name" ]; then - part="$(cryptsetup status "$mapper_name_alt" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" - fi - if [ -z "$part" ]; then - part="$(cryptsetup status "$mapper_path" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" - fi - fi - - if [ -z "$part" ]; then - part="$(lsblk -nro PATH,TYPE -s "$mapper_path" 2>/dev/null | awk '$2=="part" {print $1; exit}' | xargs || true)" - fi - - printf '%s' "$part" -} - -refresh_partition_table() { - local disk="$1" - if command -v partprobe >/dev/null 2>&1; then - partprobe "$disk" || true - fi - if command -v partx >/dev/null 2>&1; then - partx -u "$disk" || true - fi - if command -v udevadm >/dev/null 2>&1; then - udevadm settle || true - fi -} - -repair_gpt_backup_header() { - local disk="$1" - - if command -v sgdisk >/dev/null 2>&1; then - if sgdisk -e "$disk" >/dev/null 2>&1; then - refresh_partition_table "$disk" - return 0 - fi - fi - - if parted -s "$disk" print >/dev/null 2>&1; then - return 0 - fi - - if printf 'Fix\n' | parted ---pretend-input-tty "$disk" print >/dev/null 2>&1; then - refresh_partition_table "$disk" - return 0 - fi - - return 1 -} - -wait_for_partition_growth() { - local part="$1" - local old_bytes="$2" - local new_bytes=0 - local i=0 - - for ((i = 0; i < 12; i++)); do - new_bytes="$(blockdev --getsize64 "$part" 2>/dev/null || echo 0)" - if [ "$new_bytes" -gt "$old_bytes" ]; then - return 0 - fi - sleep 1 - done - return 1 -} - -is_last_partition_on_disk() { - local disk="$1" - local part_num="$2" - local last_part - - last_part="$(parted -ms "$disk" unit s print 2>/dev/null | awk -F: '$1 ~ /^[0-9]+$/ {last=$1} END {print last}')" - [ -n "$last_part" ] && [ "$part_num" = "$last_part" ] -} - -get_trailing_free_bytes() { - local disk="$1" - local part_num="$2" - local disk_bytes - local part_end_bytes - local free_bytes - - disk_bytes="$(blockdev --getsize64 "$disk" 2>/dev/null || true)" - part_end_bytes="$(parted -ms "$disk" unit B print 2>/dev/null | awk -F: -v p="$part_num" '$1==p {gsub("B","",$3); print $3; exit}')" - if [ -z "$disk_bytes" ] || [ -z "$part_end_bytes" ]; then - return 1 - fi - - free_bytes=$((disk_bytes - part_end_bytes - 1)) - if [ "$free_bytes" -lt 0 ]; then - free_bytes=0 - fi - echo "$free_bytes" -} - -resize_open_luks_mapping() { - if [ -n "$LUKS_NAME" ] && cryptsetup resize "$LUKS_NAME" >/dev/null 2>&1; then - return 0 - fi - cryptsetup resize "$LUKS_DEV" >/dev/null 2>&1 -} - -detect_storage_stack() { - STORAGE_DETECTED=0 - LUKS_NAME="" - LUKS_PART="" - DISK_DEV="" - PART_NUM="" - - require_cmd findmnt || return 1 - require_cmd lvs || return 1 - require_cmd pvs || return 1 - require_cmd lsblk || return 1 - require_cmd cryptsetup || return 1 - - ROOT_SOURCE="$(findmnt -n -o SOURCE / || true)" - if [ -z "$ROOT_SOURCE" ] || [ ! -e "$ROOT_SOURCE" ]; then - log_warn "Unable to detect root device." - return 1 - fi - - VG_NAME="$(lvs --noheadings -o vg_name "$ROOT_SOURCE" 2>/dev/null | xargs || true)" - LV_NAME="$(lvs --noheadings -o lv_name "$ROOT_SOURCE" 2>/dev/null | xargs || true)" - if [ -z "$VG_NAME" ]; then - log_warn "Root does not appear to be on LVM." - return 1 - fi - - PV_NAME="$(pvs --noheadings -o pv_name --select "vg_name=${VG_NAME}" 2>/dev/null | head -n1 | xargs || true)" - if [ -z "$PV_NAME" ]; then - log_warn "Unable to detect LVM physical volume." - return 1 - fi - - LUKS_DEV="$PV_NAME" - LUKS_NAME="$(basename "$LUKS_DEV")" - local mapper_name_alt - mapper_name_alt="$(lsblk_attr "$LUKS_DEV" NAME)" - - local luks_type - luks_type="$(lsblk_attr "$LUKS_DEV" TYPE)" - if [ "$luks_type" != "crypt" ]; then - log_warn "LVM PV is not on a LUKS device (type: ${luks_type:-unknown}). LUKS resize will be skipped." - return 1 - fi - - LUKS_PART="$(resolve_luks_backing_partition "$LUKS_DEV" "$LUKS_NAME" "$mapper_name_alt")" - if [ -z "$LUKS_PART" ] || [ ! -b "$LUKS_PART" ]; then - log_warn "Unable to detect LUKS backing partition." - return 1 - fi - - local inferred - local inferred_disk="" - local inferred_part="" - inferred="$(infer_disk_part_from_partition "$LUKS_PART" || true)" - if [ -n "$inferred" ]; then - inferred_disk="$(printf '%s\n' "$inferred" | sed -n '1p')" - inferred_part="$(printf '%s\n' "$inferred" | sed -n '2p')" - fi - - DISK_DEV="$inferred_disk" - PART_NUM="$inferred_part" - - if [ -z "$DISK_DEV" ]; then - local disk_parent - disk_parent="$(lsblk_attr "$LUKS_PART" PKNAME)" - if [ -n "$disk_parent" ]; then - DISK_DEV="/dev/${disk_parent}" - else - DISK_DEV="$(lsblk -nro PATH,TYPE -s "$LUKS_PART" 2>/dev/null | awk '$2=="disk" {print $1; exit}' | xargs || true)" - fi - fi - - if [ -z "$PART_NUM" ]; then - PART_NUM="$(lsblk_attr "$LUKS_PART" PARTNUM)" - fi - - if [ -z "$DISK_DEV" ] || [ ! -b "$DISK_DEV" ] || [ -z "$PART_NUM" ] || ! [[ "$PART_NUM" =~ ^[0-9]+$ ]]; then - log_warn "Unable to detect disk device or partition number." - log_warn "Detected values: LUKS_PART=${LUKS_PART:-} DISK_DEV=${DISK_DEV:-} PART_NUM=${PART_NUM:-}" - return 1 - fi - - STORAGE_DETECTED=1 - return 0 -} - -resize_lvm_on_luks() { - if [ "$STORAGE_DETECTED" -ne 1 ]; then - log_warn "Storage layout not detected; skipping resize." - return 0 - fi - - require_cmd parted || return 0 - require_cmd blockdev || return 0 - require_cmd cryptsetup || return 0 - require_cmd pvresize || return 0 - require_cmd lvextend || return 0 - - log_info "Root LV: ${ROOT_SOURCE}" - log_info "VG: ${VG_NAME} | LV: ${LV_NAME}" - log_info "LUKS device: ${LUKS_DEV}" - log_info "LUKS partition: ${LUKS_PART}" - log_info "Disk: ${DISK_DEV} | Partition number: ${PART_NUM}" - [ -n "$LUKS_NAME" ] && log_info "LUKS mapper name: ${LUKS_NAME}" - - if ! repair_gpt_backup_header "$DISK_DEV"; then - log_warn "Unable to repair or verify GPT backup header on ${DISK_DEV}; resize may fail." - fi - - if ! is_last_partition_on_disk "$DISK_DEV" "$PART_NUM"; then - log_warn "Partition ${PART_NUM} is not the last partition on ${DISK_DEV}. Automatic growth is skipped." - return 0 - fi - - local part_size_before - local free_bytes - - part_size_before="$(blockdev --getsize64 "$LUKS_PART" 2>/dev/null || echo 0)" - free_bytes="$(get_trailing_free_bytes "$DISK_DEV" "$PART_NUM" || true)" - if [ -z "$free_bytes" ]; then - log_warn "Unable to determine free disk space." - return 0 - fi - - if [ "$free_bytes" -lt $((1024 * 1024)) ]; then - log_ok "No significant free space detected after the root partition." - return 0 - fi - - log_info "Unallocated space available: $(human_bytes "$free_bytes")" - if ! confirm "Extend partition, LUKS device, VG, and root LV now?" "yes"; then - log_warn "Skipped resize." - return 0 - fi - - log_info "Extending partition ${LUKS_PART} to 100% of disk..." - if ! parted -s "$DISK_DEV" resizepart "$PART_NUM" 100%; then - log_warn "Partition resize failed." - return 0 - fi - - refresh_partition_table "$DISK_DEV" - if [ "$part_size_before" -gt 0 ] && ! wait_for_partition_growth "$LUKS_PART" "$part_size_before"; then - log_warn "Kernel has not reported the new partition size yet; continuing anyway." - fi - - log_info "Resizing LUKS device..." - if ! resize_open_luks_mapping; then - log_warn "LUKS resize failed." - return 0 - fi - - log_info "Resizing LVM physical volume..." - if ! pvresize "$LUKS_DEV"; then - log_warn "pvresize failed." - return 0 - fi - - log_info "Extending root LV to use all free space..." - if ! lvextend -l +100%FREE -r "$ROOT_SOURCE"; then - log_warn "lvextend failed." - return 0 - fi - - log_ok "Resize complete." -} - -parse_size_to_mib() { - local input="$1" - local normalized - normalized="$(echo "$input" | tr '[:upper:]' '[:lower:]' | xargs)" - if [[ "$normalized" =~ ^[0-9]+$ ]]; then - echo "$normalized" - return 0 - fi - if [[ "$normalized" =~ ^([0-9]+)(g|gb)$ ]]; then - echo $((BASH_REMATCH[1] * 1024)) - return 0 - fi - if [[ "$normalized" =~ ^([0-9]+)(m|mb)$ ]]; then - echo "${BASH_REMATCH[1]}" - return 0 - fi - return 1 -} - -setup_swap() { - require_cmd awk || return 0 - require_cmd fallocate || return 0 - require_cmd mkswap || return 0 - require_cmd swapon || return 0 - - local mem_kib - local mem_mib - local swap_mib - - mem_kib="$(awk '/MemTotal/ {print $2}' /proc/meminfo)" - mem_mib=$((mem_kib / 1024)) - - if [ "$mem_mib" -lt 2048 ]; then - swap_mib=$((mem_mib * 2)) - elif [ "$mem_mib" -lt 4096 ]; then - swap_mib=$mem_mib - else - swap_mib=$((mem_mib / 5)) - fi - - log_info "Detected RAM: $(mib_to_human "$mem_mib")" - log_info "Recommended swap size: $(mib_to_human "$swap_mib")" - - if ! confirm "Use recommended swap size?" "yes"; then - local custom - while true; do - custom="$(prompt_input "Enter custom swap size (MiB or GiB, e.g. 2048 or 4G)")" - if swap_mib="$(parse_size_to_mib "$custom")"; then - if [ "$swap_mib" -gt 0 ]; then - break - fi - fi - log_warn "Invalid size. Try again." - done - fi - - local swapfile="/swapfile" - if [ -e "$swapfile" ]; then - log_warn "Swap file already exists at ${swapfile}." - if ! confirm "Replace existing swap file?" "no"; then - log_warn "Skipped swap setup." - return 0 - fi - if swapon --show=NAME --noheadings | grep -qx "$swapfile"; then - swapoff "$swapfile" || true - fi - rm -f "$swapfile" - fi - - log_info "Creating swap file (${swap_mib} MiB) at ${swapfile}..." - fallocate -l "${swap_mib}M" "$swapfile" - chmod 600 "$swapfile" - mkswap "$swapfile" >/dev/null - swapon "$swapfile" - - if ! grep -qE "^[[:space:]]*${swapfile}[[:space:]]" /etc/fstab; then - echo "${swapfile} none swap sw 0 0" >> /etc/fstab - fi - - log_ok "Swap enabled." -} - -change_luks_passphrase() { - if [ "$STORAGE_DETECTED" -ne 1 ]; then - log_warn "Storage layout not detected; skipping LUKS passphrase change." - return 0 - fi - if [ -z "$LUKS_PART" ] || [ ! -e "$LUKS_PART" ]; then - log_warn "LUKS partition not found; skipping." - return 0 - fi - require_cmd cryptsetup || return 0 - - if ! confirm "Change LUKS passphrase in slot 0 now?" "yes"; then - log_warn "Skipped LUKS passphrase change." - return 0 - fi - - local old_pass - local new_pass - local tmp_old - local tmp_new - - old_pass="$(prompt_secret "Enter current LUKS passphrase")" - new_pass="$(prompt_secret_confirm "Enter new LUKS passphrase" "Confirm new LUKS passphrase")" - - tmp_old="$(mktemp)" - tmp_new="$(mktemp)" - add_temp_file "$tmp_old" - add_temp_file "$tmp_new" - - printf '%s' "$old_pass" >"$tmp_old" - printf '%s' "$new_pass" >"$tmp_new" - - log_info "Updating LUKS passphrase in slot 0..." - if cryptsetup luksChangeKey --batch-mode --key-slot 0 --key-file "$tmp_old" "$LUKS_PART" "$tmp_new"; then - log_ok "LUKS passphrase updated." - else - log_warn "Failed to update LUKS passphrase." - fi -} - -setup_clevis() { - log_info "Clevis/Tang setup is not implemented in this template yet." - if confirm "Would you like to configure Clevis with a Tang server now? (will be skipped)" "no"; then - local tang - tang="$(prompt_input "Tang server URL" "http://tang.int.r3w.de")" - log_warn "Clevis setup for ${tang} is not implemented yet. Skipping." - else - log_info "Skipping Clevis setup." - fi -} - -setup_tailscale() { - if ! require_cmd tailscale; then - log_warn "Tailscale is not installed; skipping." - return 0 - fi - - if ! confirm "Set up Tailscale now?" "yes"; then - log_warn "Skipped Tailscale setup." - return 0 - fi - - local server - local tags - local key - local tag_list="" - local t - - server="$(prompt_input "Tailscale/Headscale server URL" "https://vpn.s1q.dev")" - tags="$(prompt_input "Client tags (comma-separated)" "server")" - key="$(prompt_secret "Pre-authentication key")" - - if [ -n "$tags" ]; then - IFS=',' read -r -a tag_array <<<"$tags" - for t in "${tag_array[@]:-}"; do - t="$(echo "$t" | xargs)" - if [ -n "$t" ]; then - if [ -n "$tag_list" ]; then - tag_list+="," - fi - tag_list+="tag:${t}" - fi - done - fi - - log_info "Bringing up Tailscale..." - if [ -n "$tag_list" ]; then - tailscale up --login-server "$server" --authkey "$key" --ssh --advertise-tags "$tag_list" - else - tailscale up --login-server "$server" --authkey "$key" --ssh - fi - - log_ok "Tailscale setup complete." -} - -setup_crowdsec() { - if ! require_cmd cscli; then - log_warn "CrowdSec (cscli) not installed; skipping." - return 0 - fi - - if ! confirm "Set up CrowdSec now?" "yes"; then - log_warn "Skipped CrowdSec setup." - return 0 - fi - - local key - key="$(prompt_secret "Enrollment key")" - - log_info "Enrolling CrowdSec..." - if cscli console enroll -e "$key"; then - log_info "Restarting CrowdSec service..." - systemctl restart crowdsec || log_warn "Failed to restart crowdsec service." - log_ok "CrowdSec enrollment complete." - else - log_warn "CrowdSec enrollment failed." - fi -} - -prompt_reboot() { - log_warn "A reboot is strongly recommended after partition or swap changes." - if confirm "Reboot now?" "yes"; then - log_info "Rebooting..." - reboot - else - log_info "Please reboot later to ensure changes take effect." - fi -} - -welcome() { - section "Initial VM Setup" - log_info "This setup runs once and is fully interactive." - log_info "Hostname: $(hostname)" -} - -main() { - ensure_tty - ensure_root "$@" - welcome - - if detect_storage_stack; then - log_ok "Detected LVM on LUKS storage layout." - else - log_warn "Storage layout detection incomplete; some steps may be skipped." - fi - - TASK_TITLES=() - TASK_FUNCS=() - - add_task "Resize LVM on LUKS (if free space exists)" resize_lvm_on_luks - add_task "Configure swap file" setup_swap - add_task "Change LUKS passphrase (slot 0)" change_luks_passphrase - add_task "Clevis/Tang setup (placeholder)" setup_clevis - add_task "Configure Tailscale" setup_tailscale - add_task "Configure CrowdSec" setup_crowdsec - add_task "Reboot recommendation" prompt_reboot - - run_tasks - log_ok "Initial setup finished." -} - -main "$@" diff --git a/debian/13-trixie-luks/scripts/crowdsec-configuration.sh b/debian/13-trixie-luks/scripts/crowdsec-configuration.sh deleted file mode 100644 index 6ac3f7a..0000000 --- a/debian/13-trixie-luks/scripts/crowdsec-configuration.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euf -o pipefail - -export DEBIAN_FRONTEND=noninteractive - -# Enable write-ahead-logging (wal -- allowing more concurrency in SQLite that will improve performances in most scenarios.) -sed -i -E '/^db_config:/,/^[^[:space:]]/{s/^([[:space:]]*)type:[[:space:]]*sqlite$/&\ -\1use_wal: true/}' /etc/crowdsec/config.yaml diff --git a/debian/13-trixie-luks/scripts/crowdsec-repo-setup.sh b/debian/13-trixie-luks/scripts/crowdsec-repo-setup.sh deleted file mode 100644 index 7b2e815..0000000 --- a/debian/13-trixie-luks/scripts/crowdsec-repo-setup.sh +++ /dev/null @@ -1,370 +0,0 @@ -#!/bin/sh -# -# Inspired from packagecloud installation scripts -# -# #MIT License -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Crowdsec repositories installation script -# -# This script: -# - Requires `root` or `sudo` privileges to run -# - Attempts to detect your Linux distribution and version and configure your -# package management system for you. -# - Installs dependencies and recommendations without asking for confirmation. -# - Is POSIX compliant and can be run using bash or any POSIX-compliant shell - - -unknown_os() { - echo "Unfortunately, your operating system distribution and version are not supported by this script." - echo - echo "You can override the OS detection by setting os= and dist= prior to running this script." - echo "You can find a list of supported OSes and distributions on our website: https://packagecloud.io/docs#os_distro_version" - echo - echo "For example, to force Ubuntu Trusty: os=ubuntu dist=trusty ./script.sh" - echo - echo "Please file an issue at https://github.com/crowdsecurity/crowdsec" - exit 1 -} - -detect_os() { - if [ -z "$os" ] && [ -z "$dist" ]; then - if [ -e /etc/os-release ]; then - . /etc/os-release - os=$ID - if [ "$os" = "poky" ]; then - dist="$VERSION_ID" - elif [ "$os" = "sles" ]; then - dist="$VERSION_ID" - os=opensuse - elif [ "$os" = "opensuse" ]; then - dist="$VERSION_ID" - elif [ "$os" = "opensuse-leap" ]; then - os=opensuse - dist="$VERSION_ID" - elif [ "$os" = "amzn" ]; then - dist="$VERSION_ID" - else - dist=$(echo "$VERSION_ID" | awk -F '.' '{ print $1 }') - fi - - elif command -v lsb_release >/dev/null; then - # get major version (e.g. '5' or '6') - dist=$(lsb_release -r | cut -f2 | awk -F '.' '{ print $1 }') - - # get os (e.g. 'centos', 'redhatenterpriseserver', etc) - os=$(lsb_release -i | cut -f2 | awk '{ print tolower($1) }') - - elif [ -e /etc/oracle-release ]; then - dist=$(cut -f5 --delimiter=' ' /etc/oracle-release | awk -F '.' '{ print $1 }') - os='ol' - - elif [ -e /etc/fedora-release ]; then - dist=$(cut -f3 --delimiter=' ' /etc/fedora-release) - os='fedora' - - elif [ -e /etc/redhat-release ]; then - os_hint=$(awk '{ print tolower($1) }' /etc/redhat-release) - if [ "$os_hint" = "centos" ]; then - dist=$(awk '{ print $3 }' /etc/redhat-release | awk -F '.' '{ print $1 }') - os='centos' - elif [ "$os_hint" = "scientific" ]; then - dist=$(awk '{ print $4 }' /etc/redhat-release | awk -F '.' '{ print $1 }') - os='scientific' - else - dist=$(awk '{ print tolower($7) }' /etc/redhat-release | cut -f1 --delimiter='.') - os='redhatenterpriseserver' - fi - - elif grep -q Amazon /etc/issue; then - dist='6' - os='aws' - else - unknown_os - fi - fi - - # remove whitespace from OS and dist name and transform to lowercase - os=$(echo "$os" | tr -d ' ' | tr '[:upper:]' '[:lower:]') - dist=$(echo "$dist" | tr -d ' ' | tr '[:upper:]' '[:lower:]') - - if [ -z "$dist" ]; then - echo "Detected operating system as $os." - else - echo "Detected operating system as $os/$dist." - fi - - if [ "$os" = "ol" ] || [ "$os" = "el" ] && [ "$dist" -gt 7 ]; then - _skip_pygpgme=1 - else - _skip_pygpgme=0 - fi - -} - -gpg_check_deb() { - echo "Checking for gpg..." - if command -v gpg >/dev/null; then - echo "Detected gpg..." - else - echo "Installing gnupg for GPG verification..." - if ! apt-get install -y gnupg; then - echo "Unable to install GPG! Your base system has a problem; please check your default OS's package repositories because GPG should work." - echo "Repository installation aborted." - echo - echo "Please file an issue at https://github.com/crowdsecurity/crowdsec" - exit 1 - fi - fi -} - -curl_check_deb() { - echo "Checking for curl..." - if command -v curl >/dev/null; then - echo "Detected curl..." - else - echo "Installing curl..." - - if apt-get install -q -y curl; then - echo "Unable to install curl! Your base system has a problem; please check your default OS's package repositories because curl should work." - echo "Repository installation aborted." - echo - echo "Please file an issue at https://github.com/crowdsecurity/crowdsec" - exit 1 - fi - fi -} - -curl_check_rpm() { - echo "Checking for curl..." - if command -v curl >/dev/null; then - echo "Detected curl..." - else - echo "Installing curl..." - yum install -d0 -e0 -y curl - fi -} - -curl_check_zypper() { - echo "Checking for curl..." - if command -v curl >/dev/null; then - echo "Detected curl..." - else - echo "Installing curl..." - zypper install curl - fi -} - -finalize_yum_repo() { - if [ "$_skip_pygpgme" = 0 ]; then - echo "Installing pygpgme to verify GPG signatures..." - yum install -y pygpgme --disablerepo="crowdsec_${repo}" - if ! rpm -qa | grep -qw pygpgme; then - echo - echo "WARNING: " - echo "The pygpgme package could not be installed. This means GPG verification is not possible for any RPM installed on your system. " - echo "To fix this, add a repository with pygpgme. Usually, the EPEL repository for your system will have this. " - echo "More information: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F" - echo - - # set the repo_gpgcheck option to 0 - sed -i'' 's/repo_gpgcheck=1/repo_gpgcheck=0/' "/etc/yum.repos.d/crowdsec_${repo}.repo" - fi - fi - - echo "Installing yum-utils..." - yum install -y yum-utils --disablerepo="crowdsec_${repo}" - if ! rpm -qa | grep -qw yum-utils; then - echo - echo "WARNING: " - echo "The yum-utils package could not be installed. This means you may not be able to install source RPMs or use other yum features." - echo - fi - - echo "Generating yum cache for crowdsec..." - yum -q makecache -y --disablerepo='*' --enablerepo="crowdsec_${repo}" -} - -install_debian_keyring() { - if [ "$os" = "debian" ]; then - echo "Installing debian-archive-keyring which is needed for installing " - echo "apt-transport-https on many Debian systems." - apt-get install -y debian-archive-keyring >/dev/null 2>&1 - fi -} - -detect_apt_version() { - apt_version_full=$(apt-get -v | head -1 | awk '{ print $2 }') - apt_version_major=$(echo "$apt_version_full" | cut -d. -f1) - apt_version_minor=$(echo "$apt_version_full" | cut -d. -f2) - apt_version_modified="${apt_version_major}${apt_version_minor}0" - - echo "Detected apt version as $apt_version_full" -} - -main() { - if [ -z "$repo" ]; then - repo="crowdsec" - fi - - detect_os - case $os in - ubuntu | debian | raspbian | linuxmint) - detect_apt_version - gpg_check_deb - curl_check_deb - apt_source_path="/etc/apt/sources.list.d/crowdsec_${repo}.list" - pre_reqs="apt-transport-https ca-certificates curl" - if [ -f "$apt_source_path" ]; then - echo - echo "The file $apt_source_path already exists: overwriting it." - echo - fi - # needed dependencies - apt-get update -qq >/dev/null - #shellcheck disable=SC2086 - DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $pre_reqs >/dev/null - # gpg keys - gpg_key_url="https://packagecloud.io/crowdsec/${repo}/gpgkey" - apt_keyrings_dir="/etc/apt/keyrings" - gpg_keyring_path="$apt_keyrings_dir/crowdsec_${repo}-archive-keyring.gpg" - gpg_key_path_old="/etc/apt/trusted.gpg.d/crowdsec_${repo}.gpg" - echo - echo "Importing packagecloud gpg key... " - echo - - # move gpg key to old path if apt version is older than 1.1 - if [ "$apt_version_modified" -lt 110 ]; then - curl -fsSL "$gpg_key_url" | gpg --dearmor >"$gpg_key_path_old" - # grant 644 permisions to gpg key path old - chmod 0644 "$gpg_key_path_old" - - # deletes the keyrings directory if it is empty - echo "Packagecloud gpg key imported to $gpg_key_path_old" - else - if [ ! -d "$apt_keyrings_dir" ]; then - install -d -m 0755 "$apt_keyrings_dir" - fi - # import the gpg key - curl -fsSL "$gpg_key_url" | gpg --dearmor >"$gpg_keyring_path" - # grant 644 permisions to gpg keyring path - chmod 0644 "$gpg_keyring_path" - - echo "Packagecloud gpg key imported to $gpg_keyring_path" - fi - echo - echo "Installing ${apt_source_path}..." - echo - echo "deb [signed-by=/etc/apt/keyrings/crowdsec_${repo}-archive-keyring.gpg] https://packagecloud.io/crowdsec/${repo}/any/ any main" >"$apt_source_path" - echo "deb-src [signed-by=/etc/apt/keyrings/crowdsec_${repo}-archive-keyring.gpg] https://packagecloud.io/crowdsec/${repo}/any/ any main" >>"$apt_source_path" - apt-get update -qq >/dev/null - - ;; - centos | rhel | fedora | redhatentrepriseserver | amzn | cloudlinux | almalinux | rocky | opensuse | ol) - if [ "$os" = "ol" ] && [ "$dist" = "7" ] || [ "$os" = "amzn" ] && [ "$dist" = "2" ]; then - rpm_repo_config_url="https://packagecloud.io/install/repositories/crowdsec/${repo}/config_file.repo?os=${os}&dist=${dist}&source=script" - else - rpm_repo_config_url="https://packagecloud.io/install/repositories/crowdsec/${repo}/config_file.repo?os=rpm_any&dist=rpm_any&source=script" - fi - if [ "$os" = "opensuse" ]; then - curl_check_zypper - rpm_repo_path=/etc/zypp/repos.d/crowdsec_${repo}.repo - else - curl_check_rpm - rpm_repo_path=/etc/yum.repos.d/crowdsec_${repo}.repo - fi - - echo "Downloading repository file: $rpm_repo_config_url" - - curl -sSf "$rpm_repo_config_url" >"$rpm_repo_path" - curl_exit_code=$? - if [ "$curl_exit_code" = "22" ]; then - echo - echo - echo "Unable to download repo config from: " - echo "$rpm_repo_config_url" - echo - echo "This usually happens if your operating system is not supported by " - echo "packagecloud.io, or this script's OS detection failed." - echo - echo "You can override the OS detection by setting os= and dist= prior to running this script." - echo "You can find a list of supported OSes and distributions on our website: https://packagecloud.io/docs#os_distro_version" - echo - echo "For example, to force CentOS 6: os=el dist=6 ./script.sh" - echo - echo "If you are running a supported OS, please file an issue at https://github.com/crowdsecurity/crowdsec." - [ -e "$rpm_repo_path" ] && rm "$rpm_repo_path" - exit 1 - elif [ "$curl_exit_code" = "35" ] || [ "$curl_exit_code" = "60" ]; then - echo - echo "curl is unable to connect to packagecloud.io over TLS when running: " - echo " curl $rpm_repo_config_url" - echo - echo "This is usually due to one of two things:" - echo - echo " 1.) Missing CA root certificates (make sure the ca-certificates package is installed)" - echo " 2.) An old version of libssl. Try upgrading libssl on your system to a more recent version" - echo - echo "Contact support@crowdsec.net with information about your system for help." - [ -e "$rpm_repo_path" ] && rm "$rpm_repo_path" - exit 1 - elif [ "$curl_exit_code" -gt "0" ]; then - echo - echo "Unable to run: " - echo " curl $rpm_repo_config_url" - echo - echo "Double check your curl installation and try again." - echo - echo "Please file an issue at https://github.com/crowdsecurity/crowdsec if you think the behavior is not intended" - [ -e "$rpm_repo_path" ] && rm "$rpm_repo_path" - exit 1 - else - echo "done." - fi - if [ "$os" = "opensuse" ]; then - zypper --gpg-auto-import-keys refresh "crowdsec_${repo}" - zypper --gpg-auto-import-keys refresh "crowdsec_${repo}-source" - else - echo "$os" - finalize_yum_repo - fi - ;; - *) - echo "Error This system is not supported (yet) by this script." - echo "Please have a look at documentation https://docs.crowdsec.net/ or" - echo "file an issue at https://github.com/crowdsecurity/crowdsec if you think" - echo "the behavior is not intended" - - exit 1 - ;; - esac - - echo -} - -if [ "$(id -u)" -ne 0 ]; then - echo "This script must be run as root" - echo - echo "file an issue at https://github.com/crowdsecurity/crowdsec if you think" - echo "the behavior is not intended" - exit 1 -fi - -main diff --git a/debian/13-trixie-luks/scripts/tailscale.sh b/debian/13-trixie-luks/scripts/tailscale.sh deleted file mode 100644 index 8ffd3f5..0000000 --- a/debian/13-trixie-luks/scripts/tailscale.sh +++ /dev/null @@ -1,726 +0,0 @@ -#!/bin/sh -# Copyright (c) Tailscale Inc & contributors -# SPDX-License-Identifier: BSD-3-Clause -# -# This script detects the current operating system, and installs -# Tailscale according to that OS's conventions. -# -# Environment variables: -# TRACK: Set to "stable" or "unstable" (default: stable) -# TAILSCALE_VERSION: Pin to a specific version (e.g., "1.88.4") -# -# Examples: -# curl -fsSL https://tailscale.com/install.sh | sh -# curl -fsSL https://tailscale.com/install.sh | TAILSCALE_VERSION=1.88.4 sh -# curl -fsSL https://tailscale.com/install.sh | TRACK=unstable sh - -set -eu - -# All the code is wrapped in a main function that gets called at the -# bottom of the file, so that a truncated partial download doesn't end -# up executing half a script. -main() { - # Step 1: detect the current linux distro, version, and packaging system. - # - # We rely on a combination of 'uname' and /etc/os-release to find - # an OS name and version, and from there work out what - # installation method we should be using. - # - # The end result of this step is that the following three - # variables are populated, if detection was successful. - OS="" - VERSION="" - PACKAGETYPE="" - APT_KEY_TYPE="" # Only for apt-based distros - APT_SYSTEMCTL_START=false # Only needs to be true for Kali - TRACK="${TRACK:-stable}" - TAILSCALE_VERSION="${TAILSCALE_VERSION:-}" - - case "$TRACK" in - stable|unstable) - ;; - *) - echo "unsupported track $TRACK" - exit 1 - ;; - esac - - if [ -f /etc/os-release ]; then - # /etc/os-release populates a number of shell variables. We care about the following: - # - ID: the short name of the OS (e.g. "debian", "freebsd") - # - VERSION_ID: the numeric release version for the OS, if any (e.g. "18.04") - # - VERSION_CODENAME: the codename of the OS release, if any (e.g. "buster") - # - UBUNTU_CODENAME: if it exists, use instead of VERSION_CODENAME - . /etc/os-release - VERSION_MAJOR="${VERSION_ID:-}" - VERSION_MAJOR="${VERSION_MAJOR%%.*}" - case "$ID" in - ubuntu|pop|neon|zorin|tuxedo) - OS="ubuntu" - if [ "${UBUNTU_CODENAME:-}" != "" ]; then - VERSION="$UBUNTU_CODENAME" - else - VERSION="$VERSION_CODENAME" - fi - PACKAGETYPE="apt" - # Third-party keyrings became the preferred method of - # installation in Ubuntu 20.04. - if [ "$VERSION_MAJOR" -lt 20 ]; then - APT_KEY_TYPE="legacy" - else - APT_KEY_TYPE="keyring" - fi - ;; - debian) - OS="$ID" - VERSION="$VERSION_CODENAME" - PACKAGETYPE="apt" - # Third-party keyrings became the preferred method of - # installation in Debian 11 (Bullseye). - if [ -z "${VERSION_ID:-}" ]; then - # rolling release. If you haven't kept current, that's on you. - APT_KEY_TYPE="keyring" - # Parrot Security is a special case that uses ID=debian - elif [ "$NAME" = "Parrot Security" ]; then - # All versions new enough to have this behaviour prefer keyring - # and their VERSION_ID is not consistent with Debian. - APT_KEY_TYPE="keyring" - # They don't specify the Debian version they're based off in os-release - # but Parrot 6 is based on Debian 12 Bookworm. - VERSION=bookworm - elif [ "$VERSION_MAJOR" -lt 11 ]; then - APT_KEY_TYPE="legacy" - else - APT_KEY_TYPE="keyring" - fi - ;; - linuxmint) - if [ "${UBUNTU_CODENAME:-}" != "" ]; then - OS="ubuntu" - VERSION="$UBUNTU_CODENAME" - elif [ "${DEBIAN_CODENAME:-}" != "" ]; then - OS="debian" - VERSION="$DEBIAN_CODENAME" - else - OS="ubuntu" - VERSION="$VERSION_CODENAME" - fi - PACKAGETYPE="apt" - if [ "$VERSION_MAJOR" -lt 5 ]; then - APT_KEY_TYPE="legacy" - else - APT_KEY_TYPE="keyring" - fi - ;; - elementary) - OS="ubuntu" - VERSION="$UBUNTU_CODENAME" - PACKAGETYPE="apt" - if [ "$VERSION_MAJOR" -lt 6 ]; then - APT_KEY_TYPE="legacy" - else - APT_KEY_TYPE="keyring" - fi - ;; - industrial-os) - OS="debian" - PACKAGETYPE="apt" - if [ "$VERSION_MAJOR" -lt 5 ]; then - VERSION="buster" - APT_KEY_TYPE="legacy" - else - VERSION="bullseye" - APT_KEY_TYPE="keyring" - fi - ;; - parrot|mendel) - OS="debian" - PACKAGETYPE="apt" - if [ "$VERSION_MAJOR" -lt 5 ]; then - VERSION="buster" - APT_KEY_TYPE="legacy" - else - VERSION="bullseye" - APT_KEY_TYPE="keyring" - fi - ;; - galliumos) - OS="ubuntu" - PACKAGETYPE="apt" - VERSION="bionic" - APT_KEY_TYPE="legacy" - ;; - pureos|kaisen) - OS="debian" - PACKAGETYPE="apt" - VERSION="bullseye" - APT_KEY_TYPE="keyring" - ;; - raspbian) - OS="$ID" - VERSION="$VERSION_CODENAME" - PACKAGETYPE="apt" - # Third-party keyrings became the preferred method of - # installation in Raspbian 11 (Bullseye). - if [ "$VERSION_MAJOR" -lt 11 ]; then - APT_KEY_TYPE="legacy" - else - APT_KEY_TYPE="keyring" - fi - ;; - kali) - OS="debian" - PACKAGETYPE="apt" - APT_SYSTEMCTL_START=true - # Third-party keyrings became the preferred method of - # installation in Debian 11 (Bullseye), which Kali switched - # to in roughly 2021.x releases - if [ "$VERSION_MAJOR" -lt 2021 ]; then - # Kali VERSION_ID is "kali-rolling", which isn't distinguishing - VERSION="buster" - APT_KEY_TYPE="legacy" - else - VERSION="bullseye" - APT_KEY_TYPE="keyring" - fi - ;; - Deepin|deepin) # https://github.com/tailscale/tailscale/issues/7862 - OS="debian" - PACKAGETYPE="apt" - if [ "$VERSION_MAJOR" -lt 20 ]; then - APT_KEY_TYPE="legacy" - VERSION="buster" - else - APT_KEY_TYPE="keyring" - VERSION="bullseye" - fi - ;; - pika) - PACKAGETYPE="apt" - # All versions of PikaOS are new enough to prefer keyring - APT_KEY_TYPE="keyring" - # Older versions of PikaOS are based on Ubuntu rather than Debian - if [ "$VERSION_MAJOR" -lt 4 ]; then - OS="ubuntu" - VERSION="$UBUNTU_CODENAME" - else - OS="debian" - VERSION="$DEBIAN_CODENAME" - fi - ;; - sparky) - OS="debian" - PACKAGETYPE="apt" - VERSION="$DEBIAN_CODENAME" - APT_KEY_TYPE="keyring" - ;; - centos) - OS="$ID" - VERSION="$VERSION_MAJOR" - PACKAGETYPE="dnf" - if [ "$VERSION" = "7" ]; then - PACKAGETYPE="yum" - fi - ;; - ol) - OS="oracle" - VERSION="$VERSION_MAJOR" - PACKAGETYPE="dnf" - if [ "$VERSION" = "7" ]; then - PACKAGETYPE="yum" - fi - ;; - rhel|miraclelinux) - OS="$ID" - if [ "$ID" = "miraclelinux" ]; then - OS="rhel" - fi - VERSION="$VERSION_MAJOR" - PACKAGETYPE="dnf" - if [ "$VERSION" = "7" ]; then - PACKAGETYPE="yum" - fi - ;; - fedora) - OS="$ID" - VERSION="" - PACKAGETYPE="dnf" - ;; - rocky|almalinux|nobara|openmandriva|sangoma|risios|cloudlinux|alinux|fedora-asahi-remix|ultramarine) - OS="fedora" - VERSION="" - PACKAGETYPE="dnf" - ;; - amzn) - OS="amazon-linux" - VERSION="$VERSION_ID" - PACKAGETYPE="yum" - ;; - xenenterprise) - OS="centos" - VERSION="$VERSION_MAJOR" - PACKAGETYPE="yum" - ;; - opensuse-leap|sles) - OS="opensuse" - VERSION="leap/$VERSION_ID" - PACKAGETYPE="zypper" - ;; - opensuse-tumbleweed) - OS="opensuse" - VERSION="tumbleweed" - PACKAGETYPE="zypper" - ;; - sle-micro-rancher) - OS="opensuse" - VERSION="leap/15.4" - PACKAGETYPE="zypper" - ;; - arch|archarm|endeavouros|blendos|garuda|archcraft|cachyos) - OS="arch" - VERSION="" # rolling release - PACKAGETYPE="pacman" - ;; - manjaro|manjaro-arm|biglinux) - OS="manjaro" - VERSION="" # rolling release - PACKAGETYPE="pacman" - ;; - alpine) - OS="$ID" - VERSION="$VERSION_ID" - PACKAGETYPE="apk" - ;; - postmarketos) - OS="alpine" - VERSION="$VERSION_ID" - PACKAGETYPE="apk" - ;; - nixos) - echo "Please add Tailscale to your NixOS configuration directly:" - echo - echo "services.tailscale.enable = true;" - exit 1 - ;; - bazzite) - echo "Bazzite comes with Tailscale installed by default." - echo "Please enable Tailscale by running the following commands as root:" - echo - echo "ujust enable-tailscale" - echo "tailscale up" - exit 1 - ;; - void) - OS="$ID" - VERSION="" # rolling release - PACKAGETYPE="xbps" - ;; - gentoo) - OS="$ID" - VERSION="" # rolling release - PACKAGETYPE="emerge" - ;; - freebsd) - OS="$ID" - VERSION="$VERSION_MAJOR" - PACKAGETYPE="pkg" - ;; - osmc) - OS="debian" - PACKAGETYPE="apt" - VERSION="bullseye" - APT_KEY_TYPE="keyring" - ;; - photon) - OS="photon" - VERSION="$VERSION_MAJOR" - PACKAGETYPE="tdnf" - ;; - steamos) - echo "To install Tailscale on SteamOS, please follow the instructions here:" - echo "https://github.com/tailscale-dev/deck-tailscale" - exit 1 - ;; - - # TODO: wsl? - # TODO: synology? qnap? - esac - fi - - # If we failed to detect something through os-release, consult - # uname and try to infer things from that. - if [ -z "$OS" ]; then - if type uname >/dev/null 2>&1; then - case "$(uname)" in - FreeBSD) - # FreeBSD before 12.2 doesn't have - # /etc/os-release, so we wouldn't have found it in - # the os-release probing above. - OS="freebsd" - VERSION="$(freebsd-version | cut -f1 -d.)" - PACKAGETYPE="pkg" - ;; - OpenBSD) - OS="openbsd" - VERSION="$(uname -r)" - PACKAGETYPE="" - ;; - Darwin) - OS="macos" - VERSION="$(sw_vers -productVersion | cut -f1-2 -d.)" - PACKAGETYPE="appstore" - ;; - Linux) - OS="other-linux" - VERSION="" - PACKAGETYPE="" - ;; - esac - fi - fi - - # Ideally we want to use curl, but on some installs we - # only have wget. Detect and use what's available. - CURL= - if type curl >/dev/null; then - CURL="curl -fsSL" - elif type wget >/dev/null; then - CURL="wget -q -O-" - fi - if [ -z "$CURL" ]; then - echo "The installer needs either curl or wget to download files." - echo "Please install either curl or wget to proceed." - exit 1 - fi - - TEST_URL="https://pkgs.tailscale.com/" - RC=0 - TEST_OUT=$($CURL "$TEST_URL" 2>&1) || RC=$? - if [ $RC != 0 ]; then - echo "The installer cannot reach $TEST_URL" - echo "Please make sure that your machine has internet access." - echo "Test output:" - echo $TEST_OUT - exit 1 - fi - - # Step 2: having detected an OS we support, is it one of the - # versions we support? - OS_UNSUPPORTED= - case "$OS" in - ubuntu|debian|raspbian|centos|oracle|rhel|amazon-linux|opensuse|photon) - # Check with the package server whether a given version is supported. - URL="https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/installer-supported" - $CURL "$URL" 2> /dev/null | grep -q OK || OS_UNSUPPORTED=1 - ;; - fedora) - # All versions supported, no version checking required. - ;; - arch) - # Rolling release, no version checking needed. - ;; - manjaro) - # Rolling release, no version checking needed. - ;; - alpine) - # All versions supported, no version checking needed. - # TODO: is that true? When was tailscale packaged? - ;; - void) - # Rolling release, no version checking needed. - ;; - gentoo) - # Rolling release, no version checking needed. - ;; - freebsd) - if [ "$VERSION" != "12" ] && \ - [ "$VERSION" != "13" ] && \ - [ "$VERSION" != "14" ] && \ - [ "$VERSION" != "15" ] - then - OS_UNSUPPORTED=1 - fi - ;; - openbsd) - OS_UNSUPPORTED=1 - ;; - macos) - # We delegate macOS installation to the app store, it will - # perform version checks for us. - ;; - other-linux) - OS_UNSUPPORTED=1 - ;; - *) - OS_UNSUPPORTED=1 - ;; - esac - if [ "$OS_UNSUPPORTED" = "1" ]; then - case "$OS" in - other-linux) - echo "Couldn't determine what kind of Linux is running." - echo "You could try the static binaries at:" - echo "https://pkgs.tailscale.com/$TRACK/#static" - ;; - "") - echo "Couldn't determine what operating system you're running." - ;; - *) - echo "$OS $VERSION isn't supported by this script yet." - ;; - esac - echo - echo "If you'd like us to support your system better, please email support@tailscale.com" - echo "and tell us what OS you're running." - echo - echo "Please include the following information we gathered from your system:" - echo - echo "OS=$OS" - echo "VERSION=$VERSION" - echo "PACKAGETYPE=$PACKAGETYPE" - if type uname >/dev/null 2>&1; then - echo "UNAME=$(uname -a)" - else - echo "UNAME=" - fi - echo - if [ -f /etc/os-release ]; then - cat /etc/os-release - else - echo "No /etc/os-release" - fi - exit 1 - fi - - # Step 3: work out if we can run privileged commands, and if so, - # how. - CAN_ROOT= - SUDO= - if [ "$(id -u)" = 0 ]; then - CAN_ROOT=1 - SUDO="" - elif type sudo >/dev/null; then - CAN_ROOT=1 - SUDO="sudo" - elif type doas >/dev/null; then - CAN_ROOT=1 - SUDO="doas" - fi - if [ "$CAN_ROOT" != "1" ]; then - echo "This installer needs to run commands as root." - echo "We tried looking for 'sudo' and 'doas', but couldn't find them." - echo "Either re-run this script as root, or set up sudo/doas." - exit 1 - fi - - - # Step 4: run the installation. - OSVERSION="$OS" - [ "$VERSION" != "" ] && OSVERSION="$OSVERSION $VERSION" - - # Prepare package name with optional version - PACKAGE_NAME="tailscale" - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Installing Tailscale $TAILSCALE_VERSION for $OSVERSION, using method $PACKAGETYPE" - else - echo "Installing Tailscale for $OSVERSION, using method $PACKAGETYPE" - fi - case "$PACKAGETYPE" in - apt) - export DEBIAN_FRONTEND=noninteractive - if [ "$APT_KEY_TYPE" = "legacy" ] && ! type gpg >/dev/null; then - $SUDO apt-get update - $SUDO apt-get install -y gnupg - fi - - set -x - $SUDO mkdir -p --mode=0755 /usr/share/keyrings - case "$APT_KEY_TYPE" in - legacy) - $CURL "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION.asc" | $SUDO apt-key add - - $CURL "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION.list" | $SUDO tee /etc/apt/sources.list.d/tailscale.list - $SUDO chmod 0644 /etc/apt/sources.list.d/tailscale.list - ;; - keyring) - $CURL "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION.noarmor.gpg" | $SUDO tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null - $SUDO chmod 0644 /usr/share/keyrings/tailscale-archive-keyring.gpg - $CURL "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION.tailscale-keyring.list" | $SUDO tee /etc/apt/sources.list.d/tailscale.list - $SUDO chmod 0644 /etc/apt/sources.list.d/tailscale.list - ;; - esac - $SUDO apt-get update - if [ -n "$TAILSCALE_VERSION" ]; then - $SUDO apt-get install -y "tailscale=$TAILSCALE_VERSION" tailscale-archive-keyring - else - $SUDO apt-get install -y tailscale tailscale-archive-keyring - fi - if [ "$APT_SYSTEMCTL_START" = "true" ]; then - $SUDO systemctl enable --now tailscaled - $SUDO systemctl start tailscaled - fi - set +x - ;; - yum) - set -x - $SUDO yum install yum-utils -y - $SUDO yum-config-manager -y --add-repo "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/tailscale.repo" - if [ -n "$TAILSCALE_VERSION" ]; then - $SUDO yum install "tailscale-$TAILSCALE_VERSION" -y - else - $SUDO yum install tailscale -y - fi - $SUDO systemctl enable --now tailscaled - set +x - ;; - dnf) - # DNF 5 has a different argument format; determine which one we have. - DNF_VERSION="3" - if LANG=C.UTF-8 dnf --version | grep -q '^dnf5 version'; then - DNF_VERSION="5" - fi - - # The 'config-manager' plugin wasn't implemented when - # DNF5 was released; detect that and use the old - # version if necessary. - if [ "$DNF_VERSION" = "5" ]; then - set -x - $SUDO dnf install -y 'dnf-command(config-manager)' && DNF_HAVE_CONFIG_MANAGER=1 || DNF_HAVE_CONFIG_MANAGER=0 - set +x - - if [ "$DNF_HAVE_CONFIG_MANAGER" != "1" ]; then - if type dnf-3 >/dev/null; then - DNF_VERSION="3" - else - echo "dnf 5 detected, but 'dnf-command(config-manager)' not available and dnf-3 not found" - exit 1 - fi - fi - fi - - set -x - if [ "$DNF_VERSION" = "3" ]; then - $SUDO dnf install -y 'dnf-command(config-manager)' - $SUDO dnf config-manager --add-repo "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/tailscale.repo" - elif [ "$DNF_VERSION" = "5" ]; then - # Already installed config-manager, above. - $SUDO dnf config-manager addrepo --overwrite --from-repofile="https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/tailscale.repo" - else - echo "unexpected: unknown dnf version $DNF_VERSION" - exit 1 - fi - if [ -n "$TAILSCALE_VERSION" ]; then - $SUDO dnf install -y "tailscale-$TAILSCALE_VERSION" - else - $SUDO dnf install -y tailscale - fi - $SUDO systemctl enable --now tailscaled - set +x - ;; - tdnf) - set -x - curl -fsSL "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/tailscale.repo" > /etc/yum.repos.d/tailscale.repo - if [ -n "$TAILSCALE_VERSION" ]; then - $SUDO tdnf install -y "tailscale-$TAILSCALE_VERSION" - else - $SUDO tdnf install -y tailscale - fi - $SUDO systemctl enable --now tailscaled - set +x - ;; - zypper) - set -x - $SUDO rpm --import "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/repo.gpg" - $SUDO zypper --non-interactive ar -g -r "https://pkgs.tailscale.com/$TRACK/$OS/$VERSION/tailscale.repo" - $SUDO zypper --non-interactive --gpg-auto-import-keys refresh - if [ -n "$TAILSCALE_VERSION" ]; then - $SUDO zypper --non-interactive install "tailscale=$TAILSCALE_VERSION" - else - $SUDO zypper --non-interactive install tailscale - fi - $SUDO systemctl enable --now tailscaled - set +x - ;; - pacman) - set -x - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Warning: Arch Linux maintains their own Tailscale package. Version pinning may not work as expected, as the target version may no longer be available." - $SUDO pacman -S "tailscale=$TAILSCALE_VERSION" --noconfirm - else - $SUDO pacman -S tailscale --noconfirm - fi - $SUDO systemctl enable --now tailscaled - set +x - ;; - pkg) - set -x - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Warning: FreeBSD maintains their own Tailscale package. Version pinning may not work as expected, as the target version may no longer be available." - $SUDO pkg install --yes "tailscale-$TAILSCALE_VERSION" - else - $SUDO pkg install --yes tailscale - fi - $SUDO service tailscaled enable - $SUDO service tailscaled start - set +x - ;; - apk) - set -x - if ! grep -Eq '^http.*/community$' /etc/apk/repositories; then - if type setup-apkrepos >/dev/null; then - $SUDO setup-apkrepos -c -1 - else - echo "installing tailscale requires the community repo to be enabled in /etc/apk/repositories" - exit 1 - fi - fi - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Warning: Alpine Linux maintains their own Tailscale package. Version pinning may not work as expected, as the target version may no longer be available." - $SUDO apk add "tailscale=$TAILSCALE_VERSION" - else - $SUDO apk add tailscale - fi - $SUDO rc-update add tailscale - $SUDO rc-service tailscale start - set +x - ;; - xbps) - set -x - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Warning: Void Linux maintains their own Tailscale package. Version pinning may not work as expected, as the target version may no longer be available." - $SUDO xbps-install "tailscale-$TAILSCALE_VERSION" -y - else - $SUDO xbps-install tailscale -y - fi - set +x - ;; - emerge) - set -x - if [ -n "$TAILSCALE_VERSION" ]; then - echo "Warning: Gentoo maintains their own Tailscale package. Version pinning may not work as expected, as the target version may no longer be available." - $SUDO emerge --ask=n "=net-vpn/tailscale-$TAILSCALE_VERSION" - else - $SUDO emerge --ask=n net-vpn/tailscale - fi - set +x - ;; - appstore) - set -x - open "https://apps.apple.com/us/app/tailscale/id1475387142" - set +x - ;; - *) - echo "unexpected: unknown package type $PACKAGETYPE" - exit 1 - ;; - esac - - echo "Installation complete! Log in to start using Tailscale by running:" - echo - if [ -z "$SUDO" ]; then - echo "tailscale up" - else - echo "$SUDO tailscale up" - fi -} - -main diff --git a/debian/13-trixie-luks/variables-common.pkr.hcl b/debian/13-trixie-luks/variables-common.pkr.hcl deleted file mode 120000 index f97f3d6..0000000 --- a/debian/13-trixie-luks/variables-common.pkr.hcl +++ /dev/null @@ -1 +0,0 @@ -../../variables-common.pkr.hcl \ No newline at end of file diff --git a/debian/13-trixie-luks/variables.pkr.hcl b/debian/13-trixie-luks/variables.pkr.hcl deleted file mode 100644 index 346e5c3..0000000 --- a/debian/13-trixie-luks/variables.pkr.hcl +++ /dev/null @@ -1,67 +0,0 @@ -# Variables -variable "template_vm_id" { - type = string - default = "65000" - description = "The VM ID of the Proxmox template to use for building the image" -} - -variable "mac_address" { - type = string - default = "BC:24:11:00:13:37" - description = "The MAC address to assign to the VM's network adapter" -} - -variable "iso_url" { - type = string - default = "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.3.0-amd64-netinst.iso" - description = "The URL to download the Debian 13 Trixie ISO" -} - -variable "iso_checksum" { - type = string - default = "file:https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA256SUMS" - description = "ISO checksum (SHA256 or checksum file URL)" -} - -variable "disk_storage_pool" { - type = string - default = "ceph-pool" - description = "The Proxmox storage pool to use for the VM disk" -} - -variable "iso_storage_pool" { - type = string - default = "cephfs" - description = "The Proxmox storage pool to use for the ISO" -} - -variable "template_cpu_type" { - type = string - default = "host" - description = "The CPU type for the Proxmox template" -} - -variable "network_bridge" { - type = string - default = "vmbr9" - description = "The network bridge to attach the VM to" -} - -variable "proxmox_node" { - type = string - default = "sbx0pve02" - description = "The Proxmox node to use for building the image" -} - -variable "install_finished_inform_port" { - type = string - default = "10000" - description = "The server port to inform when installation is finished" -} - - - -# local values -local "timestamp" { - expression = formatdate("YYYYMMDD-hhMMss-ZZZ", timestamp()) -} diff --git a/debian/13-trixie/debian-trixie.pkr.hcl b/debian/13-trixie/debian-trixie.pkr.hcl index 0e278b8..8109b80 100644 --- a/debian/13-trixie/debian-trixie.pkr.hcl +++ b/debian/13-trixie/debian-trixie.pkr.hcl @@ -7,6 +7,20 @@ packer { } } +locals { + image_slug = var.enable_luks ? "debian-13-trixie-luks" : "debian-13-trixie" + template_description = var.enable_luks ? "Debian 13 Trixie, LUKS encrypted, built with Packer on ${local.timestamp}\n\nLUKS default passphrase: `${var.default_luks_passphrase}`" : "Debian 13 Trixie, built with Packer on ${local.timestamp}" + luks_boot_args = var.enable_luks ? [ + "partman-crypto/passphrase='${var.default_luks_passphrase}' ", + "partman-crypto/passphrase-again='${var.default_luks_passphrase}' ", + "INSTALL_FINISHED_INFORM_URL='http://{{ .HTTPIP }}:${var.install_finished_inform_port}/install_finished' ", + ] : [] + clevis_install_commands = var.enable_luks ? [ + "apt-get update", + "apt-get install -y clevis clevis-luks clevis-initramfs", + ] : ["true"] +} + source "proxmox-iso" "debian-13-trixie" { # Proxmox Connection Settings proxmox_url = "${var.proxmox_api_url}" @@ -19,8 +33,8 @@ source "proxmox-iso" "debian-13-trixie" { # VM General Settings node = "${var.proxmox_node}" vm_id = "${var.template_vm_id}" - vm_name = "debian-13-trixie-${local.timestamp}" - template_description = "Debian 13 Trixie, built with Packer on ${local.timestamp}" + vm_name = "${local.image_slug}-${local.timestamp}" + template_description = "${local.template_description}" os = "l26" qemu_agent = true @@ -78,42 +92,26 @@ source "proxmox-iso" "debian-13-trixie" { boot = "order=scsi0;scsi1" boot_wait = "10s" communicator = "ssh" - boot_command = [ + boot_command = concat([ "c", "linux /install.amd/vmlinuz auto-install/enable=true priority=critical ", "DEBIAN_FRONTEND=text ", "console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0 ", "passwd/root-password='${var.default_root_passphrase}' ", "passwd/root-password-again='${var.default_root_passphrase}' ", + ], local.luks_boot_args, [ "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg noprompt", "initrd /install.amd/initrd.gz", "DEBCONF_DEBUG=5", - "boot" - ] - # Static IP - # boot_command = [ - # "c", - # "linux /install.amd/vmlinuz auto-install/enable=true priority=critical ", - # "DEBIAN_FRONTEND=text ", - # "console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0 ", - # "passwd/root-password='${var.default_root_passphrase}' ", - # "passwd/root-password-again='${var.default_root_passphrase}' ", - # "netcfg/disable_autoconfig=true ", - # "netcfg/get_ipaddress=172.16.2.254 ", - # "netcfg/get_netmask=255.255.255.0 ", - # "netcfg/get_gateway=172.16.2.3 ", - # "netcfg/get_nameservers=172.16.2.3 ", - # "netcfg/confirm_static=true ", - # "netcfg/get_hostname=debian-installer ", - # "netcfg/get_domain=local ", - # "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg noprompt", - # "initrd /install.amd/initrd.gz", - # "DEBCONF_DEBUG=5", - # "boot" - # ] + "boot", + ]) # PACKER Autoinstall Settings - http_directory = "debian/13-trixie/http" + http_content = { + "/preseed.cfg" = templatefile("${abspath(path.root)}/http/preseed.cfg.pkrtpl", { + enable_luks = var.enable_luks + }) + } http_interface = "${var.source_proxmox_http_interface}" # SSH Settings @@ -124,7 +122,7 @@ source "proxmox-iso" "debian-13-trixie" { } build { - name = "debian-13-trixie-image" + name = "${local.image_slug}-image" sources = ["source.proxmox-iso.debian-13-trixie"] # Install dependencies and default packages @@ -197,6 +195,11 @@ build { ] } + # Install Clevis for LUKS builds + provisioner "shell" { + inline = local.clevis_install_commands + } + # Setup Serial Console for xterm.js in Proxmox VE provisioner "shell" { inline = [ diff --git a/debian/13-trixie/files/initial-setup.sh b/debian/13-trixie/files/initial-setup.sh index b78d695..acd62f6 100644 --- a/debian/13-trixie/files/initial-setup.sh +++ b/debian/13-trixie/files/initial-setup.sh @@ -5,12 +5,18 @@ IFS=$'\n\t' SCRIPT_NAME="$(basename "$0")" TASK_INDEX=0 TASK_TOTAL=0 +TEMP_FILES=() STORAGE_DETECTED=0 +STORAGE_MODE="" ROOT_SOURCE="" VG_NAME="" LV_NAME="" PV_NAME="" +LUKS_DEV="" +LUKS_NAME="" +LUKS_PART="" +RESIZE_PART="" DISK_DEV="" PART_NUM="" @@ -56,6 +62,18 @@ section() { echo "${DIM}------------------------------------------------------------${RESET}" } +add_temp_file() { + TEMP_FILES+=("$1") +} + +cleanup() { + local file + for file in "${TEMP_FILES[@]:-}"; do + [ -f "$file" ] && rm -f "$file" + done +} +trap cleanup EXIT + ensure_tty() { if [ ! -t 0 ] || [ ! -t 1 ]; then die "This setup must run interactively in a TTY." @@ -227,6 +245,29 @@ infer_disk_part_from_partition() { return 1 } +resolve_luks_backing_partition() { + local mapper_path="$1" + local mapper_name="$2" + local mapper_name_alt="$3" + local part="" + + if command -v cryptsetup >/dev/null 2>&1; then + part="$(cryptsetup status "$mapper_name" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" + if [ -z "$part" ] && [ -n "$mapper_name_alt" ] && [ "$mapper_name_alt" != "$mapper_name" ]; then + part="$(cryptsetup status "$mapper_name_alt" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" + fi + if [ -z "$part" ]; then + part="$(cryptsetup status "$mapper_path" 2>/dev/null | awk '/^[[:space:]]*device:/ {print $2; exit}' | xargs || true)" + fi + fi + + if [ -z "$part" ]; then + part="$(lsblk -nro PATH,TYPE -s "$mapper_path" 2>/dev/null | awk '$2=="part" {print $1; exit}' | xargs || true)" + fi + + printf '%s' "$part" +} + refresh_partition_table() { local disk="$1" if command -v partprobe >/dev/null 2>&1; then @@ -324,8 +365,20 @@ show_disk_layout() { fi } +resize_open_luks_mapping() { + if [ -n "$LUKS_NAME" ] && cryptsetup resize "$LUKS_NAME" >/dev/null 2>&1; then + return 0 + fi + cryptsetup resize "$LUKS_DEV" >/dev/null 2>&1 +} + detect_storage_stack() { STORAGE_DETECTED=0 + STORAGE_MODE="" + LUKS_DEV="" + LUKS_NAME="" + LUKS_PART="" + RESIZE_PART="" DISK_DEV="" PART_NUM="" @@ -358,10 +411,29 @@ detect_storage_stack() { return 1 fi + local pv_type + pv_type="$(lsblk_attr "$PV_NAME" TYPE)" + if [ "$pv_type" = "crypt" ]; then + STORAGE_MODE="luks_lvm" + LUKS_DEV="$PV_NAME" + LUKS_NAME="$(basename "$LUKS_DEV")" + local mapper_name_alt + mapper_name_alt="$(lsblk_attr "$LUKS_DEV" NAME)" + LUKS_PART="$(resolve_luks_backing_partition "$LUKS_DEV" "$LUKS_NAME" "$mapper_name_alt")" + if [ -z "$LUKS_PART" ] || [ ! -b "$LUKS_PART" ]; then + log_warn "Unable to detect LUKS backing partition." + return 1 + fi + RESIZE_PART="$LUKS_PART" + else + STORAGE_MODE="lvm" + RESIZE_PART="$PV_NAME" + fi + local inferred local inferred_disk="" local inferred_part="" - inferred="$(infer_disk_part_from_partition "$PV_NAME" || true)" + inferred="$(infer_disk_part_from_partition "$RESIZE_PART" || true)" if [ -n "$inferred" ]; then inferred_disk="$(printf '%s\n' "$inferred" | sed -n '1p')" inferred_part="$(printf '%s\n' "$inferred" | sed -n '2p')" @@ -372,21 +444,21 @@ detect_storage_stack() { if [ -z "$DISK_DEV" ]; then local disk_parent - disk_parent="$(lsblk_attr "$PV_NAME" PKNAME)" + disk_parent="$(lsblk_attr "$RESIZE_PART" PKNAME)" if [ -n "$disk_parent" ]; then DISK_DEV="/dev/${disk_parent}" else - DISK_DEV="$(lsblk -nro PATH,TYPE -s "$PV_NAME" 2>/dev/null | awk '$2=="disk" {print $1; exit}' | xargs || true)" + DISK_DEV="$(lsblk -nro PATH,TYPE -s "$RESIZE_PART" 2>/dev/null | awk '$2=="disk" {print $1; exit}' | xargs || true)" fi fi if [ -z "$PART_NUM" ]; then - PART_NUM="$(lsblk_attr "$PV_NAME" PARTNUM)" + PART_NUM="$(lsblk_attr "$RESIZE_PART" PARTNUM)" fi if [ -z "$DISK_DEV" ] || [ ! -b "$DISK_DEV" ] || [ -z "$PART_NUM" ] || ! [[ "$PART_NUM" =~ ^[0-9]+$ ]]; then log_warn "Unable to detect disk device or partition number." - log_warn "Detected values: PV_NAME=${PV_NAME:-} DISK_DEV=${DISK_DEV:-} PART_NUM=${PART_NUM:-}" + log_warn "Detected values: RESIZE_PART=${RESIZE_PART:-} DISK_DEV=${DISK_DEV:-} PART_NUM=${PART_NUM:-}" return 1 fi @@ -394,7 +466,7 @@ detect_storage_stack() { return 0 } -resize_lvm() { +resize_lvm_storage() { if [ "$STORAGE_DETECTED" -ne 1 ]; then log_warn "Storage layout not detected; skipping resize." return 0 @@ -402,12 +474,21 @@ resize_lvm() { require_cmd parted || return 0 require_cmd blockdev || return 0 + if [ "$STORAGE_MODE" = "luks_lvm" ]; then + require_cmd cryptsetup || return 0 + fi require_cmd pvresize || return 0 require_cmd lvextend || return 0 log_info "Root LV: ${ROOT_SOURCE}" log_info "VG: ${VG_NAME} | LV: ${LV_NAME}" + log_info "Storage mode: ${STORAGE_MODE}" log_info "LVM PV: ${PV_NAME}" + if [ "$STORAGE_MODE" = "luks_lvm" ]; then + log_info "LUKS device: ${LUKS_DEV}" + log_info "LUKS partition: ${LUKS_PART}" + [ -n "$LUKS_NAME" ] && log_info "LUKS mapper name: ${LUKS_NAME}" + fi log_info "Disk: ${DISK_DEV} | Partition number: ${PART_NUM}" if ! repair_gpt_backup_header "$DISK_DEV"; then @@ -422,7 +503,7 @@ resize_lvm() { local part_size_before local free_bytes - part_size_before="$(blockdev --getsize64 "$PV_NAME" 2>/dev/null || echo 0)" + part_size_before="$(blockdev --getsize64 "$RESIZE_PART" 2>/dev/null || echo 0)" free_bytes="$(get_trailing_free_bytes "$DISK_DEV" "$PART_NUM" || true)" if [ -z "$free_bytes" ]; then log_warn "Unable to determine free disk space." @@ -436,22 +517,36 @@ resize_lvm() { log_info "Unallocated space available: $(human_bytes "$free_bytes")" show_disk_layout "$DISK_DEV" - if ! confirm "Extend partition, VG, and root LV now?" "yes"; then + local resize_prompt + if [ "$STORAGE_MODE" = "luks_lvm" ]; then + resize_prompt="Extend partition, LUKS device, VG, and root LV now?" + else + resize_prompt="Extend partition, VG, and root LV now?" + fi + if ! confirm "$resize_prompt" "yes"; then log_warn "Skipped resize." return 0 fi - log_info "Extending partition ${PV_NAME} to 100% of disk..." + log_info "Extending partition ${RESIZE_PART} to 100% of disk..." if ! parted -s "$DISK_DEV" resizepart "$PART_NUM" 100%; then log_warn "Partition resize failed." return 0 fi refresh_partition_table "$DISK_DEV" - if [ "$part_size_before" -gt 0 ] && ! wait_for_partition_growth "$PV_NAME" "$part_size_before"; then + if [ "$part_size_before" -gt 0 ] && ! wait_for_partition_growth "$RESIZE_PART" "$part_size_before"; then log_warn "Kernel has not reported the new partition size yet; continuing anyway." fi + if [ "$STORAGE_MODE" = "luks_lvm" ]; then + log_info "Resizing LUKS device..." + if ! resize_open_luks_mapping; then + log_warn "LUKS resize failed." + return 0 + fi + fi + log_info "Resizing LVM physical volume..." if ! pvresize "$PV_NAME"; then log_warn "pvresize failed." @@ -549,6 +644,62 @@ setup_swap() { log_ok "Swap enabled." } +change_luks_passphrase() { + if [ "$STORAGE_MODE" != "luks_lvm" ]; then + log_info "Root storage is not LUKS-backed; skipping LUKS passphrase change." + return 0 + fi + if [ -z "$LUKS_PART" ] || [ ! -e "$LUKS_PART" ]; then + log_warn "LUKS partition not found; skipping." + return 0 + fi + require_cmd cryptsetup || return 0 + + if ! confirm "Change LUKS passphrase in slot 0 now?" "yes"; then + log_warn "Skipped LUKS passphrase change." + return 0 + fi + + local old_pass + local new_pass + local tmp_old + local tmp_new + + old_pass="$(prompt_secret "Enter current LUKS passphrase")" + new_pass="$(prompt_secret_confirm "Enter new LUKS passphrase" "Confirm new LUKS passphrase")" + + tmp_old="$(mktemp)" + tmp_new="$(mktemp)" + add_temp_file "$tmp_old" + add_temp_file "$tmp_new" + + printf '%s' "$old_pass" >"$tmp_old" + printf '%s' "$new_pass" >"$tmp_new" + + log_info "Updating LUKS passphrase in slot 0..." + if cryptsetup luksChangeKey --batch-mode --key-slot 0 --key-file "$tmp_old" "$LUKS_PART" "$tmp_new"; then + log_ok "LUKS passphrase updated." + else + log_warn "Failed to update LUKS passphrase." + fi +} + +setup_clevis() { + if [ "$STORAGE_MODE" != "luks_lvm" ]; then + log_info "Root storage is not LUKS-backed; skipping Clevis/Tang setup." + return 0 + fi + + log_info "Clevis/Tang setup is not implemented in this template yet." + if confirm "Would you like to configure Clevis with a Tang server now? (will be skipped)" "no"; then + local tang + tang="$(prompt_input "Tang server URL" "http://tang.int.r3w.de")" + log_warn "Clevis setup for ${tang} is not implemented yet. Skipping." + else + log_info "Skipping Clevis setup." + fi +} + setup_tailscale() { if ! require_cmd tailscale; then log_warn "Tailscale is not installed; skipping." @@ -639,7 +790,7 @@ main() { welcome if detect_storage_stack; then - log_ok "Detected LVM storage layout." + log_ok "Detected ${STORAGE_MODE} storage layout." else log_warn "Storage layout detection incomplete; some steps may be skipped." fi @@ -647,8 +798,12 @@ main() { TASK_TITLES=() TASK_FUNCS=() - add_task "Resize LVM (if free space exists)" resize_lvm + add_task "Resize LVM storage (if free space exists)" resize_lvm_storage add_task "Configure swap file" setup_swap + if [ "$STORAGE_MODE" = "luks_lvm" ]; then + add_task "Change LUKS passphrase (slot 0)" change_luks_passphrase + add_task "Clevis/Tang setup (placeholder)" setup_clevis + fi add_task "Configure Tailscale" setup_tailscale add_task "Configure CrowdSec" setup_crowdsec add_task "Reboot recommendation" prompt_reboot diff --git a/debian/13-trixie/http/preseed.cfg b/debian/13-trixie/http/preseed.cfg deleted file mode 100644 index 39a08dd..0000000 --- a/debian/13-trixie/http/preseed.cfg +++ /dev/null @@ -1,161 +0,0 @@ -#_preseed_V1 - -### Localization -d-i debian-installer/locale string en_US.UTF-8 -d-i keyboard-configuration/xkb-keymap select us - -### Unattended -d-i auto-install/enable boolean true -d-i debconf/priority select critical -d-i debian-installer/framebuffer boolean false - -### Network -d-i netcfg/choose_interface select ens18 -d-i netcfg/get_hostname string debian-13-template -d-i netcfg/get_domain string -d-i netcfg/wireless_wep string -d-i netcfg/disable_dhcp boolean false - -### Root Password (no user) -d-i passwd/make-user boolean false -# Root password is set via kernel cmdline in debian-trixie.pkr.hcl; these lines are ignored but left here for reference: -# d-i passwd/root-password password "$PACKER_ROOT_PASS" -# d-i passwd/root-password-again password "$PACKER_ROOT_PASS" - -### Mirror / APT -d-i apt-setup/cdrom/set-first boolean false -d-i apt-setup/cdrom/set-next boolean false -d-i apt-setup/cdrom/set-failed boolean false - -d-i mirror/country string manual -d-i mirror/http/hostname string ftp.de.debian.org -d-i mirror/http/directory string /debian -d-i mirror/http/proxy string - -# If you want an explicit suite: -# d-i mirror/suite string trixie - -# Your extra repo line (updated to also include non-free-firmware for trixie) -d-i apt-setup/local0/repository string http://ftp.de.debian.org/debian/ trixie main contrib non-free non-free-firmware -popularity-contest popularity-contest/participate boolean false - -d-i apt-setup/contrib boolean true -d-i apt-setup/non-free boolean true -d-i apt-setup/non-free-firmware boolean true -d-i apt-setup/security_host string security.debian.org -d-i apt-setup/services-select multiselect security, updates - -### Timezone -d-i clock-setup/utc boolean true -d-i time/zone string UTC -d-i clock-setup/ntp boolean true - -### Storage (UEFI + /boot + LUKS/LVM) -# Pick first detected disk automatically -d-i preseed/early_command string \ - DISK="$(list-devices disk | head -n1)"; \ - debconf-set partman-auto/disk "$DISK"; - -# "crypto" = LVM within an encrypted partition [oai_citation:3‡Debian](https://www.debian.org/releases/stable/amd64/apbs04.en.html) -# "lvm" = LVM without encryption -d-i partman-auto/method string lvm -d-i partman-auto-lvm/guided_size string max -d-i partman-auto-lvm/new_vg_name string vg0 - -# Cleanup old metadata if present -d-i partman-lvm/device_remove_lvm boolean true -d-i partman-md/device_remove_md boolean true - -# Confirmations -d-i partman-lvm/confirm boolean true -d-i partman-lvm/confirm_nooverwrite boolean true -d-i partman-md/confirm boolean true -d-i partman-md/confirm_nooverwrite boolean true - -# # LUKS password -# # LUKS passphrase is set via kernel cmdline in debian-trixie.pkr.hcl; these lines are ignored but left here for reference: -# # d-i partman-crypto/passphrase password "$PACKER_LUKS_PASS" -# # d-i partman-crypto/passphrase-again password "$PACKER_LUKS_PASS" -# d-i partman-crypto/weak_passphrase boolean true -# d-i partman-crypto/confirm boolean true -# d-i partman-auto-crypto/erase_disks boolean false - -# Ensure GPT -d-i partman-partitioning/choose_label select gpt -d-i partman-partitioning/default_label string gpt - -# Force UEFI (if needed in your environment) -d-i partman-efi/non_efi_system boolean true - -# Do NOT go back to partitioning menu if a partition/LV has no filesystem -d-i partman-basicmethods/method_only boolean false - -# Accept installing without swap (answer "No" to the warning) -d-i partman-basicfilesystems/no_swap boolean false -d-i partman-basicfilesystems/no_swap seen true - -# Custom recipe -# NOTE: Avoid putting comments inside this expert_recipe block; d-i can ignore it. [oai_citation:4‡Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/796185/debian-preseed-install-auto-creates-swap) -d-i partman-auto/choose_recipe select luks-lvm -d-i partman-auto/expert_recipe string \ - luks-lvm :: \ - 1075 1075 1075 fat32 \ - $primary{ } \ - $iflabel{ gpt } \ - $reusemethod{ } \ - method{ efi } \ - format{ } \ - filesystem{ fat32 } \ - mountpoint{ /boot/efi } \ - . \ - 1075 1075 1075 ext4 \ - $primary{ } \ - $defaultignore{ } \ - method{ format } \ - format{ } \ - use_filesystem{ } \ - filesystem{ ext4 } \ - mountpoint{ /boot } \ - . \ - 25770 25770 -1 ext4 \ - $lvmok{ } \ - lv_name{ root } \ - method{ format } \ - format{ } \ - use_filesystem{ } \ - filesystem{ ext4 } \ - mountpoint{ / } \ - . - -d-i partman-partitioning/confirm_write_new_label boolean true -d-i partman/choose_partition select finish -d-i partman/confirm boolean true -d-i partman/confirm_nooverwrite boolean true - -### Software -d-i debconf/frontend select noninteractive -tasksel tasksel/first multiselect standard, ssh-server - -d-i pkgsel/include string cloud-init curl qemu-guest-agent sudo vim -d-i pkgsel/upgrade select full-upgrade -d-i pkgsel/update-policy select none -d-i pkgsel/updatedb boolean true - -### Bootloader -d-i grub-installer/only_debian boolean true -d-i grub-installer/with_other_os boolean true -d-i grub-installer/bootdev string default - -### Late command (single declaration; multiple commands chained) -# 1) Remove filler LV so vg0 has free space after install -# 2) Enable root ssh login (same intent as your original) -d-i preseed/late_command string \ - lvremove -f /dev/vg0/reserved || true; \ - in-target sed -i 's/^#PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config || true - -# Eject the installation media before rebooting -d-i cdrom-detect/eject boolean true -d-i cdrom-detect/eject seen true - -### Finish -d-i finish-install/reboot_in_progress note diff --git a/debian/13-trixie-luks/http/preseed.cfg b/debian/13-trixie/http/preseed.cfg.pkrtpl similarity index 94% rename from debian/13-trixie-luks/http/preseed.cfg rename to debian/13-trixie/http/preseed.cfg.pkrtpl index b93751e..ed632f4 100644 --- a/debian/13-trixie-luks/http/preseed.cfg +++ b/debian/13-trixie/http/preseed.cfg.pkrtpl @@ -57,7 +57,8 @@ d-i preseed/early_command string \ debconf-set partman-auto/disk "$DISK"; # "crypto" = LVM within an encrypted partition [oai_citation:3‡Debian](https://www.debian.org/releases/stable/amd64/apbs04.en.html) -d-i partman-auto/method string crypto +# "lvm" = LVM without encryption +d-i partman-auto/method string ${enable_luks ? "crypto" : "lvm"} d-i partman-auto-lvm/guided_size string max d-i partman-auto-lvm/new_vg_name string vg0 @@ -71,6 +72,7 @@ d-i partman-lvm/confirm_nooverwrite boolean true d-i partman-md/confirm boolean true d-i partman-md/confirm_nooverwrite boolean true +%{ if enable_luks ~} # LUKS password # LUKS passphrase is set via kernel cmdline in debian-trixie.pkr.hcl; these lines are ignored but left here for reference: # d-i partman-crypto/passphrase password "$PACKER_LUKS_PASS" @@ -78,6 +80,7 @@ d-i partman-md/confirm_nooverwrite boolean true d-i partman-crypto/weak_passphrase boolean true d-i partman-crypto/confirm boolean true d-i partman-auto-crypto/erase_disks boolean false +%{ endif ~} # Ensure GPT d-i partman-partitioning/choose_label select gpt @@ -95,9 +98,9 @@ d-i partman-basicfilesystems/no_swap seen true # Custom recipe # NOTE: Avoid putting comments inside this expert_recipe block; d-i can ignore it. [oai_citation:4‡Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/796185/debian-preseed-install-auto-creates-swap) -d-i partman-auto/choose_recipe select luks-lvm +d-i partman-auto/choose_recipe select debian-lvm d-i partman-auto/expert_recipe string \ - luks-lvm :: \ + debian-lvm :: \ 1075 1075 1075 fat32 \ $primary{ } \ $iflabel{ gpt } \ @@ -150,8 +153,8 @@ d-i grub-installer/bootdev string default # 2) Enable root ssh login (same intent as your original) d-i preseed/late_command string \ lvremove -f /dev/vg0/reserved || true; \ - in-target sed -i 's/^#PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config || true; \ - in-target curl -X POST "$INSTALL_FINISHED_INFORM_URL" + in-target sed -i 's/^#PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config || true%{ if enable_luks }; \ + in-target curl -X POST "$INSTALL_FINISHED_INFORM_URL"%{ endif } # Eject the installation media before rebooting d-i cdrom-detect/eject boolean true diff --git a/debian/13-trixie/variables.pkr.hcl b/debian/13-trixie/variables.pkr.hcl index ede3f32..5a20bc5 100644 --- a/debian/13-trixie/variables.pkr.hcl +++ b/debian/13-trixie/variables.pkr.hcl @@ -59,7 +59,11 @@ variable "install_finished_inform_port" { description = "The server port to inform when installation is finished" } - +variable "enable_luks" { + type = bool + default = false + description = "Build Debian with LUKS encrypted root storage." +} # local values local "timestamp" { diff --git a/debian/13-trixie/variants/luks.pkrvars.hcl b/debian/13-trixie/variants/luks.pkrvars.hcl new file mode 100644 index 0000000..e19e059 --- /dev/null +++ b/debian/13-trixie/variants/luks.pkrvars.hcl @@ -0,0 +1,2 @@ +enable_luks = true +template_vm_id = "65001" diff --git a/mise.toml b/mise.toml index 9f90a58..db54c10 100644 --- a/mise.toml +++ b/mise.toml @@ -17,14 +17,15 @@ run = "packer fmt --recursive ${usage_dir?}" [tasks.build] usage = ''' arg "" help="Directory containing the Packer template to build e.g. debian/13-trixie" +flag "--var-file " help="HCL var-file to pass to Packer" flag "-i --install-finished-inform-port " help="Server port to inform when installation is finished" hide=#true ''' run = ''' mise run lint ${usage_dir?} -[[ -z "${usage_install_finished_inform_port}" ]] && packer build ${usage_dir?} || true -[[ -n "${usage_install_finished_inform_port}" ]] && packer build \ - -var "install_finished_inform_port=${usage_install_finished_inform_port?}" \ - ${usage_dir?} || true +build_args=() +[[ -n "${usage_var_file}" ]] && build_args+=("-var-file=${usage_var_file?}") +[[ -n "${usage_install_finished_inform_port}" ]] && build_args+=("-var" "install_finished_inform_port=${usage_install_finished_inform_port?}") +packer build "${build_args[@]}" ${usage_dir?} || true ''' [tasks.build-luks] @@ -33,7 +34,7 @@ arg "" help="Directory containing the Packer template to build e.g. debian/ flag "--luks-wait-seconds " help="Seconds to wait for the LUKS password prompt after installation before sending the password" default="45" ''' run = ''' -_scripts/unlock-luks-after-install.py -t ${usage_dir?} --luks-wait-seconds ${usage_luks_wait_seconds?} +_scripts/unlock-luks-after-install.py -t ${usage_dir?} --var-file ${usage_dir?}/variants/luks.pkrvars.hcl --luks-wait-seconds ${usage_luks_wait_seconds?} ''' [tasks.init]