Compare commits
13 Commits
dea01cc8a0
...
f94b220020
| Author | SHA1 | Date | |
|---|---|---|---|
| f94b220020 | |||
| 3fd470d63e | |||
| a3cd507b2a | |||
| f74ec325ea | |||
| bef15af69f | |||
| 7970d933e8 | |||
| a123a32feb | |||
| 54c704de4e | |||
| 9308d09d7b | |||
| f367844239 | |||
| 53e4499d2b | |||
| eb63a4fa83 | |||
| 9e3688ae2b |
425
README.md
425
README.md
@@ -1,8 +1,8 @@
|
|||||||
# Ansible Bootstrap
|
# 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
|
## Table of Contents
|
||||||
|
|
||||||
@@ -13,15 +13,14 @@ Most roles are adaptable for use with systems beyond Arch Linux, requiring only
|
|||||||
- 4.1 [Core Variables](#41-core-variables)
|
- 4.1 [Core Variables](#41-core-variables)
|
||||||
- 4.2 [`system` Dictionary](#42-system-dictionary)
|
- 4.2 [`system` Dictionary](#42-system-dictionary)
|
||||||
- 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary)
|
- 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary)
|
||||||
- 4.4 [VMware Guest Operations](#44-vmware-guest-operations)
|
- 4.4 [`cis` Dictionary](#44-cis-dictionary)
|
||||||
- 4.5 [Multi-Disk Schema](#45-multi-disk-schema)
|
- 4.5 [VMware Guest Operations](#45-vmware-guest-operations)
|
||||||
- 4.6 [Advanced Partitioning Overrides](#46-advanced-partitioning-overrides)
|
- 4.6 [Multi-Disk Schema](#46-multi-disk-schema)
|
||||||
5. [How to Use the Playbook](#5-how-to-use-the-playbook)
|
- 4.7 [Advanced Partitioning Overrides](#47-advanced-partitioning-overrides)
|
||||||
- 5.1 [Prerequisites](#51-prerequisites)
|
- 4.8 [Cleanup Defaults](#48-cleanup-defaults)
|
||||||
- 5.2 [Running the Playbook](#52-running-the-playbook)
|
5. [Execution Pipeline](#5-execution-pipeline)
|
||||||
- 5.3 [Example Usage](#53-example-usage)
|
6. [Usage](#6-usage)
|
||||||
6. [Security](#6-security)
|
7. [Security](#7-security)
|
||||||
7. [Operational Notes](#7-operational-notes)
|
|
||||||
8. [Safety](#8-safety)
|
8. [Safety](#8-safety)
|
||||||
|
|
||||||
## 1. Supported Platforms
|
## 1. Supported Platforms
|
||||||
@@ -29,17 +28,17 @@ Most roles are adaptable for use with systems beyond Arch Linux, requiring only
|
|||||||
### Distributions
|
### Distributions
|
||||||
|
|
||||||
| `system.os` | Distribution | `system.version` |
|
| `system.os` | Distribution | `system.version` |
|
||||||
| ------------ | ------------------------ | ------------------------------- |
|
| ------------ | ------------------------ | ------------------------------------- |
|
||||||
| `almalinux` | AlmaLinux | `8`, `9`, `10` |
|
| `almalinux` | AlmaLinux | `8`, `9`, `10` |
|
||||||
| `alpine` | Alpine Linux | latest (rolling) |
|
| `alpine` | Alpine Linux | latest (rolling) |
|
||||||
| `archlinux` | Arch Linux | latest (rolling) |
|
| `archlinux` | Arch Linux | latest (rolling) |
|
||||||
| `debian` | Debian | `10`, `11`, `12`, `13`, `unstable` |
|
| `debian` | Debian | `10`-`13`, `unstable` |
|
||||||
| `fedora` | Fedora | `40`, `41`, `42`, `43` |
|
| `fedora` | Fedora | `38`-`45` |
|
||||||
| `opensuse` | openSUSE Tumbleweed | latest (rolling) |
|
| `opensuse` | openSUSE Tumbleweed | latest (rolling) |
|
||||||
| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` |
|
| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` |
|
||||||
| `rocky` | Rocky Linux | `8`, `9`, `10` |
|
| `rocky` | Rocky Linux | `8`, `9`, `10` |
|
||||||
| `ubuntu` | Ubuntu | latest |
|
| `ubuntu` | Ubuntu (latest non-LTS) | optional (e.g. `24.04`) |
|
||||||
| `ubuntu-lts` | Ubuntu LTS | latest |
|
| `ubuntu-lts` | Ubuntu LTS | optional (e.g. `24.04`) |
|
||||||
| `void` | Void Linux | latest (rolling) |
|
| `void` | Void Linux | latest (rolling) |
|
||||||
|
|
||||||
### Hypervisors
|
### Hypervisors
|
||||||
@@ -55,28 +54,28 @@ Most roles are adaptable for use with systems beyond Arch Linux, requiring only
|
|||||||
## 2. Compatibility Notes
|
## 2. Compatibility Notes
|
||||||
|
|
||||||
- `rhel_iso` is required for `system.os: rhel`.
|
- `rhel_iso` is required for `system.os: rhel`.
|
||||||
- RHEL installs should use `system.filesystem: ext4` or `system.filesystem: xfs` (not `btrfs`).
|
- RHEL installs should use `ext4` or `xfs` (not `btrfs`).
|
||||||
- For RHEL 8 specifically, prefer `ext4` over `xfs` if you hit installer/filesystem edge cases.
|
- `custom_iso: true` skips ArchISO validation; your installer must provide required tooling.
|
||||||
- `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` explicitly.
|
||||||
- On non-Arch installers, set `system.features.chroot.tool` (`arch-chroot`, `chroot`, or `systemd-nspawn`) explicitly as needed.
|
|
||||||
|
|
||||||
## 3. Configuration Model
|
## 3. Configuration Model
|
||||||
|
|
||||||
The project uses two dict-based variables:
|
Two dict-based variables drive the entire configuration:
|
||||||
|
|
||||||
- `system` for host/runtime/install configuration
|
- **`system`** -- host, network, users, disk layout, encryption, and feature toggles
|
||||||
- `hypervisor` for virtualization backend configuration
|
- **`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
|
### Variable Placement
|
||||||
|
|
||||||
| Location | Scope | Typical use |
|
| Location | Scope | Typical use |
|
||||||
| -------------------------- | ----------- | ------------------------------------------------------------ |
|
| ------------------------ | ----------- | -------------------------------------------------------------- |
|
||||||
| `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` |
|
| `group_vars/all.yml` | All hosts | Shared `hypervisor`, `system.filesystem`, `boot_iso` |
|
||||||
| `group_vars/<group>.yml` | Group | Environment or role-specific defaults |
|
| `group_vars/<group>.yml` | Group | Environment-specific defaults |
|
||||||
| `host_vars/<host>.yml` | Single host | Host-specific overrides |
|
| `host_vars/<host>.yml` | Single host | Host-specific overrides (`system.network.ip`, `system.id`, etc.) |
|
||||||
| Inventory inline host vars | Single host | Inline definitions for quick setup |
|
|
||||||
|
|
||||||
### Example Inventory
|
### Example Inventory
|
||||||
|
|
||||||
@@ -90,7 +89,8 @@ all:
|
|||||||
type: proxmox
|
type: proxmox
|
||||||
url: pve01.example.com
|
url: pve01.example.com
|
||||||
username: root@pam
|
username: root@pam
|
||||||
password: CHANGE_ME
|
password: !vault |
|
||||||
|
$ANSIBLE_VAULT...
|
||||||
host: pve01
|
host: pve01
|
||||||
storage: local-lvm
|
storage: local-lvm
|
||||||
|
|
||||||
@@ -107,7 +107,6 @@ all:
|
|||||||
id: 101
|
id: 101
|
||||||
cpus: 2
|
cpus: 2
|
||||||
memory: 4096
|
memory: 4096
|
||||||
balloon: 0
|
|
||||||
network:
|
network:
|
||||||
bridge: vmbr0
|
bridge: vmbr0
|
||||||
ip: 10.0.0.10
|
ip: 10.0.0.10
|
||||||
@@ -124,19 +123,24 @@ all:
|
|||||||
fstype: xfs
|
fstype: xfs
|
||||||
users:
|
users:
|
||||||
- name: ops
|
- name: ops
|
||||||
password: CHANGE_ME
|
password: !vault |
|
||||||
|
$ANSIBLE_VAULT...
|
||||||
keys:
|
keys:
|
||||||
- "ssh-ed25519 AAAA..."
|
- "ssh-ed25519 AAAA..."
|
||||||
|
sudo: true
|
||||||
root:
|
root:
|
||||||
password: CHANGE_ME
|
password: !vault |
|
||||||
|
$ANSIBLE_VAULT...
|
||||||
luks:
|
luks:
|
||||||
enabled: true
|
enabled: true
|
||||||
passphrase: CHANGE_ME
|
passphrase: !vault |
|
||||||
auto: true
|
$ANSIBLE_VAULT...
|
||||||
method: tpm2
|
method: tpm2
|
||||||
tpm2:
|
tpm2:
|
||||||
pcrs: "7"
|
pcrs: "7"
|
||||||
features:
|
features:
|
||||||
|
cis:
|
||||||
|
enabled: true
|
||||||
firewall:
|
firewall:
|
||||||
enabled: true
|
enabled: true
|
||||||
backend: firewalld
|
backend: firewalld
|
||||||
@@ -147,38 +151,36 @@ all:
|
|||||||
|
|
||||||
### 4.1 Core Variables
|
### 4.1 Core Variables
|
||||||
|
|
||||||
These top-level variables sit outside the `system`/`hypervisor` dictionaries.
|
Top-level variables outside `system`/`hypervisor`/`cis`.
|
||||||
|
|
||||||
| Variable | Type | Description |
|
| Variable | Type | Default | Description |
|
||||||
| ----------------------------------- | ------ | ------------------------------------------------ |
|
| ---------------- | ------ | -------------------------- | ---------------------------------------------------- |
|
||||||
| `boot_iso` | string | Path to the boot ISO image (required for virtual installs). |
|
| `boot_iso` | string | -- | Boot ISO path (required for virtual installs) |
|
||||||
| `rhel_iso` | string | Path to the RHEL ISO (required when `system.os: rhel`). |
|
| `rhel_iso` | string | -- | RHEL ISO path (required when `system.os: rhel`) |
|
||||||
| `custom_iso` | bool | Skip ArchISO validation and pacman setup. Default `false`. |
|
| `custom_iso` | bool | `false` | Skip ArchISO validation and pacman setup |
|
||||||
| `thirdparty_tasks` | string | Drop-in task file included during environment setup. Default `dropins/preparation.yml`. |
|
| `thirdparty_tasks` | string | `dropins/preparation.yml` | Drop-in task file included during environment setup |
|
||||||
|
|
||||||
### 4.2 `system` Dictionary
|
### 4.2 `system` Dictionary
|
||||||
|
|
||||||
Top-level host install/runtime settings. Use these keys under `system`.
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
| ------------ | ---------- | -------------------- | ---------------------------------------- |
|
| ------------ | ---------- | ------------------ | ------------------------------------------------------ |
|
||||||
| `type` | string | `virtual` | `virtual` or `physical` |
|
| `type` | string | `virtual` | `virtual` or `physical` |
|
||||||
| `os` | string | empty | Target distribution (see [table](#distributions)) |
|
| `os` | string | -- | Target distribution (see [table](#distributions)) |
|
||||||
| `version` | string | empty | Version selector for distro families |
|
| `version` | string | -- | Version selector for versioned distros |
|
||||||
| `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` |
|
| `filesystem` | string | -- | `btrfs`, `ext4`, or `xfs` |
|
||||||
| `name` | string | inventory hostname | Final hostname |
|
| `name` | string | inventory hostname | Final hostname |
|
||||||
| `timezone` | string | `Europe/Vienna` | System timezone (tz database name) |
|
| `timezone` | string | `Europe/Vienna` | System timezone (tz database name) |
|
||||||
| `locale` | string | `en_US.UTF-8` | System locale |
|
| `locale` | string | `en_US.UTF-8` | System locale |
|
||||||
| `keymap` | string | `us` | Console keymap (`vconsole.conf`) |
|
| `keymap` | string | `us` | Console keymap |
|
||||||
| `id` | int/string | empty | VMID (required for Proxmox) |
|
| `id` | int/string | -- | VMID (required for Proxmox) |
|
||||||
| `cpus` | int | `0` | vCPU count |
|
| `cpus` | int | `0` | vCPU count (required for virtual) |
|
||||||
| `memory` | int | `0` | Memory in MiB |
|
| `memory` | int | `0` | Memory in MiB (required for virtual) |
|
||||||
| `balloon` | int | `0` | Balloon memory in MiB |
|
| `balloon` | int | `0` | Balloon memory in MiB (Proxmox) |
|
||||||
| `path` | string | empty | Hypervisor folder/path (libvirt/vmware) |
|
| `path` | string | -- | Hypervisor folder/path |
|
||||||
| `packages` | list | `[]` | Additional packages installed post-reboot |
|
| `packages` | list | `[]` | Additional packages installed post-reboot |
|
||||||
| `network` | dict | see below | Network configuration |
|
| `network` | dict | see below | Network configuration |
|
||||||
| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#45-multi-disk-schema)) |
|
| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#46-multi-disk-schema)) |
|
||||||
| `users` | list | `[]` | User accounts (see below) |
|
| `users` | list | `[]` | User accounts |
|
||||||
| `root` | dict | see below | Root account settings |
|
| `root` | dict | see below | Root account settings |
|
||||||
| `luks` | dict | see below | Encryption settings |
|
| `luks` | dict | see below | Encryption settings |
|
||||||
| `features` | dict | see below | Feature toggles |
|
| `features` | dict | see below | Feature toggles |
|
||||||
@@ -186,231 +188,228 @@ Top-level host install/runtime settings. Use these keys under `system`.
|
|||||||
#### `system.network`
|
#### `system.network`
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
| -------------- | ---------- | ------- | ---------------------------------------------------- |
|
| -------------- | ---------- | ------- | ---------------------------------------------- |
|
||||||
| `bridge` | string | empty | Hypervisor network/bridge name |
|
| `bridge` | string | -- | Hypervisor network/bridge name |
|
||||||
| `vlan` | string/int | empty | VLAN tag |
|
| `vlan` | string/int | -- | VLAN tag |
|
||||||
| `ip` | string | empty | Static IP (omit for DHCP) |
|
| `ip` | string | -- | Static IP (omit for DHCP) |
|
||||||
| `prefix` | int | empty | CIDR prefix for static IP |
|
| `prefix` | int | -- | CIDR prefix (1-32, required with `ip`) |
|
||||||
| `gateway` | string | empty | Default gateway (static only) |
|
| `gateway` | string | -- | Default gateway |
|
||||||
| `dns.servers` | list | `[]` | DNS resolvers (must be a YAML list) |
|
| `dns.servers` | list | `[]` | DNS resolvers (must be a YAML list) |
|
||||||
| `dns.search` | list | `[]` | Search domains (must be a YAML list) |
|
| `dns.search` | list | `[]` | Search domains (must be a YAML list) |
|
||||||
| `interfaces` | list | `[]` | Multi-NIC config (overrides flat fields above) |
|
| `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`
|
#### `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 |
|
| Key | Type | Default | Description |
|
||||||
| ---------- | ------ | ------- | -------------------------------------------- |
|
| ---------- | ----------- | ------- | -------------------------------------------------- |
|
||||||
| `name` | string | empty | Username created on target (required) |
|
| `name` | string | -- | Username (required) |
|
||||||
| `password` | string | empty | User password (also used for sudo) |
|
| `password` | string | -- | User password (required for first user) |
|
||||||
| `keys` | list | `[]` | SSH public keys for `authorized_keys` |
|
| `keys` | list | `[]` | SSH public keys |
|
||||||
| `sudo` | string | empty | Custom sudoers rule (optional, per-user) |
|
| `sudo` | bool/string | -- | `true` for NOPASSWD ALL, or custom sudoers string |
|
||||||
|
|
||||||
|
The first user's credentials are prompted interactively via `vars_prompt` unless supplied in inventory or `-e`.
|
||||||
|
|
||||||
#### `system.root`
|
#### `system.root`
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
| ---------- | ------ | ------- | -------------- |
|
| ---------- | ------ | ------- | ------------- |
|
||||||
| `password` | string | empty | Root password |
|
| `password` | string | -- | Root password |
|
||||||
|
|
||||||
#### `system.luks`
|
#### `system.luks`
|
||||||
|
|
||||||
LUKS container, unlock, and initramfs-related settings.
|
| Key | Type | Default | Description |
|
||||||
|
| ------------ | ------ | ------------------ | ------------------------------------------ |
|
||||||
| Key | Type | Default | Allowed | Description |
|
| `enabled` | bool | `false` | Enable encrypted root |
|
||||||
| ------------ | ------ | ------------------ | -------------------------- | ------------------------------------------ |
|
| `passphrase` | string | -- | Passphrase for format/open/enroll |
|
||||||
| `enabled` | bool | `false` | `true`/`false` | Enable encrypted root workflow |
|
| `mapper` | string | `SYSTEM_DECRYPTED` | Mapper name under `/dev/mapper` |
|
||||||
| `passphrase` | string | empty | any string | Passphrase used for format/open/enroll |
|
| `auto` | bool | `true` | Auto-unlock toggle |
|
||||||
| `mapper` | string | `SYSTEM_DECRYPTED` | mapper name | Mapper name under `/dev/mapper` |
|
| `method` | string | `tpm2` | Auto-unlock backend: `tpm2` or `keyfile` |
|
||||||
| `auto` | bool | `true` | `true`/`false` | Auto-unlock behavior toggle |
|
| `keysize` | int | `64` | Keyfile size in bytes |
|
||||||
| `method` | string | `tpm2` | `tpm2`, `keyfile` | Auto-unlock backend when `auto=true` |
|
| `options` | string | `discard,tries=3` | Additional crypttab options |
|
||||||
| `keysize` | int | `64` | positive int | Keyfile size (bytes) for keyfile mode |
|
| `type` | string | `luks2` | LUKS format type |
|
||||||
| `options` | string | `discard,tries=3` | crypttab opts | Additional crypttab/kernel options |
|
| `cipher` | string | `aes-xts-plain64` | Cipher |
|
||||||
| `type` | string | `luks2` | cryptsetup type | LUKS format type |
|
| `hash` | string | `sha512` | Hash algorithm |
|
||||||
| `cipher` | string | `aes-xts-plain64` | cipher name | Cryptsetup cipher |
|
| `iter` | int | `4000` | PBKDF iteration time (ms) |
|
||||||
| `hash` | string | `sha512` | hash name | Cryptsetup hash |
|
| `bits` | int | `512` | Key size (bits) |
|
||||||
| `iter` | int | `4000` | positive int | PBKDF iteration time (ms) |
|
| `pbkdf` | string | `argon2id` | PBKDF algorithm |
|
||||||
| `bits` | int | `512` | positive int | Key size (bits) |
|
| `urandom` | bool | `true` | Use urandom during key generation |
|
||||||
| `pbkdf` | string | `argon2id` | pbkdf name | PBKDF algorithm |
|
| `verify` | bool | `true` | Verify passphrase during format |
|
||||||
| `urandom` | bool | `true` | `true`/`false` | Use urandom during key generation |
|
|
||||||
| `verify` | bool | `true` | `true`/`false` | Verify passphrase during format |
|
|
||||||
|
|
||||||
#### `system.luks.tpm2`
|
#### `system.luks.tpm2`
|
||||||
|
|
||||||
TPM2-specific policy settings used when `system.luks.method: tpm2`.
|
| Key | Type | Default | Description |
|
||||||
|
| -------- | ------------- | ------- | ---------------------------------------------- |
|
||||||
| Key | Type | Default | Allowed | Description |
|
| `device` | string | `auto` | TPM2 device selector |
|
||||||
| ------ | ----------- | ------- | --------------------- | --------------------------------------------------- |
|
| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`) |
|
||||||
| `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"`) |
|
|
||||||
|
|
||||||
#### `system.features`
|
#### `system.features`
|
||||||
|
|
||||||
Feature toggles for optional system configuration.
|
| Key | Type | Default | Description |
|
||||||
|
| ------------------ | ------ | -------------- | ------------------------------------ |
|
||||||
| Key | Type | Default | Allowed | Description |
|
| `cis.enabled` | bool | `false` | Enable CIS hardening (see [4.4](#44-cis-dictionary)) |
|
||||||
| ------------------ | ------ | -------------- | ------------------------------------------ | ---------------------------------- |
|
| `selinux.enabled` | bool | `true` | SELinux management |
|
||||||
| `cis.enabled` | bool | `false` | `true`/`false` | Enable CIS hardening role |
|
| `firewall.enabled` | bool | `true` | Firewall setup |
|
||||||
| `selinux.enabled` | bool | `true` | `true`/`false` | SELinux management |
|
| `firewall.backend` | string | `firewalld` | `firewalld` or `ufw` |
|
||||||
| `firewall.enabled` | bool | `true` | `true`/`false` | Enable firewall role actions |
|
| `firewall.toolkit` | string | `nftables` | `nftables` or `iptables` |
|
||||||
| `firewall.backend` | string | `firewalld` | `firewalld`, `ufw` | Firewall service backend |
|
| `ssh.enabled` | bool | `true` | SSH service/package management |
|
||||||
| `firewall.toolkit` | string | `nftables` | `nftables`, `iptables` | Packet filtering toolkit |
|
| `zstd.enabled` | bool | `true` | zstd-related tuning |
|
||||||
| `ssh.enabled` | bool | `true` | `true`/`false` | SSH service/package management |
|
| `swap.enabled` | bool | `true` | Swap setup |
|
||||||
| `zstd.enabled` | bool | `true` | `true`/`false` | zstd related tuning |
|
| `banner.motd` | bool | `false` | MOTD banner |
|
||||||
| `swap.enabled` | bool | `true` | `true`/`false` | Swap setup toggle |
|
| `banner.sudo` | bool | `true` | Sudo banner |
|
||||||
| `banner.motd` | bool | `false` | `true`/`false` | MOTD banner management |
|
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` |
|
||||||
| `banner.sudo` | bool | `true` | `true`/`false` | Sudo banner management |
|
|
||||||
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, `systemd-nspawn` | Chroot wrapper command |
|
|
||||||
|
|
||||||
### 4.3 `hypervisor` Dictionary
|
### 4.3 `hypervisor` Dictionary
|
||||||
|
|
||||||
| Key | Type | Description |
|
| Key | Type | Default | Description |
|
||||||
| ------------ | ------ | -------------------------------------------------------- |
|
| ------------ | ------ | ------- | ---------------------------------------------------- |
|
||||||
| `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, or `none` |
|
| `type` | string | -- | `libvirt`, `proxmox`, `vmware`, `xen`, or `none` |
|
||||||
| `url` | string | Proxmox/VMware API host |
|
| `url` | string | -- | API host (Proxmox/VMware) |
|
||||||
| `username` | string | API username |
|
| `username` | string | -- | API username |
|
||||||
| `password` | string | API password |
|
| `password` | string | -- | API password |
|
||||||
| `host` | string | Proxmox node name |
|
| `host` | string | -- | Proxmox node name |
|
||||||
| `storage` | string | Proxmox/VMware storage identifier |
|
| `storage` | string | -- | Storage identifier (Proxmox/VMware) |
|
||||||
| `datacenter` | string | VMware datacenter |
|
| `datacenter` | string | -- | VMware datacenter |
|
||||||
| `cluster` | string | VMware cluster |
|
| `cluster` | string | -- | VMware cluster |
|
||||||
| `certs` | bool | TLS certificate validation for VMware |
|
| `certs` | bool | `true` | TLS certificate validation (VMware) |
|
||||||
| `ssh` | bool | VMware: enable SSH on guest and switch connection to SSH |
|
| `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.
|
||||||
|
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
**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).
|
||||||
|
|
||||||
|
**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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cis:
|
||||||
|
sysctl:
|
||||||
|
net.ipv6.conf.all.disable_ipv6: 0 # re-enable IPv6
|
||||||
|
net.ipv4.ip_forward: 1 # enable for routers/containers
|
||||||
|
```
|
||||||
|
|
||||||
|
**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 |
|
| Variable | Description |
|
||||||
| ------------------------------- | -------------------------------------------------- |
|
| ------------------------------- | -------------------------------------------- |
|
||||||
| `ansible_vmware_tools_user` | Guest OS username for guest operations |
|
| `ansible_vmware_tools_user` | Guest OS username |
|
||||||
| `ansible_vmware_tools_password` | Guest OS password for guest operations |
|
| `ansible_vmware_tools_password` | Guest OS password |
|
||||||
| `ansible_vmware_guest_path` | VM inventory path (`/datacenter/vm/folder/name`) |
|
| `ansible_vmware_guest_path` | VM inventory path |
|
||||||
| `ansible_vmware_host` | vCenter/ESXi hostname |
|
| `ansible_vmware_host` | vCenter/ESXi hostname |
|
||||||
| `ansible_vmware_user` | vCenter/ESXi API username |
|
| `ansible_vmware_user` | vCenter/ESXi API username |
|
||||||
| `ansible_vmware_password` | vCenter/ESXi API password |
|
| `ansible_vmware_password` | vCenter/ESXi API password |
|
||||||
| `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation |
|
| `ansible_vmware_validate_certs` | TLS certificate validation |
|
||||||
|
|
||||||
### 4.5 Multi-Disk Schema
|
### 4.6 Multi-Disk Schema
|
||||||
|
|
||||||
`system.disks[0]` is always the OS disk. Additional entries define data disks.
|
`system.disks[0]` is the OS disk (no `mount.path`). Additional entries define data disks.
|
||||||
|
|
||||||
| Key | Type | Description |
|
| Key | Type | Description |
|
||||||
| ------------- | ------ | ---------------------------------------------------- |
|
| ------------- | ------ | ------------------------------------------------------ |
|
||||||
| `size` | number | Disk size in GB (required for virtual installs) |
|
| `size` | number | Disk size in GB (required for virtual) |
|
||||||
| `device` | string | Explicit block device (required for physical data disks) |
|
| `device` | string | Block device path (required for physical data disks) |
|
||||||
| `mount.path` | string | Mount point (for additional 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.fstype`| string | `btrfs`, `ext4`, or `xfs` |
|
||||||
| `mount.label` | string | Optional filesystem label |
|
| `mount.label` | string | Filesystem label |
|
||||||
| `mount.opts` | string | Mount options (default: `defaults`) |
|
| `mount.opts` | string | Mount options (default: `defaults`) |
|
||||||
|
|
||||||
Virtual install example:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
system:
|
system:
|
||||||
disks:
|
disks:
|
||||||
- size: 80
|
- size: 80 # OS disk
|
||||||
- size: 200
|
- size: 200 # Data disk
|
||||||
mount:
|
mount:
|
||||||
path: /data
|
path: /data
|
||||||
fstype: xfs
|
fstype: xfs
|
||||||
label: DATA
|
label: DATA
|
||||||
opts: defaults,noatime
|
|
||||||
- size: 300
|
|
||||||
mount:
|
|
||||||
path: /backup
|
|
||||||
fstype: ext4
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Physical install example (device paths required):
|
### 4.7 Advanced Partitioning Overrides
|
||||||
|
|
||||||
```yaml
|
| Variable | Default | Description |
|
||||||
system:
|
| ------------------------------ | ------------ | ---------------------------------------- |
|
||||||
type: physical
|
| `partitioning_efi_size_mib` | `512` | EFI system partition size in MiB |
|
||||||
disks:
|
| `partitioning_boot_size_mib` | `1024` | Separate `/boot` size in MiB |
|
||||||
- device: /dev/sda
|
| `partitioning_separate_boot` | auto-derived | Force a separate `/boot` partition |
|
||||||
size: 120
|
| `partitioning_boot_fs_fstype` | auto-derived | Filesystem for `/boot` |
|
||||||
- device: /dev/sdb
|
| `partitioning_use_full_disk` | `true` | Use remaining VG space for root LV |
|
||||||
size: 500
|
|
||||||
mount:
|
|
||||||
path: /data
|
|
||||||
fstype: ext4
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 |
|
### 4.8 Cleanup Defaults
|
||||||
| ------------------------------ | ------------------------------------------------- | ------------ |
|
|
||||||
| `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` |
|
|
||||||
|
|
||||||
## 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.
|
## 5. Execution Pipeline
|
||||||
- Inventory file with target systems defined and variables configured.
|
|
||||||
- Disposable/non-production targets (the playbook enforces production-safety checks).
|
|
||||||
|
|
||||||
### 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
|
```bash
|
||||||
ansible-playbook -i inventory.yml main.yml
|
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_example.yml` -- Proxmox virtual setup
|
||||||
- `inventory_libvirt_example.yml` -- libvirt virtual setup
|
- `inventory_libvirt_example.yml` -- libvirt virtual setup
|
||||||
- `inventory_baremetal_example.yml` -- bare-metal physical setup
|
- `inventory_baremetal_example.yml` -- bare-metal physical setup
|
||||||
- `vars_example.yml` -- shared variable overrides
|
|
||||||
- `vars_baremetal_example.yml` -- bare-metal variable overrides
|
|
||||||
|
|
||||||
```bash
|
## 7. Security
|
||||||
# Proxmox example
|
|
||||||
ansible-playbook -i inventory_example.yml main.yml
|
|
||||||
|
|
||||||
# libvirt example
|
Use **Ansible Vault** for all sensitive values (`hypervisor.password`, `system.luks.passphrase`, `system.users[].password`, `system.root.password`).
|
||||||
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.
|
|
||||||
|
|
||||||
## 8. Safety
|
## 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.
|
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.
|
||||||
|
|
||||||
Always run lint after changes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ansible-lint
|
|
||||||
```
|
|
||||||
|
|||||||
41
roles/bootstrap/tasks/_dnf_family.yml
Normal file
41
roles/bootstrap/tasks/_dnf_family.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
- name: "Bootstrap {{ os | capitalize }}"
|
||||||
|
vars:
|
||||||
|
_dnf_os_config:
|
||||||
|
rocky:
|
||||||
|
repos: [baseos, appstream]
|
||||||
|
groups: [core]
|
||||||
|
almalinux:
|
||||||
|
repos: [baseos, appstream]
|
||||||
|
groups: [core]
|
||||||
|
fedora:
|
||||||
|
repos: [fedora, fedora-updates]
|
||||||
|
groups: [critical-path-base, core]
|
||||||
|
_dnf_repos: "{{ _dnf_os_config[os].repos | map('regex_replace', '^', '--repo=') | join(' ') }}"
|
||||||
|
_dnf_groups: "{{ _dnf_os_config[os].groups | join(' ') }}"
|
||||||
|
_dnf_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: "Install {{ os | capitalize }} base system"
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dnf --releasever={{ os_version }} --best {{ _dnf_repos }}
|
||||||
|
--installroot=/mnt --setopt=install_weak_deps=False
|
||||||
|
groupinstall -y {{ _dnf_groups }}
|
||||||
|
register: _dnf_base_result
|
||||||
|
changed_when: _dnf_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
install -y {{ _dnf_extra }}
|
||||||
|
register: _dnf_extra_result
|
||||||
|
changed_when: _dnf_extra_result.rc == 0
|
||||||
|
|
||||||
|
- name: Reinstall kernel core
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
||||||
|
register: _dnf_kernel_result
|
||||||
|
changed_when: _dnf_kernel_result.rc == 0
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap AlmaLinux
|
|
||||||
vars:
|
|
||||||
bootstrap_almalinux_extra: >-
|
|
||||||
{{
|
|
||||||
lookup('vars', bootstrap_var_key)
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install AlmaLinux base system
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
|
|
||||||
--installroot=/mnt --setopt=install_weak_deps=False
|
|
||||||
groupinstall -y core
|
|
||||||
register: bootstrap_almalinux_base_result
|
|
||||||
changed_when: bootstrap_almalinux_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
|
||||||
install -y {{ bootstrap_almalinux_extra }}
|
|
||||||
register: bootstrap_almalinux_extra_result
|
|
||||||
changed_when: bootstrap_almalinux_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Reinstall kernel core
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
|
||||||
register: bootstrap_almalinux_kernel_result
|
|
||||||
changed_when: bootstrap_almalinux_kernel_result.rc == 0
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap Fedora
|
|
||||||
vars:
|
|
||||||
bootstrap_fedora_extra: >-
|
|
||||||
{{
|
|
||||||
lookup('vars', bootstrap_var_key)
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install Fedora base system
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
dnf --releasever={{ os_version }} --best --repo=fedora --repo=fedora-updates
|
|
||||||
--installroot=/mnt --setopt=install_weak_deps=False
|
|
||||||
groupinstall -y critical-path-base core
|
|
||||||
register: bootstrap_fedora_base_result
|
|
||||||
changed_when: bootstrap_fedora_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
|
||||||
install -y {{ bootstrap_fedora_extra }}
|
|
||||||
register: bootstrap_fedora_extra_result
|
|
||||||
changed_when: bootstrap_fedora_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Reinstall kernel core
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
|
||||||
register: bootstrap_fedora_kernel_result
|
|
||||||
changed_when: bootstrap_fedora_kernel_result.rc == 0
|
|
||||||
@@ -29,13 +29,13 @@
|
|||||||
- name: Run OS-specific bootstrap process
|
- name: Run OS-specific bootstrap process
|
||||||
vars:
|
vars:
|
||||||
bootstrap_os_task_map:
|
bootstrap_os_task_map:
|
||||||
almalinux: almalinux.yml
|
almalinux: _dnf_family.yml
|
||||||
alpine: alpine.yml
|
alpine: alpine.yml
|
||||||
archlinux: archlinux.yml
|
archlinux: archlinux.yml
|
||||||
debian: debian.yml
|
debian: debian.yml
|
||||||
fedora: fedora.yml
|
fedora: _dnf_family.yml
|
||||||
opensuse: opensuse.yml
|
opensuse: opensuse.yml
|
||||||
rocky: rocky.yml
|
rocky: _dnf_family.yml
|
||||||
rhel: rhel.yml
|
rhel: rhel.yml
|
||||||
ubuntu: ubuntu.yml
|
ubuntu: ubuntu.yml
|
||||||
ubuntu-lts: ubuntu.yml
|
ubuntu-lts: ubuntu.yml
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap Rocky Linux
|
|
||||||
vars:
|
|
||||||
bootstrap_rocky_extra: >-
|
|
||||||
{{
|
|
||||||
lookup('vars', bootstrap_var_key)
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install Rocky Linux base system
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
|
|
||||||
--installroot=/mnt --setopt=install_weak_deps=False
|
|
||||||
groupinstall -y core
|
|
||||||
register: bootstrap_rocky_base_result
|
|
||||||
changed_when: bootstrap_rocky_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
|
||||||
install -y {{ bootstrap_rocky_extra }}
|
|
||||||
register: bootstrap_rocky_extra_result
|
|
||||||
changed_when: bootstrap_rocky_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Reinstall kernel core
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
|
||||||
register: bootstrap_rocky_kernel_result
|
|
||||||
changed_when: bootstrap_rocky_kernel_result.rc == 0
|
|
||||||
@@ -51,9 +51,10 @@
|
|||||||
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
||||||
|
|
||||||
- name: Enable universe repository
|
- name: Enable universe repository
|
||||||
ansible.builtin.command: "{{ chroot_command }} sed -i '1s|$| universe|' /etc/apt/sources.list"
|
ansible.builtin.replace:
|
||||||
register: bootstrap_ubuntu_repo_result
|
path: /mnt/etc/apt/sources.list
|
||||||
changed_when: bootstrap_ubuntu_repo_result.rc == 0
|
regexp: '^(deb\s+\S+\s+\S+\s+main)$'
|
||||||
|
replace: '\1 universe'
|
||||||
|
|
||||||
- name: Update package lists
|
- name: Update package lists
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt update"
|
ansible.builtin.command: "{{ chroot_command }} apt update"
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ bootstrap_rhel: "{{ bootstrap_rhel_base + bootstrap_rhel_versioned }}"
|
|||||||
bootstrap_almalinux: >-
|
bootstrap_almalinux: >-
|
||||||
{{
|
{{
|
||||||
bootstrap_rhel_base
|
bootstrap_rhel_base
|
||||||
+ ['grub2', 'grub2-efi', 'dbus-daemon', 'lrzsz',
|
+ ['grub2', 'grub2-efi',
|
||||||
'nfsv4-client-utils', 'nc', 'ppp', 'python3', 'zram-generator']
|
'nfsv4-client-utils', 'nc', 'ppp', 'python3', 'zram-generator']
|
||||||
|
+ (['dbus-daemon'] if (os_version_major | default('10') | int) >= 9 else [])
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_rocky: >-
|
bootstrap_rocky: >-
|
||||||
|
|||||||
@@ -1,4 +1,85 @@
|
|||||||
---
|
---
|
||||||
|
# User-facing API: override via top-level `cis` dict in inventory.
|
||||||
|
# Merged with these defaults in _normalize.yml → cis_cfg.
|
||||||
|
cis_defaults:
|
||||||
|
modules_blacklist:
|
||||||
|
- freevxfs
|
||||||
|
- jffs2
|
||||||
|
- hfs
|
||||||
|
- hfsplus
|
||||||
|
- cramfs
|
||||||
|
- udf
|
||||||
|
- usb-storage
|
||||||
|
- dccp
|
||||||
|
- sctp
|
||||||
|
- rds
|
||||||
|
- tipc
|
||||||
|
- firewire-core
|
||||||
|
- firewire-sbp2
|
||||||
|
- thunderbolt
|
||||||
|
sysctl:
|
||||||
|
fs.suid_dumpable: 0
|
||||||
|
kernel.dmesg_restrict: 1
|
||||||
|
kernel.kptr_restrict: 2
|
||||||
|
kernel.perf_event_paranoid: 3
|
||||||
|
kernel.unprivileged_bpf_disabled: 1
|
||||||
|
kernel.yama.ptrace_scope: 2
|
||||||
|
kernel.randomize_va_space: 2
|
||||||
|
net.ipv4.ip_forward: 0
|
||||||
|
net.ipv4.tcp_syncookies: 1
|
||||||
|
net.ipv4.icmp_echo_ignore_broadcasts: 1
|
||||||
|
net.ipv4.icmp_ignore_bogus_error_responses: 1
|
||||||
|
net.ipv4.conf.all.log_martians: 1
|
||||||
|
net.ipv4.conf.all.rp_filter: 1
|
||||||
|
net.ipv4.conf.all.secure_redirects: 0
|
||||||
|
net.ipv4.conf.all.send_redirects: 0
|
||||||
|
net.ipv4.conf.all.accept_redirects: 0
|
||||||
|
net.ipv4.conf.all.accept_source_route: 0
|
||||||
|
net.ipv4.conf.all.arp_ignore: 1
|
||||||
|
net.ipv4.conf.all.arp_announce: 2
|
||||||
|
net.ipv4.conf.default.log_martians: 1
|
||||||
|
net.ipv4.conf.default.rp_filter: 1
|
||||||
|
net.ipv4.conf.default.secure_redirects: 0
|
||||||
|
net.ipv4.conf.default.send_redirects: 0
|
||||||
|
net.ipv4.conf.default.accept_redirects: 0
|
||||||
|
net.ipv6.conf.all.accept_redirects: 0
|
||||||
|
net.ipv6.conf.all.disable_ipv6: 1
|
||||||
|
net.ipv6.conf.default.accept_redirects: 0
|
||||||
|
net.ipv6.conf.default.disable_ipv6: 1
|
||||||
|
net.ipv6.conf.lo.disable_ipv6: 1
|
||||||
|
sshd_options:
|
||||||
|
- { option: LogLevel, value: VERBOSE }
|
||||||
|
- { option: LoginGraceTime, value: "60" }
|
||||||
|
- { option: PermitRootLogin, value: "no" }
|
||||||
|
- { option: StrictModes, value: "yes" }
|
||||||
|
- { option: MaxAuthTries, value: "4" }
|
||||||
|
- { option: MaxSessions, value: "10" }
|
||||||
|
- { option: MaxStartups, value: "10:30:60" }
|
||||||
|
- { option: PubkeyAuthentication, value: "yes" }
|
||||||
|
- { option: HostbasedAuthentication, value: "no" }
|
||||||
|
- { option: IgnoreRhosts, value: "yes" }
|
||||||
|
- { option: PasswordAuthentication, value: "no" }
|
||||||
|
- { option: PermitEmptyPasswords, value: "no" }
|
||||||
|
- { option: KerberosAuthentication, value: "no" }
|
||||||
|
- { option: GSSAPIAuthentication, value: "no" }
|
||||||
|
- { option: AllowAgentForwarding, value: "no" }
|
||||||
|
- { option: AllowTcpForwarding, value: "no" }
|
||||||
|
- { option: KbdInteractiveAuthentication, value: "no" }
|
||||||
|
- { option: GatewayPorts, value: "no" }
|
||||||
|
- { option: X11Forwarding, value: "no" }
|
||||||
|
- { option: PermitUserEnvironment, value: "no" }
|
||||||
|
- { option: ClientAliveInterval, value: "300" }
|
||||||
|
- { option: ClientAliveCountMax, value: "1" }
|
||||||
|
- { option: PermitTunnel, value: "no" }
|
||||||
|
- { option: Banner, value: /etc/issue.net }
|
||||||
|
pwquality_minlen: 14
|
||||||
|
tmout: 900
|
||||||
|
umask: "077"
|
||||||
|
umask_profile: "027"
|
||||||
|
faillock_deny: 5
|
||||||
|
faillock_unlock_time: 900
|
||||||
|
password_remember: 5
|
||||||
|
|
||||||
# Platform-specific binary names for CIS permission targets
|
# Platform-specific binary names for CIS permission targets
|
||||||
cis_fusermount_binary: "{{ 'fusermount3' if is_rhel | default(false) | bool else 'fusermount' }}"
|
cis_fusermount_binary: "{{ 'fusermount3' if is_rhel | default(false) | bool else 'fusermount' }}"
|
||||||
cis_write_binary: "{{ 'write' if is_rhel | default(false) | bool else 'wall' }}"
|
cis_write_binary: "{{ 'write' if is_rhel | default(false) | bool else 'wall' }}"
|
||||||
|
|||||||
4
roles/cis/tasks/_normalize.yml
Normal file
4
roles/cis/tasks/_normalize.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: Build cis_cfg from defaults and user overrides
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cis_cfg: "{{ cis_defaults | combine(cis | default({}), recursive=true) }}"
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: "/mnt/etc/profile"
|
path: "/mnt/etc/profile"
|
||||||
regexp: "^(\\s*)umask\\s+\\d+"
|
regexp: "^(\\s*)umask\\s+\\d+"
|
||||||
line: "umask 027"
|
line: "umask {{ cis_cfg.umask_profile }}"
|
||||||
|
|
||||||
# Non-RHEL/non-Debian distros: loop evaluates to [] (intentional skip)
|
# Non-RHEL/non-Debian distros: loop evaluates to [] (intentional skip)
|
||||||
- name: Prevent Login to Accounts With Empty Password
|
- name: Prevent Login to Accounts With Empty Password
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
# Fedora ships its own crypto-policies preset and update-crypto-policies
|
||||||
|
# behaves differently; applying DEFAULT:NO-SHA1 can break package signing.
|
||||||
- name: Configure System Cryptography Policy
|
- name: Configure System Cryptography Policy
|
||||||
when: os in (os_family_rhel | difference(['fedora']))
|
when: os in (os_family_rhel | difference(['fedora']))
|
||||||
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
---
|
---
|
||||||
|
- name: Normalize CIS configuration
|
||||||
|
ansible.builtin.include_tasks: _normalize.yml
|
||||||
|
|
||||||
- name: Include CIS hardening tasks
|
- name: Include CIS hardening tasks
|
||||||
ansible.builtin.include_tasks: "{{ cis_task }}"
|
ansible.builtin.include_tasks: "{{ cis_task }}"
|
||||||
loop:
|
loop:
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
---
|
---
|
||||||
- name: Disable Kernel Modules
|
- name: Disable Kernel Modules
|
||||||
vars:
|
vars:
|
||||||
cis_modules_base:
|
|
||||||
- freevxfs
|
|
||||||
- jffs2
|
|
||||||
- hfs
|
|
||||||
- hfsplus
|
|
||||||
- cramfs
|
|
||||||
- udf
|
|
||||||
- usb-storage
|
|
||||||
- dccp
|
|
||||||
- sctp
|
|
||||||
- rds
|
|
||||||
- tipc
|
|
||||||
cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}"
|
cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['squashfs'] }}"
|
||||||
cis_modules_all: "{{ cis_modules_base + cis_modules_squashfs }}"
|
cis_modules_all: "{{ cis_cfg.modules_blacklist + cis_modules_squashfs }}"
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /mnt/etc/modprobe.d/cis.conf
|
dest: /mnt/etc/modprobe.d/cis.conf
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
line: "{{ item.content }}"
|
line: "{{ item.content }}"
|
||||||
loop:
|
loop:
|
||||||
- { path: /mnt/etc/security/limits.conf, regexp: '^\*\s+hard\s+core\s+', content: "* hard core 0" }
|
- { path: /mnt/etc/security/limits.conf, regexp: '^\*\s+hard\s+core\s+', content: "* hard core 0" }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*minlen\s*=', content: minlen = 14 }
|
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*minlen\s*=', content: "minlen = {{ cis_cfg.pwquality_minlen }}" }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*dcredit\s*=', content: dcredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*dcredit\s*=', content: dcredit = -1 }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ucredit\s*=', content: ucredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ucredit\s*=', content: ucredit = -1 }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ocredit\s*=', content: ocredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ocredit\s*=', content: ocredit = -1 }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*lcredit\s*=', content: lcredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*lcredit\s*=', content: lcredit = -1 }
|
||||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
||||||
regexp: '^\s*umask\s+\d+'
|
regexp: '^\s*umask\s+\d+'
|
||||||
content: umask 077
|
content: "umask {{ cis_cfg.umask }}"
|
||||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
||||||
regexp: '^\s*(export\s+)?TMOUT='
|
regexp: '^\s*(export\s+)?TMOUT='
|
||||||
content: export TMOUT=900
|
content: "export TMOUT={{ cis_cfg.tmout }}"
|
||||||
- path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}'
|
- path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}'
|
||||||
regexp: '^\s*#?\s*Storage='
|
regexp: '^\s*#?\s*Storage='
|
||||||
content: Storage=persistent
|
content: Storage=persistent
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
}}
|
}}
|
||||||
regexp: '^\s*auth\s+required\s+pam_faillock\.so'
|
regexp: '^\s*auth\s+required\s+pam_faillock\.so'
|
||||||
content: >-
|
content: >-
|
||||||
auth required pam_faillock.so onerr=fail audit silent deny=5 unlock_time=900
|
auth required pam_faillock.so onerr=fail audit silent deny={{ cis_cfg.faillock_deny }} unlock_time={{ cis_cfg.faillock_unlock_time }}
|
||||||
- path: >-
|
- path: >-
|
||||||
/mnt/etc/{{
|
/mnt/etc/{{
|
||||||
"pam.d/common-account"
|
"pam.d/common-account"
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
}}
|
}}
|
||||||
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
|
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
|
||||||
content: >-
|
content: >-
|
||||||
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
|
password [success=1 default=ignore] pam_unix.so obscure sha512 remember={{ cis_cfg.password_remember }}
|
||||||
- { path: /mnt/etc/hosts.deny, regexp: '^ALL:\s*ALL', content: "ALL: ALL" }
|
- { path: /mnt/etc/hosts.deny, regexp: '^ALL:\s*ALL', content: "ALL: ALL" }
|
||||||
- { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" }
|
- { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" }
|
||||||
loop_control:
|
loop_control:
|
||||||
|
|||||||
@@ -4,31 +4,7 @@
|
|||||||
path: /mnt/etc/ssh/sshd_config
|
path: /mnt/etc/ssh/sshd_config
|
||||||
regexp: ^\s*#?{{ item.option }}\s+.*$
|
regexp: ^\s*#?{{ item.option }}\s+.*$
|
||||||
line: "{{ item.option }} {{ item.value }}"
|
line: "{{ item.option }} {{ item.value }}"
|
||||||
loop:
|
loop: "{{ cis_cfg.sshd_options }}"
|
||||||
- { option: LogLevel, value: VERBOSE }
|
|
||||||
- { option: LoginGraceTime, value: "60" }
|
|
||||||
- { option: PermitRootLogin, value: "no" }
|
|
||||||
- { option: StrictModes, value: "yes" }
|
|
||||||
- { option: MaxAuthTries, value: "4" }
|
|
||||||
- { option: MaxSessions, value: "10" }
|
|
||||||
- { option: MaxStartups, value: "10:30:60" }
|
|
||||||
- { option: PubkeyAuthentication, value: "yes" }
|
|
||||||
- { option: HostbasedAuthentication, value: "no" }
|
|
||||||
- { option: IgnoreRhosts, value: "yes" }
|
|
||||||
- { option: PasswordAuthentication, value: "no" }
|
|
||||||
- { option: PermitEmptyPasswords, value: "no" }
|
|
||||||
- { option: KerberosAuthentication, value: "no" }
|
|
||||||
- { option: GSSAPIAuthentication, value: "no" }
|
|
||||||
- { option: AllowAgentForwarding, value: "no" }
|
|
||||||
- { option: AllowTcpForwarding, value: "no" }
|
|
||||||
- { option: KbdInteractiveAuthentication, value: "no" }
|
|
||||||
- { option: GatewayPorts, value: "no" }
|
|
||||||
- { option: X11Forwarding, value: "no" }
|
|
||||||
- { option: PermitUserEnvironment, value: "no" }
|
|
||||||
- { option: ClientAliveInterval, value: "300" }
|
|
||||||
- { option: ClientAliveCountMax, value: "1" }
|
|
||||||
- { option: PermitTunnel, value: "no" }
|
|
||||||
- { option: Banner, value: /etc/issue.net }
|
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.option }}"
|
label: "{{ item.option }}"
|
||||||
|
|
||||||
|
|||||||
@@ -5,30 +5,6 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: |
|
content: |
|
||||||
## CIS Sysctl configurations
|
## CIS Sysctl configurations
|
||||||
fs.suid_dumpable=0
|
{% for key, value in cis_cfg.sysctl | dictsort %}
|
||||||
kernel.dmesg_restrict=1
|
{{ key }}={{ value }}
|
||||||
kernel.yama.ptrace_scope=1
|
{% endfor %}
|
||||||
kernel.randomize_va_space=2
|
|
||||||
# Network
|
|
||||||
# Disable forwarding; override in inventory for routers/containers
|
|
||||||
net.ipv4.ip_forward=0
|
|
||||||
net.ipv4.tcp_syncookies=1
|
|
||||||
net.ipv4.icmp_echo_ignore_broadcasts=1
|
|
||||||
net.ipv4.icmp_ignore_bogus_error_responses=1
|
|
||||||
net.ipv4.conf.all.log_martians=1
|
|
||||||
net.ipv4.conf.all.rp_filter=1
|
|
||||||
net.ipv4.conf.all.secure_redirects=0
|
|
||||||
net.ipv4.conf.all.send_redirects=0
|
|
||||||
net.ipv4.conf.all.accept_redirects=0
|
|
||||||
net.ipv4.conf.all.accept_source_route=0
|
|
||||||
net.ipv4.conf.default.log_martians=1
|
|
||||||
net.ipv4.conf.default.rp_filter=1
|
|
||||||
net.ipv4.conf.default.secure_redirects=0
|
|
||||||
net.ipv4.conf.default.send_redirects=0
|
|
||||||
net.ipv4.conf.default.accept_redirects=0
|
|
||||||
net.ipv6.conf.all.accept_redirects=0
|
|
||||||
# Disable IPv6; override in inventory if IPv6 is needed
|
|
||||||
net.ipv6.conf.all.disable_ipv6=1
|
|
||||||
net.ipv6.conf.default.accept_redirects=0
|
|
||||||
net.ipv6.conf.default.disable_ipv6=1
|
|
||||||
net.ipv6.conf.lo.disable_ipv6=1
|
|
||||||
|
|||||||
@@ -16,18 +16,6 @@
|
|||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_get_xml.get_xml }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_get_xml.get_xml }}"
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Remove boot ISO device from VM XML (target match)
|
|
||||||
community.general.xml:
|
|
||||||
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
|
||||||
xpath: "/domain/devices/disk[target/@dev='sda']"
|
|
||||||
state: absent
|
|
||||||
register: cleanup_libvirt_xml_strip_boot
|
|
||||||
|
|
||||||
- name: Update cleaned VM XML after removing boot ISO
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot.xmlstring }}"
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Remove boot ISO device from VM XML (source match)
|
- name: Remove boot ISO device from VM XML (source match)
|
||||||
when: boot_iso is defined and boot_iso | length > 0
|
when: boot_iso is defined and boot_iso | length > 0
|
||||||
community.general.xml:
|
community.general.xml:
|
||||||
@@ -42,16 +30,16 @@
|
|||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot_source.xmlstring }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot_source.xmlstring }}"
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Remove cloud-init ISO device from VM XML (target match)
|
- name: Remove boot ISO device from VM XML (target fallback)
|
||||||
community.general.xml:
|
community.general.xml:
|
||||||
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
||||||
xpath: "/domain/devices/disk[target/@dev='sdb']"
|
xpath: "/domain/devices/disk[target/@dev='sda']"
|
||||||
state: absent
|
state: absent
|
||||||
register: cleanup_libvirt_xml_strip_cloudinit
|
register: cleanup_libvirt_xml_strip_boot
|
||||||
|
|
||||||
- name: Update cleaned VM XML after removing cloud-init ISO
|
- name: Update cleaned VM XML after removing boot ISO
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_cloudinit.xmlstring }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot.xmlstring }}"
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Remove cloud-init ISO device from VM XML (source match)
|
- name: Remove cloud-init ISO device from VM XML (source match)
|
||||||
@@ -66,6 +54,18 @@
|
|||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_cloudinit_source.xmlstring }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_cloudinit_source.xmlstring }}"
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Remove cloud-init ISO device from VM XML (target fallback)
|
||||||
|
community.general.xml:
|
||||||
|
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
||||||
|
xpath: "/domain/devices/disk[target/@dev='sdb']"
|
||||||
|
state: absent
|
||||||
|
register: cleanup_libvirt_xml_strip_cloudinit
|
||||||
|
|
||||||
|
- name: Update cleaned VM XML after removing cloud-init ISO
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_cloudinit.xmlstring }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Strip XML declaration for libvirt define
|
- name: Strip XML declaration for libvirt define
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
cleanup_libvirt_domain_xml_clean: >-
|
cleanup_libvirt_domain_xml_clean: >-
|
||||||
|
|||||||
@@ -259,7 +259,7 @@
|
|||||||
(system_cfg.memory | float / 1024 >= 16.0)
|
(system_cfg.memory | float / 1024 >= 16.0)
|
||||||
| ternary(
|
| ternary(
|
||||||
(system_cfg.memory | float / 2048),
|
(system_cfg.memory | float / 2048),
|
||||||
[system_cfg.memory | float / 1024, 4.0] | max
|
[system_cfg.memory | float / 1024, 2.0] | max
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
+ 5.5
|
+ 5.5
|
||||||
|
|||||||
@@ -152,6 +152,6 @@ partitioning_swap_size_gb: >-
|
|||||||
((partitioning_memory_mb / 1024) >= 16.0)
|
((partitioning_memory_mb / 1024) >= 16.0)
|
||||||
| ternary(
|
| ternary(
|
||||||
(partitioning_memory_mb / 2048) | int,
|
(partitioning_memory_mb / 2048) | int,
|
||||||
[partitioning_memory_mb / 1024, 4.0] | max | int
|
[partitioning_memory_mb / 1024, 2.0] | max | int
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
# Swap sizing:
|
# Swap sizing:
|
||||||
# - RAM >= 16 GB → swap = RAM/2 (in GB)
|
# - RAM >= 16 GB → swap = RAM/2 (in GB)
|
||||||
# - RAM < 16 GB → swap = max(RAM_GB, 4)
|
# - RAM < 16 GB → swap = max(RAM_GB, 2)
|
||||||
# - Capped to: min(target, 4 + max(disk - overhead, 0))
|
# - Capped to: min(target, 4 + max(disk - overhead, 0))
|
||||||
# - Further capped to: max available after subtracting reserved + CIS + extent reserve + 4 GB buffer
|
# - Further capped to: max available after subtracting reserved + CIS + extent reserve + 4 GB buffer
|
||||||
#
|
#
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
((partitioning_memory_mb | float / 1024) >= 16.0)
|
((partitioning_memory_mb | float / 1024) >= 16.0)
|
||||||
| ternary(
|
| ternary(
|
||||||
(partitioning_memory_mb | float / 2048),
|
(partitioning_memory_mb | float / 2048),
|
||||||
[(partitioning_memory_mb | float / 1024), 4] | max | float
|
[(partitioning_memory_mb | float / 1024), 2] | max | float
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if system_cfg.features.swap.enabled | bool
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
|||||||
@@ -48,6 +48,14 @@
|
|||||||
ansible.builtin.command: udevadm settle
|
ansible.builtin.command: udevadm settle
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Wipe existing filesystem signatures on additional disk partitions
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.builtin.command: "wipefs --force --all {{ item.partition }}"
|
||||||
|
changed_when: true
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.partition }}"
|
||||||
|
|
||||||
- name: Create filesystems on additional disks
|
- name: Create filesystems on additional disks
|
||||||
when: partitioning_extra_disks | length > 0
|
when: partitioning_extra_disks | length > 0
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ users:
|
|||||||
- name: "{{ user.name }}"
|
- name: "{{ user.name }}"
|
||||||
primary_group: "{{ user.name }}"
|
primary_group: "{{ user.name }}"
|
||||||
groups: users
|
groups: users
|
||||||
sudo: "{{ 'ALL=(ALL) NOPASSWD:ALL' if (user.sudo is defined and user.sudo is sameas true) else user.sudo | default('ALL=(ALL) NOPASSWD:ALL') }}"
|
sudo: "ALL=(ALL) NOPASSWD:ALL"
|
||||||
passwd: "{{ user.password | password_hash('sha512') }}"
|
passwd: "{{ user.password | password_hash('sha512') }}"
|
||||||
lock_passwd: false
|
lock_passwd: false
|
||||||
{% set ssh_keys = user['keys'] | default([]) %}
|
{% set ssh_keys = user['keys'] | default([]) %}
|
||||||
|
|||||||
Reference in New Issue
Block a user