diff --git a/README.md b/README.md index ec81244..303a561 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Ansible Bootstrap -An Ansible playbook for automating Linux system bootstrap in an Infrastructure-as-Code manner. It uses the Arch Linux ISO as a foundational tool to provide an efficient and systematic method for the automatic deployment of a variety of Linux distributions on designated target systems, ensuring a standardized setup across different platforms. +Automated Linux system bootstrap using the Arch Linux ISO as a universal installer. Deploys any supported distribution on virtual or physical targets via Infrastructure-as-Code. -Most roles are adaptable for use with systems beyond Arch Linux, requiring only that the target system can install the necessary package manager (e.g. `dnf` for RHEL-based systems). A replacement for the `arch-chroot` command may also be required; set `system.features.chroot.tool` accordingly. +Non-Arch targets require the appropriate package manager available from the ISO environment (e.g. `dnf` for RHEL-family). Set `system.features.chroot.tool` if `arch-chroot` is unavailable. ## Table of Contents @@ -13,34 +13,33 @@ Most roles are adaptable for use with systems beyond Arch Linux, requiring only - 4.1 [Core Variables](#41-core-variables) - 4.2 [`system` Dictionary](#42-system-dictionary) - 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary) - - 4.4 [VMware Guest Operations](#44-vmware-guest-operations) - - 4.5 [Multi-Disk Schema](#45-multi-disk-schema) - - 4.6 [Advanced Partitioning Overrides](#46-advanced-partitioning-overrides) -5. [How to Use the Playbook](#5-how-to-use-the-playbook) - - 5.1 [Prerequisites](#51-prerequisites) - - 5.2 [Running the Playbook](#52-running-the-playbook) - - 5.3 [Example Usage](#53-example-usage) -6. [Security](#6-security) -7. [Operational Notes](#7-operational-notes) + - 4.4 [`cis` Dictionary](#44-cis-dictionary) + - 4.5 [VMware Guest Operations](#45-vmware-guest-operations) + - 4.6 [Multi-Disk Schema](#46-multi-disk-schema) + - 4.7 [Advanced Partitioning Overrides](#47-advanced-partitioning-overrides) + - 4.8 [Cleanup Defaults](#48-cleanup-defaults) +5. [Execution Pipeline](#5-execution-pipeline) +6. [Usage](#6-usage) +7. [Security](#7-security) 8. [Safety](#8-safety) ## 1. Supported Platforms ### Distributions -| `system.os` | Distribution | `system.version` | -| ------------ | ------------------------ | ------------------------------- | -| `almalinux` | AlmaLinux | `8`, `9`, `10` | -| `alpine` | Alpine Linux | latest (rolling) | -| `archlinux` | Arch Linux | latest (rolling) | -| `debian` | Debian | `10`, `11`, `12`, `13`, `unstable` | -| `fedora` | Fedora | `40`, `41`, `42`, `43` | -| `opensuse` | openSUSE Tumbleweed | latest (rolling) | -| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` | -| `rocky` | Rocky Linux | `8`, `9`, `10` | -| `ubuntu` | Ubuntu | latest | -| `ubuntu-lts` | Ubuntu LTS | latest | -| `void` | Void Linux | latest (rolling) | +| `system.os` | Distribution | `system.version` | +| ------------ | ------------------------ | ------------------------------------- | +| `almalinux` | AlmaLinux | `8`, `9`, `10` | +| `alpine` | Alpine Linux | latest (rolling) | +| `archlinux` | Arch Linux | latest (rolling) | +| `debian` | Debian | `10`-`13`, `unstable` | +| `fedora` | Fedora | `38`-`45` | +| `opensuse` | openSUSE Tumbleweed | latest (rolling) | +| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` | +| `rocky` | Rocky Linux | `8`, `9`, `10` | +| `ubuntu` | Ubuntu (latest non-LTS) | optional (e.g. `24.04`) | +| `ubuntu-lts` | Ubuntu LTS | optional (e.g. `24.04`) | +| `void` | Void Linux | latest (rolling) | ### Hypervisors @@ -55,28 +54,28 @@ Most roles are adaptable for use with systems beyond Arch Linux, requiring only ## 2. Compatibility Notes - `rhel_iso` is required for `system.os: rhel`. -- RHEL installs should use `system.filesystem: ext4` or `system.filesystem: xfs` (not `btrfs`). -- For RHEL 8 specifically, prefer `ext4` over `xfs` if you hit installer/filesystem edge cases. -- `custom_iso: true` skips ArchISO validation and pacman preparation; your installer image must already provide required tooling. -- On non-Arch installers, set `system.features.chroot.tool` (`arch-chroot`, `chroot`, or `systemd-nspawn`) explicitly as needed. +- RHEL installs should use `ext4` or `xfs` (not `btrfs`). +- `custom_iso: true` skips ArchISO validation; your installer must provide required tooling. +- On non-Arch installers, set `system.features.chroot.tool` explicitly. ## 3. Configuration Model -The project uses two dict-based variables: +Two dict-based variables drive the entire configuration: -- `system` for host/runtime/install configuration -- `hypervisor` for virtualization backend configuration +- **`system`** -- host, network, users, disk layout, encryption, and feature toggles +- **`hypervisor`** -- virtualization backend credentials and targeting -These are normal Ansible variables and belong in host/group vars. You can define them in inventory host entries, `group_vars/*`, or `host_vars/*`. Dictionary variables are merged across scopes (`group_vars` -> `host_vars`) by project config (`hash_behaviour = merge`), so you can set shared values like `system.filesystem` once in group vars and override only host-specific keys per host. +An optional third dict **`cis`** overrides CIS hardening parameters when `system.features.cis.enabled: true`. + +All three are standard Ansible variables. Place them in `group_vars/`, `host_vars/`, or inline inventory. With `hash_behaviour = merge`, dictionaries merge across scopes, so shared values go in group vars and host-specific overrides go per-host. ### Variable Placement -| Location | Scope | Typical use | -| -------------------------- | ----------- | ------------------------------------------------------------ | -| `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` | -| `group_vars/.yml` | Group | Environment or role-specific defaults | -| `host_vars/.yml` | Single host | Host-specific overrides | -| Inventory inline host vars | Single host | Inline definitions for quick setup | +| Location | Scope | Typical use | +| ------------------------ | ----------- | -------------------------------------------------------------- | +| `group_vars/all.yml` | All hosts | Shared `hypervisor`, `system.filesystem`, `boot_iso` | +| `group_vars/.yml` | Group | Environment-specific defaults | +| `host_vars/.yml` | Single host | Host-specific overrides (`system.network.ip`, `system.id`, etc.) | ### Example Inventory @@ -90,7 +89,8 @@ all: type: proxmox url: pve01.example.com username: root@pam - password: CHANGE_ME + password: !vault | + $ANSIBLE_VAULT... host: pve01 storage: local-lvm @@ -107,7 +107,6 @@ all: id: 101 cpus: 2 memory: 4096 - balloon: 0 network: bridge: vmbr0 ip: 10.0.0.10 @@ -124,19 +123,24 @@ all: fstype: xfs users: - name: ops - password: CHANGE_ME + password: !vault | + $ANSIBLE_VAULT... keys: - "ssh-ed25519 AAAA..." + sudo: true root: - password: CHANGE_ME + password: !vault | + $ANSIBLE_VAULT... luks: enabled: true - passphrase: CHANGE_ME - auto: true + passphrase: !vault | + $ANSIBLE_VAULT... method: tpm2 tpm2: pcrs: "7" features: + cis: + enabled: true firewall: enabled: true backend: firewalld @@ -147,270 +151,265 @@ all: ### 4.1 Core Variables -These top-level variables sit outside the `system`/`hypervisor` dictionaries. +Top-level variables outside `system`/`hypervisor`/`cis`. -| Variable | Type | Description | -| ----------------------------------- | ------ | ------------------------------------------------ | -| `boot_iso` | string | Path to the boot ISO image (required for virtual installs). | -| `rhel_iso` | string | Path to the RHEL ISO (required when `system.os: rhel`). | -| `custom_iso` | bool | Skip ArchISO validation and pacman setup. Default `false`. | -| `thirdparty_tasks` | string | Drop-in task file included during environment setup. Default `dropins/preparation.yml`. | +| Variable | Type | Default | Description | +| ---------------- | ------ | -------------------------- | ---------------------------------------------------- | +| `boot_iso` | string | -- | Boot ISO path (required for virtual installs) | +| `rhel_iso` | string | -- | RHEL ISO path (required when `system.os: rhel`) | +| `custom_iso` | bool | `false` | Skip ArchISO validation and pacman setup | +| `thirdparty_tasks` | string | `dropins/preparation.yml` | Drop-in task file included during environment setup | ### 4.2 `system` Dictionary -Top-level host install/runtime settings. Use these keys under `system`. - -| Key | Type | Default | Description | -| ------------ | ---------- | -------------------- | ---------------------------------------- | -| `type` | string | `virtual` | `virtual` or `physical` | -| `os` | string | empty | Target distribution (see [table](#distributions)) | -| `version` | string | empty | Version selector for distro families | -| `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` | -| `name` | string | inventory hostname | Final hostname | -| `timezone` | string | `Europe/Vienna` | System timezone (tz database name) | -| `locale` | string | `en_US.UTF-8` | System locale | -| `keymap` | string | `us` | Console keymap (`vconsole.conf`) | -| `id` | int/string | empty | VMID (required for Proxmox) | -| `cpus` | int | `0` | vCPU count | -| `memory` | int | `0` | Memory in MiB | -| `balloon` | int | `0` | Balloon memory in MiB | -| `path` | string | empty | Hypervisor folder/path (libvirt/vmware) | -| `packages` | list | `[]` | Additional packages installed post-reboot | -| `network` | dict | see below | Network configuration | -| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#45-multi-disk-schema)) | -| `users` | list | `[]` | User accounts (see below) | -| `root` | dict | see below | Root account settings | -| `luks` | dict | see below | Encryption settings | -| `features` | dict | see below | Feature toggles | +| Key | Type | Default | Description | +| ------------ | ---------- | ------------------ | ------------------------------------------------------ | +| `type` | string | `virtual` | `virtual` or `physical` | +| `os` | string | -- | Target distribution (see [table](#distributions)) | +| `version` | string | -- | Version selector for versioned distros | +| `filesystem` | string | -- | `btrfs`, `ext4`, or `xfs` | +| `name` | string | inventory hostname | Final hostname | +| `timezone` | string | `Europe/Vienna` | System timezone (tz database name) | +| `locale` | string | `en_US.UTF-8` | System locale | +| `keymap` | string | `us` | Console keymap | +| `id` | int/string | -- | VMID (required for Proxmox) | +| `cpus` | int | `0` | vCPU count (required for virtual) | +| `memory` | int | `0` | Memory in MiB (required for virtual) | +| `balloon` | int | `0` | Balloon memory in MiB (Proxmox) | +| `path` | string | -- | Hypervisor folder/path | +| `packages` | list | `[]` | Additional packages installed post-reboot | +| `network` | dict | see below | Network configuration | +| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#46-multi-disk-schema)) | +| `users` | list | `[]` | User accounts | +| `root` | dict | see below | Root account settings | +| `luks` | dict | see below | Encryption settings | +| `features` | dict | see below | Feature toggles | #### `system.network` -| Key | Type | Default | Description | -| -------------- | ---------- | ------- | ---------------------------------------------------- | -| `bridge` | string | empty | Hypervisor network/bridge name | -| `vlan` | string/int | empty | VLAN tag | -| `ip` | string | empty | Static IP (omit for DHCP) | -| `prefix` | int | empty | CIDR prefix for static IP | -| `gateway` | string | empty | Default gateway (static only) | -| `dns.servers` | list | `[]` | DNS resolvers (must be a YAML list) | -| `dns.search` | list | `[]` | Search domains (must be a YAML list) | -| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) | +| Key | Type | Default | Description | +| -------------- | ---------- | ------- | ---------------------------------------------- | +| `bridge` | string | -- | Hypervisor network/bridge name | +| `vlan` | string/int | -- | VLAN tag | +| `ip` | string | -- | Static IP (omit for DHCP) | +| `prefix` | int | -- | CIDR prefix (1-32, required with `ip`) | +| `gateway` | string | -- | Default gateway | +| `dns.servers` | list | `[]` | DNS resolvers (must be a YAML list) | +| `dns.search` | list | `[]` | Search domains (must be a YAML list) | +| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) | -When `interfaces` is empty, the flat fields (`bridge`, `ip`, `prefix`, `gateway`, `vlan`) are auto-wrapped into a single-entry `interfaces[]` list. When `interfaces` is set, it takes precedence and the flat fields are back-populated from `interfaces[0]` for backward compatibility. Each `interfaces[]` entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`. +When `interfaces` is empty, the flat fields (`bridge`, `ip`, `prefix`, `gateway`, `vlan`) are auto-wrapped into a single-entry list. When `interfaces` is set, it takes precedence. Each entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`. #### `system.users` -A list of user account dictionaries. Credentials for the first user are prompted interactively by default via `vars_prompt` in `main.yml`, but can be supplied via inventory, vars files, or `-e` for non-interactive runs. +| Key | Type | Default | Description | +| ---------- | ----------- | ------- | -------------------------------------------------- | +| `name` | string | -- | Username (required) | +| `password` | string | -- | User password (required for first user) | +| `keys` | list | `[]` | SSH public keys | +| `sudo` | bool/string | -- | `true` for NOPASSWD ALL, or custom sudoers string | -| Key | Type | Default | Description | -| ---------- | ------ | ------- | -------------------------------------------- | -| `name` | string | empty | Username created on target (required) | -| `password` | string | empty | User password (also used for sudo) | -| `keys` | list | `[]` | SSH public keys for `authorized_keys` | -| `sudo` | string | empty | Custom sudoers rule (optional, per-user) | +The first user's credentials are prompted interactively via `vars_prompt` unless supplied in inventory or `-e`. #### `system.root` -| Key | Type | Default | Description | -| ---------- | ------ | ------- | -------------- | -| `password` | string | empty | Root password | +| Key | Type | Default | Description | +| ---------- | ------ | ------- | ------------- | +| `password` | string | -- | Root password | #### `system.luks` -LUKS container, unlock, and initramfs-related settings. - -| Key | Type | Default | Allowed | Description | -| ------------ | ------ | ------------------ | -------------------------- | ------------------------------------------ | -| `enabled` | bool | `false` | `true`/`false` | Enable encrypted root workflow | -| `passphrase` | string | empty | any string | Passphrase used for format/open/enroll | -| `mapper` | string | `SYSTEM_DECRYPTED` | mapper name | Mapper name under `/dev/mapper` | -| `auto` | bool | `true` | `true`/`false` | Auto-unlock behavior toggle | -| `method` | string | `tpm2` | `tpm2`, `keyfile` | Auto-unlock backend when `auto=true` | -| `keysize` | int | `64` | positive int | Keyfile size (bytes) for keyfile mode | -| `options` | string | `discard,tries=3` | crypttab opts | Additional crypttab/kernel options | -| `type` | string | `luks2` | cryptsetup type | LUKS format type | -| `cipher` | string | `aes-xts-plain64` | cipher name | Cryptsetup cipher | -| `hash` | string | `sha512` | hash name | Cryptsetup hash | -| `iter` | int | `4000` | positive int | PBKDF iteration time (ms) | -| `bits` | int | `512` | positive int | Key size (bits) | -| `pbkdf` | string | `argon2id` | pbkdf name | PBKDF algorithm | -| `urandom` | bool | `true` | `true`/`false` | Use urandom during key generation | -| `verify` | bool | `true` | `true`/`false` | Verify passphrase during format | +| Key | Type | Default | Description | +| ------------ | ------ | ------------------ | ------------------------------------------ | +| `enabled` | bool | `false` | Enable encrypted root | +| `passphrase` | string | -- | Passphrase for format/open/enroll | +| `mapper` | string | `SYSTEM_DECRYPTED` | Mapper name under `/dev/mapper` | +| `auto` | bool | `true` | Auto-unlock toggle | +| `method` | string | `tpm2` | Auto-unlock backend: `tpm2` or `keyfile` | +| `keysize` | int | `64` | Keyfile size in bytes | +| `options` | string | `discard,tries=3` | Additional crypttab options | +| `type` | string | `luks2` | LUKS format type | +| `cipher` | string | `aes-xts-plain64` | Cipher | +| `hash` | string | `sha512` | Hash algorithm | +| `iter` | int | `4000` | PBKDF iteration time (ms) | +| `bits` | int | `512` | Key size (bits) | +| `pbkdf` | string | `argon2id` | PBKDF algorithm | +| `urandom` | bool | `true` | Use urandom during key generation | +| `verify` | bool | `true` | Verify passphrase during format | #### `system.luks.tpm2` -TPM2-specific policy settings used when `system.luks.method: tpm2`. - -| Key | Type | Default | Allowed | Description | -| ------ | ----------- | ------- | --------------------- | --------------------------------------------------- | -| `device` | string | `auto` | `auto` or device path | TPM2 device selector | -| `pcrs` | string/list | empty | PCR expression | PCR binding policy (e.g. `"7"` or `"0+7"`) | +| Key | Type | Default | Description | +| -------- | ------------- | ------- | ---------------------------------------------- | +| `device` | string | `auto` | TPM2 device selector | +| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`) | #### `system.features` -Feature toggles for optional system configuration. - -| Key | Type | Default | Allowed | Description | -| ------------------ | ------ | -------------- | ------------------------------------------ | ---------------------------------- | -| `cis.enabled` | bool | `false` | `true`/`false` | Enable CIS hardening role | -| `selinux.enabled` | bool | `true` | `true`/`false` | SELinux management | -| `firewall.enabled` | bool | `true` | `true`/`false` | Enable firewall role actions | -| `firewall.backend` | string | `firewalld` | `firewalld`, `ufw` | Firewall service backend | -| `firewall.toolkit` | string | `nftables` | `nftables`, `iptables` | Packet filtering toolkit | -| `ssh.enabled` | bool | `true` | `true`/`false` | SSH service/package management | -| `zstd.enabled` | bool | `true` | `true`/`false` | zstd related tuning | -| `swap.enabled` | bool | `true` | `true`/`false` | Swap setup toggle | -| `banner.motd` | bool | `false` | `true`/`false` | MOTD banner management | -| `banner.sudo` | bool | `true` | `true`/`false` | Sudo banner management | -| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, `systemd-nspawn` | Chroot wrapper command | +| Key | Type | Default | Description | +| ------------------ | ------ | -------------- | ------------------------------------ | +| `cis.enabled` | bool | `false` | Enable CIS hardening (see [4.4](#44-cis-dictionary)) | +| `selinux.enabled` | bool | `true` | SELinux management | +| `firewall.enabled` | bool | `true` | Firewall setup | +| `firewall.backend` | string | `firewalld` | `firewalld` or `ufw` | +| `firewall.toolkit` | string | `nftables` | `nftables` or `iptables` | +| `ssh.enabled` | bool | `true` | SSH service/package management | +| `zstd.enabled` | bool | `true` | zstd-related tuning | +| `swap.enabled` | bool | `true` | Swap setup | +| `banner.motd` | bool | `false` | MOTD banner | +| `banner.sudo` | bool | `true` | Sudo banner | +| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` | ### 4.3 `hypervisor` Dictionary -| Key | Type | Description | -| ------------ | ------ | -------------------------------------------------------- | -| `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, or `none` | -| `url` | string | Proxmox/VMware API host | -| `username` | string | API username | -| `password` | string | API password | -| `host` | string | Proxmox node name | -| `storage` | string | Proxmox/VMware storage identifier | -| `datacenter` | string | VMware datacenter | -| `cluster` | string | VMware cluster | -| `certs` | bool | TLS certificate validation for VMware | -| `ssh` | bool | VMware: enable SSH on guest and switch connection to SSH | +| Key | Type | Default | Description | +| ------------ | ------ | ------- | ---------------------------------------------------- | +| `type` | string | -- | `libvirt`, `proxmox`, `vmware`, `xen`, or `none` | +| `url` | string | -- | API host (Proxmox/VMware) | +| `username` | string | -- | API username | +| `password` | string | -- | API password | +| `host` | string | -- | Proxmox node name | +| `storage` | string | -- | Storage identifier (Proxmox/VMware) | +| `datacenter` | string | -- | VMware datacenter | +| `cluster` | string | -- | VMware cluster | +| `certs` | bool | `true` | TLS certificate validation (VMware) | +| `ssh` | bool | `false` | Enable SSH on guest and switch connection (VMware) | -### 4.4 VMware Guest Operations +### 4.4 `cis` Dictionary -When `hypervisor.type: vmware` uses the `vmware_tools` connection, these Ansible connection variables are required. +When `system.features.cis.enabled: true`, the CIS role applies hardening. All values have sensible defaults; override specific keys via the `cis` dict. -| Variable | Description | -| ------------------------------- | -------------------------------------------------- | -| `ansible_vmware_tools_user` | Guest OS username for guest operations | -| `ansible_vmware_tools_password` | Guest OS password for guest operations | -| `ansible_vmware_guest_path` | VM inventory path (`/datacenter/vm/folder/name`) | -| `ansible_vmware_host` | vCenter/ESXi hostname | -| `ansible_vmware_user` | vCenter/ESXi API username | -| `ansible_vmware_password` | vCenter/ESXi API password | -| `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation | +| Key | Type | Default | Description | +| -------------------- | ------ | ------- | ------------------------------------------------ | +| `modules_blacklist` | list | see below | Kernel modules to blacklist via modprobe | +| `sysctl` | dict | see below | Sysctl key/value pairs written to `10-cis.conf` | +| `sshd_options` | list | see below | SSHD options applied via lineinfile | +| `pwquality_minlen` | int | `14` | Minimum password length | +| `tmout` | int | `900` | Shell timeout (seconds) | +| `umask` | string | `077` | Default umask in bashrc | +| `umask_profile` | string | `027` | Default umask in /etc/profile | +| `faillock_deny` | int | `5` | Failed login attempts before lockout | +| `faillock_unlock_time` | int | `900` | Lockout duration (seconds) | +| `password_remember` | int | `5` | Password history depth | -### 4.5 Multi-Disk Schema +**Default modules blacklist:** `freevxfs`, `jffs2`, `hfs`, `hfsplus`, `cramfs`, `udf`, `usb-storage`, `dccp`, `sctp`, `rds`, `tipc`, `firewire-core`, `firewire-sbp2`, `thunderbolt`. `squashfs` is added automatically except on Ubuntu (snap dependency). -`system.disks[0]` is always the OS disk. Additional entries define data disks. +**Default sysctl settings** include: `kernel.yama.ptrace_scope=2`, `kernel.kptr_restrict=2`, `kernel.perf_event_paranoid=3`, `kernel.unprivileged_bpf_disabled=1`, IPv4/IPv6 hardening, ARP protection, and IPv6 disabled by default. Override individual keys: -| Key | Type | Description | -| ------------- | ------ | ---------------------------------------------------- | -| `size` | number | Disk size in GB (required for virtual installs) | -| `device` | string | Explicit block device (required for physical data disks) | -| `mount.path` | string | Mount point (for additional disks) | -| `mount.fstype`| string | `btrfs`, `ext4`, or `xfs` | -| `mount.label` | string | Optional filesystem label | -| `mount.opts` | string | Mount options (default: `defaults`) | +```yaml +cis: + sysctl: + net.ipv6.conf.all.disable_ipv6: 0 # re-enable IPv6 + net.ipv4.ip_forward: 1 # enable for routers/containers +``` -Virtual install example: +**Default SSHD options** enforce: `PermitRootLogin no`, `PasswordAuthentication no`, `X11Forwarding no`, `AllowTcpForwarding no`, `MaxAuthTries 4`, and post-quantum KEX (mlkem768x25519-sha256 on OpenSSH 9.9+). Override per-option: + +```yaml +cis: + sshd_options: + - { option: X11Forwarding, value: "yes" } + - { option: AllowTcpForwarding, value: "yes" } +``` + +Note: providing `sshd_options` replaces the entire list. Copy the defaults from `roles/cis/defaults/main.yml` and modify as needed. + +### 4.5 VMware Guest Operations + +When `hypervisor.type: vmware` uses the `vmware_tools` connection: + +| Variable | Description | +| ------------------------------- | -------------------------------------------- | +| `ansible_vmware_tools_user` | Guest OS username | +| `ansible_vmware_tools_password` | Guest OS password | +| `ansible_vmware_guest_path` | VM inventory path | +| `ansible_vmware_host` | vCenter/ESXi hostname | +| `ansible_vmware_user` | vCenter/ESXi API username | +| `ansible_vmware_password` | vCenter/ESXi API password | +| `ansible_vmware_validate_certs` | TLS certificate validation | + +### 4.6 Multi-Disk Schema + +`system.disks[0]` is the OS disk (no `mount.path`). Additional entries define data disks. + +| Key | Type | Description | +| ------------- | ------ | ------------------------------------------------------ | +| `size` | number | Disk size in GB (required for virtual) | +| `device` | string | Block device path (required for physical data disks) | +| `partition` | string | Partition device path (required for physical data disks) | +| `mount.path` | string | Mount point (additional disks only) | +| `mount.fstype`| string | `btrfs`, `ext4`, or `xfs` | +| `mount.label` | string | Filesystem label | +| `mount.opts` | string | Mount options (default: `defaults`) | ```yaml system: disks: - - size: 80 - - size: 200 + - size: 80 # OS disk + - size: 200 # Data disk mount: path: /data fstype: xfs label: DATA - opts: defaults,noatime - - size: 300 - mount: - path: /backup - fstype: ext4 ``` -Physical install example (device paths required): +### 4.7 Advanced Partitioning Overrides -```yaml -system: - type: physical - disks: - - device: /dev/sda - size: 120 - - device: /dev/sdb - size: 500 - mount: - path: /data - fstype: ext4 -``` +| Variable | Default | Description | +| ------------------------------ | ------------ | ---------------------------------------- | +| `partitioning_efi_size_mib` | `512` | EFI system partition size in MiB | +| `partitioning_boot_size_mib` | `1024` | Separate `/boot` size in MiB | +| `partitioning_separate_boot` | auto-derived | Force a separate `/boot` partition | +| `partitioning_boot_fs_fstype` | auto-derived | Filesystem for `/boot` | +| `partitioning_use_full_disk` | `true` | Use remaining VG space for root LV | -### 4.6 Advanced Partitioning Overrides +**Swap sizing:** RAM >= 16GB gets swap = RAM/2. RAM < 16GB gets swap = max(RAM_GB, 2GB). Further capped to prevent over-allocation on small disks. -Use these only when you need to override the default partition layout logic. +**LVM layout** (when not using btrfs): root, swap, and when CIS is enabled: `/home` (2-20GB, 10% of disk), `/var` (2GB), `/var/log` (2GB), `/var/log/audit` (1.5GB). -| Variable | Description | Default | -| ------------------------------ | ------------------------------------------------- | ------------ | -| `partitioning_efi_size_mib` | EFI system partition size in MiB | `512` | -| `partitioning_boot_size_mib` | Separate `/boot` size in MiB (when used) | `1024` | -| `partitioning_separate_boot` | Force a separate `/boot` partition | auto-derived | -| `partitioning_boot_fs_fstype` | Filesystem for `/boot` when separate | auto-derived | -| `partitioning_use_full_disk` | Consume remaining VG space for root LV | `true` | +### 4.8 Cleanup Defaults -## 5. How to Use the Playbook +Post-install verification and recovery settings. -### 5.1 Prerequisites +| Variable | Default | Description | +| --------------------------- | ------- | ----------------------------------------------------- | +| `cleanup_verify_boot` | `true` | Check VM accessibility after reboot | +| `cleanup_boot_timeout` | `300` | Timeout in seconds for boot verification | +| `cleanup_remove_on_failure` | `true` | Auto-remove VMs that fail to boot (created this run only) | -- Ansible installed on the control machine. -- Inventory file with target systems defined and variables configured. -- Disposable/non-production targets (the playbook enforces production-safety checks). +## 5. Execution Pipeline -### 5.2 Running the Playbook +Roles execute in this order: -Execute the playbook using `ansible-playbook`, ensuring that all necessary variables are defined either in the inventory, in a vars file, or passed via `-e`. Credentials (`root_password`, `user_name`, `user_password`, `user_public_key`) are prompted interactively unless supplied through inventory or extra vars. +1. **global_defaults** -- normalize inputs, validate, set OS flags +2. **system_check** -- detect installer environment, verify live/non-prod target +3. **virtualization** -- create VM (if virtual), attach disks, cloud-init +4. **environment** -- prepare installer: mount ISO, configure repos, setup pacman +5. **partitioning** -- create partitions, LVM, LUKS, mount filesystems +6. **bootstrap** -- install base system and packages (OS-specific) +7. **configuration** -- users, fstab, locales, bootloader, encryption enrollment, networking +8. **cis** -- CIS hardening (when `system.features.cis.enabled: true`) +9. **cleanup** -- unmount, shutdown installer, remove media, verify boot + +## 6. Usage ```bash ansible-playbook -i inventory.yml main.yml -ansible-playbook -i inventory.yml main.yml -e @vars_example.yml +ansible-playbook -i inventory.yml main.yml -e @vars.yml ``` -### 5.3 Example Usage +Credentials for the first user and root are prompted interactively via `vars_prompt` unless already set in inventory or passed via `-e`. -Use the bundled example files as starting points for new inventories: +Example inventory files are included: - `inventory_example.yml` -- Proxmox virtual setup - `inventory_libvirt_example.yml` -- libvirt virtual setup - `inventory_baremetal_example.yml` -- bare-metal physical setup -- `vars_example.yml` -- shared variable overrides -- `vars_baremetal_example.yml` -- bare-metal variable overrides -```bash -# Proxmox example -ansible-playbook -i inventory_example.yml main.yml +## 7. Security -# libvirt example -ansible-playbook -i inventory_libvirt_example.yml main.yml - -# Custom inventory with separate vars file -ansible-playbook -i inventory.yml main.yml -e @vars_example.yml -``` - -## 6. Security - -To protect sensitive information such as passwords, API keys, and other confidential variables (e.g. `hypervisor.password`, `system.luks.passphrase`), **use Ansible Vault** instead of plaintext inventory files. - -## 7. Operational Notes - -- For virtual installs, `system.cpus`, `system.memory`, and `system.disks[0].size` are required and validated. -- For physical installs, sizing is derived from the detected install drive; set installer access (`ansible_user`/`ansible_password`) when the installer environment differs from the prompted user credentials. -- `system.network.dns.servers` and `system.network.dns.search` must be YAML lists. -- `hypervisor.type` selects backend-specific provisioning and cleanup behavior. -- Guest tools are selected automatically by hypervisor: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`. -- With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported (libvirt/proxmox/vmware). -- With LUKS enabled on non-Arch targets, provisioning uses an ESP (512 MiB), a separate `/boot` (1 GiB), and the encrypted root; adjust sizes via `partitioning_efi_size_mib` and `partitioning_boot_size_mib` if needed. -- For VMware, `hypervisor.ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks. -- Molecule is scaffolded with a delegated driver and a no-op converge for lint-only validation. +Use **Ansible Vault** for all sensitive values (`hypervisor.password`, `system.luks.passphrase`, `system.users[].password`, `system.root.password`). ## 8. Safety -This playbook intentionally aborts if it detects a non-live/production target. It also refuses to touch pre-existing VMs and only cleans up VMs created in the current run. - -Always run lint after changes: - -```bash -ansible-lint -``` +The playbook aborts on non-live/production targets. It refuses to touch pre-existing VMs and only cleans up VMs created in the current run.