Files
Ansible-Bootstrap/README.md

373 lines
13 KiB
Markdown

# Ansible Bootstrap Playbook
Automate Linux system bootstrap across multiple distributions and hypervisors in an Infrastructure-as-Code workflow.
## Supported Distributions
| Distribution | Versions |
|---|---|
| AlmaLinux | 8.x, 9.x, 10.x |
| Alpine Linux | latest |
| Arch Linux | latest |
| Debian | 10, 11, 12, 13, unstable |
| Fedora | 40, 41, 42, 43 |
| openSUSE Tumbleweed | latest |
| RHEL | 8.x, 9.x, 10.x |
| Rocky Linux | 8.x, 9.x, 10.x |
| Ubuntu | latest |
| Ubuntu LTS | latest |
| Void Linux | latest |
## Supported Hypervisors
| Hypervisor | Value |
|---|---|
| libvirt | `libvirt` |
| Proxmox | `proxmox` |
| VMware | `vmware` |
| Xen | `xen` |
| Bare metal | `none` |
## 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.
## Configuration Model
The project uses only dict-based variables:
- `system` for host/runtime/install configuration
- `hypervisor` for virtualization backend configuration
These dictionaries 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.
Set shared values like `system.filesystem` once in group vars, then override only host-specific keys per host.
### Variable Placement
| Location | Scope | Typical use |
|---|---|---|
| `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` |
| `group_vars/<group>.yml` | Group | Environment or role-specific defaults |
| `host_vars/<host>.yml` | Single host | Host-specific overrides |
| Inventory host vars | Single host | Inline definitions for quick setup |
### Example Host Definition
```yaml
all:
vars:
system:
filesystem: btrfs
boot_iso: "local:iso/archlinux-x86_64.iso"
hypervisor:
type: proxmox
url: pve01.example.com
username: root@pam
password: CHANGE_ME
host: pve01
storage: local-lvm
children:
bootstrap:
hosts:
app01.example.com:
ansible_host: 10.0.0.10
system:
type: virtual
os: debian
version: "12"
name: app01.example.com
id: 101
cpus: 2
memory: 4096
balloon: 0
network: vmbr0
ip: 10.0.0.10
prefix: 24
gateway: 10.0.0.1
dns:
servers: [1.1.1.1, 1.0.0.1]
search: [example.com]
disks:
- size: 40
- size: 120
mount:
path: /data
fstype: xfs
user:
name: ops
password: CHANGE_ME
key: "ssh-ed25519 AAAA..."
root:
password: CHANGE_ME
luks:
enabled: true
passphrase: CHANGE_ME
auto: true
method: tpm2
tpm2:
pcrs: "7"
features:
firewall:
enabled: true
backend: firewalld
toolkit: nftables
```
## Core Variables
| Variable | Type | Description |
|---|---|---|
| `boot_iso` | string | Required when `system.type=virtual` |
| `rhel_iso` | string | Required when `system.os=rhel` |
| `custom_iso` | bool | Skip Arch ISO-specific preparation checks |
## `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 (`archlinux` if omitted on physical) | Target distribution |
| `version` | string | empty | Version selector for distro families |
| `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` |
| `name` | string | inventory hostname | Final hostname |
| `id` | int/string | empty | VMID for Proxmox |
| `cpus` | int | `0` | vCPU count |
| `memory` | int | `0` | Memory in MiB |
| `balloon` | int | `0` | Balloon memory in MiB |
| `network` | string | empty | Hypervisor network/bridge |
| `vlan` | string/int | empty | VLAN tag |
| `ip` | string | empty | Static IP (optional) |
| `prefix` | int | empty | Prefix for static IP |
| `gateway` | string | empty | Static gateway |
| `path` | string | empty | Hypervisor folder/path (libvirt/vmware) |
| `packages` | list/string | empty | Post-reboot packages |
| `dns` | dict | `{servers: [], search: []}` | DNS nested dictionary |
| `disks` | list | `[]` | Disk layout list |
| `user` | dict | `{name:'', password:'', key:''}` | User account dictionary |
| `root` | dict | `{password:''}` | Root account dictionary |
| `luks` | dict | see below | Encryption dictionary |
| `features` | dict | see below | Feature flags dictionary |
### `system.dns`
DNS options used by network configuration tasks.
Use these keys under `system.dns`.
| Key | Type | Default | Description |
|---|---|---|---|
| `servers` | list/string | `[]` | DNS resolvers; comma-separated string is normalized |
| `search` | list/string | `[]` | Search domains; comma-separated string is normalized |
### `system.user`
Target user account settings.
Use these keys under `system.user`.
| Key | Type | Default | Description |
|---|---|---|---|
| `name` | string | empty | Username created on target |
| `password` | string | empty | User password (also used for sudo/become) |
| `key` | string | empty | SSH public key for `authorized_keys` |
### `system.root`
Use these keys under `system.root`.
| Key | Type | Default | Description |
|---|---|---|---|
| `password` | string | empty | Root password |
### `system.luks`
LUKS container, unlock, and initramfs-related settings.
Use these keys under `system.luks`.
| 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 |
### `system.luks.tpm2`
TPM2-specific policy settings used when `system.luks.method=tpm2`.
Use these keys under `system.luks.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 (for example `"7"` or `"0+7"`) |
### `system.features`
Feature toggles for optional system configuration.
Use these keys under `system.features`.
| 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 selection |
| `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 | `true` | `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 |
## Multi-Disk Schema
`system.disks[0]` is always the OS disk. Additional entries can define data disks.
| Key | Type | Description |
|---|---|---|
| `size` | number | Disk size in GB (required for virtual) |
| `device` | string | Explicit disk device (required for physical data disks) |
| `mount.path` | string | Mount target (for additional disks) |
| `mount.fstype` | string | `btrfs`, `ext4`, or `xfs` |
| `mount.label` | string | Optional filesystem label |
| `mount.opts` | string | Mount options (`defaults` by default) |
Example:
```yaml
system:
disks:
- size: 80
- size: 200
mount:
path: /data
fstype: xfs
label: DATA
opts: defaults,noatime
- size: 300
mount:
path: /backup
fstype: ext4
```
For physical installs, include device paths:
```yaml
system:
type: physical
disks:
- device: /dev/sda
size: 120
- device: /dev/sdb
size: 500
mount:
path: /data
fstype: ext4
```
## Advanced Partitioning Overrides
Use these only when you need to override default layout behavior.
| 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 separate `/boot` partition logic | 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` |
## `hypervisor` Dictionary
Use these keys under `hypervisor`.
| Key | Type | Description |
|---|---|---|
| `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, `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 |
| `datacenter` | string | VMware datacenter |
| `cluster` | string | VMware cluster |
| `certs` | bool | TLS cert validation for VMware |
| `ssh` | bool | VMware installer SSH bootstrap helper |
## VMware Guest Operations Variables
When `hypervisor.type: vmware` and connection uses `vmware_tools`, ensure these variables are set in inventory/group/host vars as needed by your vCenter/ESXi environment.
| 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 host |
| `ansible_vmware_user` | vCenter/ESXi API username |
| `ansible_vmware_password` | vCenter/ESXi API password |
| `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation |
## Prerequisites
- Ansible installed on the control machine.
- Inventory and variables prepared for your target hosts.
- Disposable/non-production targets (the playbook enforces production-safety checks).
## Usage
```bash
ansible-playbook -i inventory_example.yml main.yml
ansible-playbook -i inventory_libvirt_example.yml main.yml
ansible-playbook -i inventory.yml main.yml -e @vars_example.yml
```
## Security
Store sensitive data (passwords, API tokens, private keys) with Ansible Vault instead of plaintext inventory files.
## 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 needed.
- `system.dns.servers` and `system.dns.search` accept either YAML lists or comma-separated strings.
- `hypervisor.type` selects backend-specific provisioning/cleanup behavior.
- Guest tools are selected automatically by hypervisor: `qemu-guest-agent` (`libvirt`/`proxmox`) and `open-vm-tools` (`vmware`).
- With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported.
- With LUKS on non-Arch targets, provisioning may use a separate `/boot`; tune with `partitioning_efi_size_mib` and `partitioning_boot_size_mib`.
- For VMware, `hypervisor.ssh: true` enables SSH on the guest and switches the connection to SSH for remaining tasks.
- Molecule scenario is lint-focused (`delegated` driver with non-destructive placeholder converge).
## 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.
## Validation
Always run lint after changes:
```bash
ansible-lint
```