Compare commits
155 Commits
master
...
415fc5a26b
| Author | SHA1 | Date | |
|---|---|---|---|
| 415fc5a26b | |||
| aaada3a826 | |||
| a5e516aa91 | |||
| b9544a60d3 | |||
| d0b26a57ef | |||
| f73982d502 | |||
| d92b89b001 | |||
| 3362aad149 | |||
| 2e59d7d27c | |||
| d0bcbb95d8 | |||
| 0181f9104a | |||
| 4de84a7312 | |||
| 04d5e99e56 | |||
| 378d9a88c2 | |||
| 905043baf3 | |||
| 9164815185 | |||
| 81f15fffb7 | |||
| d454c3cd82 | |||
| 9ffb2aa69f | |||
| 6d843ff409 | |||
| 775dbefa67 | |||
| 06823044dd | |||
| 919c44bb29 | |||
| 0d01f2afdc | |||
| e532dcac16 | |||
| 6cbecf2db0 | |||
| d612f9dabb | |||
| 00c3cd5180 | |||
| fef1f44a07 | |||
| e1464562f7 | |||
| 60c552be45 | |||
| c96fcf5e96 | |||
| 4e70ee2e3e | |||
| 81bbd2b22a | |||
| e65fbfd570 | |||
| 122bd5cdf4 | |||
| c8d3de3d8d | |||
| 4ed15e5ea8 | |||
| 518babe328 | |||
| 918e14051d | |||
| 3d18962160 | |||
| 457d558133 | |||
| e06a95fdbc | |||
| 7bae512560 | |||
| 3e91057689 | |||
| e9647571fc | |||
| c32769d831 | |||
| 7cfa4aee8d | |||
| a7e7f49d84 | |||
| cfcccbf512 | |||
| 75c4ba6b4c | |||
| b62066d675 | |||
| 53a2c27984 | |||
| bb82ff120b | |||
| 221d77b94d | |||
| d71ea511f9 | |||
| b3299781dc | |||
| 5e7a06b7db | |||
| d77f65ce05 | |||
| 39fc15d7d8 | |||
| b076968404 | |||
| 4f03ccbfcf | |||
| 5746be4561 | |||
| 39cc49a05b | |||
| 2d63ca9c5a | |||
| 9f56328890 | |||
| dc763bdc42 | |||
| 25deaab87d | |||
| 89f054e8fd | |||
| cbe238f4d5 | |||
| c6f1686db8 | |||
| c9a15dfccf | |||
| f83a9ebd67 | |||
| e16868a78d | |||
| 406db38296 | |||
| cb3f36a040 | |||
| d97f0cfff8 | |||
| e8f609dd03 | |||
| a599e26a63 | |||
| 3085ebc336 | |||
| f967ea1c3b | |||
| 2c4995ede8 | |||
| ccf3193c92 | |||
| d92944c345 | |||
| 3c94a33ae7 | |||
| af82baf1d8 | |||
| ec55701f00 | |||
| 2a1a47ecc1 | |||
| 4808ce4401 | |||
| db1fd13623 | |||
| e5660b0ba7 | |||
| 173ecd299b | |||
| 4d242ad987 | |||
| f8ac22cfab | |||
| 12a7549aaa | |||
| 6705411b2d | |||
| fe2b216fc7 | |||
| 26824ca6bb | |||
| c60fcca86d | |||
| cdd8062937 | |||
| ebedff1c4e | |||
| 04d05a4e8b | |||
| ee6e06a3fe | |||
| 527bc11d1d | |||
| d331e07536 | |||
| 287036bcb4 | |||
| ca5a3c8807 | |||
| c8dd89681b | |||
| 9d4af56976 | |||
| 3c55eaf4a1 | |||
| d905dce89e | |||
| 76f1382e3e | |||
| 04c27cd7d0 | |||
| 147430b36e | |||
| f8ba5c41db | |||
| 7a4fc24f32 | |||
| 7bf7c29291 | |||
| ccfce65673 | |||
| 528f2fc775 | |||
| 505110f580 | |||
| 1d1b2fff42 | |||
| 4cf4816be0 | |||
| e37b5a535b | |||
| 5312ec8cc6 | |||
| a3b772c543 | |||
| adde811f47 | |||
| f788767839 | |||
| 8b773d2304 | |||
| c988ab8f9a | |||
| 8864db253b | |||
| 06ca8d8787 | |||
| 374b5fc7ef | |||
| 6bfd530c90 | |||
| b077e549db | |||
| 43ce280d11 | |||
| a6b51b4cb4 | |||
| 6dd31cc95f | |||
| 4b98ec1434 | |||
| 2444c5d7af | |||
| ec6ca49265 | |||
| fe43bf6733 | |||
| 31c155ce92 | |||
| 0c75114b94 | |||
| cd9ed65c91 | |||
| 9986d19ed6 | |||
| d73e78c5f2 | |||
| b6f620fb70 | |||
| cc40bae858 | |||
| 344753fa5b | |||
| 6be464a0e2 | |||
| 48b5f602fa | |||
| cc118274a3 | |||
|
|
d733513e29 | ||
|
|
402f2b9bc0 | ||
| 4ec5432989 |
@@ -1,6 +1,2 @@
|
|||||||
skip_list:
|
skip_list:
|
||||||
- run-once
|
- run-once
|
||||||
- var-naming[no-role-prefix] # user-facing API dicts (cis, system, hypervisor) are intentionally not role-prefixed
|
|
||||||
- args[module] # false positives from variable-based module_defaults (_proxmox_auth, _vmware_auth)
|
|
||||||
exclude_paths:
|
|
||||||
- roles/global_defaults/
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,6 +6,3 @@ vars.yml
|
|||||||
vars.yaml
|
vars.yaml
|
||||||
vars_kvm.yml
|
vars_kvm.yml
|
||||||
vars_libvirt.yml
|
vars_libvirt.yml
|
||||||
vars_proxmox.yml
|
|
||||||
|
|
||||||
.sisyphus/
|
|
||||||
|
|||||||
19
.yamllint
19
.yamllint
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
extends: default
|
|
||||||
|
|
||||||
rules:
|
|
||||||
document-start: disable
|
|
||||||
line-length:
|
|
||||||
max: 200
|
|
||||||
allow-non-breakable-words: true
|
|
||||||
allow-non-breakable-inline-mappings: true
|
|
||||||
truthy:
|
|
||||||
allowed-values: ["true", "false"]
|
|
||||||
check-keys: false
|
|
||||||
comments:
|
|
||||||
min-spaces-from-content: 1
|
|
||||||
comments-indentation: disable
|
|
||||||
braces:
|
|
||||||
max-spaces-inside: 1
|
|
||||||
octal-values:
|
|
||||||
forbid-implicit-octal: true
|
|
||||||
637
README.md
637
README.md
@@ -1,458 +1,203 @@
|
|||||||
# Ansible Bootstrap
|
# Ansible-Bootstrap
|
||||||
|
|
||||||
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.
|
An Ansible playbook for automating system bootstrap processes in an Infrastructure-as-Code manner.
|
||||||
|
|
||||||
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.
|
# Info
|
||||||
|
|
||||||
|
Most of the roles are adaptable for use with systems beyond ArchLinux, requiring only that the target system can install a necessary package manager, such as `dnf` for RHEL-based systems. Additionally, a replacement for the `arch-chroot` command may be required for these systems.
|
||||||
|
|
||||||
|
**NOTE**:
|
||||||
|
|
||||||
|
- For RHEL 8, RHEL 9, and RHEL 10, repository access requires the `rhel_iso` variable. This variable specifies a local ISO or proxy repository.
|
||||||
|
- RHEL systems do not support `btrfs`. Use `ext4` or `xfs` as alternatives.
|
||||||
|
- For RHEL 8, `xfs` may cause installation issues; `ext4` is recommended.
|
||||||
|
- `custom_iso: true` skips ArchISO validation and pacman setup, your installer ISO must provide the tools required by the selected roles.
|
||||||
|
|
||||||
|
# Supported Distributions
|
||||||
|
|
||||||
|
This playbook supports multiple Linux distributions with specific versions tailored to each.
|
||||||
|
Below is a list of supported distributions:
|
||||||
|
|
||||||
|
| `os` | Distribution |
|
||||||
|
| ---------- | ---------------------------------- |
|
||||||
|
| archlinux | ArchLinux (Latest rolling release) |
|
||||||
|
| almalinux | AlmaLinux 9.x |
|
||||||
|
| debian11 | Debian 11 (Bullseye) |
|
||||||
|
| debian12 | Debian 12 (Bookworm) |
|
||||||
|
| debian13 | Debian 13 (Trixie) |
|
||||||
|
| fedora | Fedora 42 |
|
||||||
|
| rhel8 | Red Hat Enterprise Linux 8 |
|
||||||
|
| rhel9 | Red Hat Enterprise Linux 9 |
|
||||||
|
| rhel10 | Red Hat Enterprise Linux 10 |
|
||||||
|
| rocky | Rocky Linux 9.x |
|
||||||
|
| ubuntu | Ubuntu 25.04 (Plucky Puffin) |
|
||||||
|
| ubuntu-lts | Ubuntu 24.04 LTS (Noble Numbat) |
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Supported Platforms](#1-supported-platforms)
|
1. [Overview](#1-overview)
|
||||||
2. [Compatibility Notes](#2-compatibility-notes)
|
2. [Global Variables](#2-global-variables)
|
||||||
3. [Configuration Model](#3-configuration-model)
|
3. [Inventory Variables](#3-inventory-variables)
|
||||||
4. [Variable Reference](#4-variable-reference)
|
4. [How to Use the Playbook](#4-how-to-use-the-playbook)
|
||||||
- 4.1 [Core Variables](#41-core-variables)
|
- 4.1 [Prerequisites](#41-prerequisites)
|
||||||
- 4.2 [`system` Dictionary](#42-system-dictionary)
|
- 4.2 [Running the Playbook](#42-running-the-playbook)
|
||||||
- 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary)
|
- 4.3 [Example Usage](#43-example-usage)
|
||||||
- 4.4 [`cis` Dictionary](#44-cis-dictionary)
|
|
||||||
- 4.5 [VMware Guest Operations](#45-vmware-guest-operations)
|
## 1. Overview
|
||||||
- 4.6 [Multi-Disk Schema](#46-multi-disk-schema)
|
|
||||||
- 4.7 [Advanced Partitioning Overrides](#47-advanced-partitioning-overrides)
|
The playbook uses the ArchLinux ISO as a foundational tool to provides an efficient and systematic method for the automatic deployment of a variety of Linux distributions on designated target systems. It ensures a standardized setup across different platforms, equipping each system with the essential configurations and software necessary for its designated role.
|
||||||
- 4.8 [Cleanup Defaults](#48-cleanup-defaults)
|
|
||||||
5. [Execution Pipeline](#5-execution-pipeline)
|
## 2. Global Variables
|
||||||
6. [Usage](#6-usage)
|
|
||||||
7. [Security](#7-security)
|
Global variables apply across your Ansible project and are loaded from `vars.yml` by default. These variables define common settings such as hypervisor connection details and the boot ISO path. They can be overridden by inventory variables for specific hosts or VMs if needed.
|
||||||
8. [Safety](#8-safety)
|
|
||||||
|
### 2.1 Core Provisioning
|
||||||
## 1. Supported Platforms
|
|
||||||
|
| Variable | Description | Example Value |
|
||||||
### Distributions
|
| ----------------------- | ---------------------------------------------------------- | ----------------------------------------- |
|
||||||
|
| `install_type` | Type of installation. | `virtual`, `physical` |
|
||||||
| `system.os` | Distribution | `system.version` |
|
| `hypervisor` | Type of hypervisor. | `libvirt`, `proxmox`, `vmware`, `none` |
|
||||||
| ------------ | ------------------------ | ------------------------------------- |
|
| `install_drive` | Drive where the system will be installed. | `/dev/sda` |
|
||||||
| `almalinux` | AlmaLinux | `8`, `9`, `10` |
|
| `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` |
|
||||||
| `alpine` | Alpine Linux | latest (rolling) |
|
| `rhel_iso` | Path to the RHEL ISO file, required for RHEL 8/9/10. | `local-btrfs:iso/rhel-9.4-x86_64-dvd.iso` |
|
||||||
| `archlinux` | Arch Linux | latest (rolling) |
|
| `custom_iso` (optional) | Skip ArchISO checks and pacman setup on installer media. | `true`, `false (default)` |
|
||||||
| `debian` | Debian | `10`-`13`, `unstable` |
|
| `cis` (optional) | Adjusts the installation to be CIS level 3 conformant. | `true`, `false` |
|
||||||
| `fedora` | Fedora | `38`-`45` |
|
| `selinux` (optional) | Toggle SELinux where supported. | `true`, `false` |
|
||||||
| `opensuse` | openSUSE Tumbleweed | latest (rolling) |
|
|
||||||
| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` |
|
### 2.2 Hypervisor Access (virtual installs)
|
||||||
| `rocky` | Rocky Linux | `8`, `9`, `10` |
|
|
||||||
| `ubuntu` | Ubuntu (latest non-LTS) | optional (e.g. `24.04`) |
|
| Variable | Description | Example Value |
|
||||||
| `ubuntu-lts` | Ubuntu LTS | optional (e.g. `24.04`) |
|
| ----------------------- | ---------------------------------------------------------- | -------------------- |
|
||||||
| `void` | Void Linux | latest (rolling) |
|
| `hypervisor_url` | URL/IP address for the hypervisor interface. | `192.168.0.2` |
|
||||||
|
| `hypervisor_username` | Username for hypervisor authentication. | `root@pam` |
|
||||||
### Hypervisors
|
| `hypervisor_password` | Password for hypervisor authentication. | `123456` |
|
||||||
|
| `hypervisor_datacenter` | Name of the hypervisor datacenter. | `default-datacenter` |
|
||||||
| Hypervisor | `hypervisor.type` |
|
| `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` |
|
||||||
| ----------- | ----------------- |
|
| `hypervisor_node` | Hypervisor node name. | `node01` |
|
||||||
| libvirt | `libvirt` |
|
| `hypervisor_storage` | Storage identifier for VM disks. | `local-btrfs` |
|
||||||
| Proxmox VE | `proxmox` |
|
| `vm_path` (optional) | Libvirt image dir or VMware folder path. | `/var/lib/libvirt/images` |
|
||||||
| VMware | `vmware` |
|
| `vmware_ssh` | If Ansible should use SSH after base VMware setup. | `true`, `false` |
|
||||||
| Xen | `xen` |
|
| `vlan_name` (optional) | VLAN for the VM's network interface. | `vlan100` |
|
||||||
| Bare metal | `none` |
|
| `note` (optional) | VMware VM annotation. | `Provisioned by Ansible` |
|
||||||
|
|
||||||
## 2. Compatibility Notes
|
### 2.3 VMware Tools connection (VMware installs)
|
||||||
|
|
||||||
- `rhel_iso` is required for `system.os: rhel`.
|
These are required when `hypervisor: vmware` uses the `vmware_tools` connection.
|
||||||
- RHEL installs should use `ext4` or `xfs` (not `btrfs`).
|
|
||||||
- `custom_iso: true` skips ArchISO validation; your installer must provide required tooling.
|
| Variable | Description | Example Value |
|
||||||
- On non-Arch installers, set `system.features.chroot.tool` explicitly.
|
| ------------------------------- | ------------------------------------------ | -------------------------------------- |
|
||||||
|
| `ansible_vmware_tools_user` | Guest OS user for guest operations. | `root` |
|
||||||
## 3. Configuration Model
|
| `ansible_vmware_tools_password` | Guest OS password for guest operations. | `""` |
|
||||||
|
| `ansible_vmware_guest_path` | VM inventory path (datacenter + folder). | `/dc01/vm/Folder/vm01.example.com` |
|
||||||
Two dict-based variables drive the entire configuration:
|
| `ansible_vmware_host` | vCenter/ESXi hostname. | `vcenter01.example.com` |
|
||||||
|
| `ansible_vmware_user` | vCenter/ESXi username. | `administrator@vsphere.local` |
|
||||||
- **`system`** -- host, network, users, disk layout, encryption, and feature toggles
|
| `ansible_vmware_password` | vCenter/ESXi password. | `********` |
|
||||||
- **`hypervisor`** -- virtualization backend credentials and targeting
|
| `ansible_vmware_validate_certs` | Validate vCenter/ESXi TLS certs. | `false` |
|
||||||
|
|
||||||
An optional third dict **`cis`** overrides CIS hardening parameters when `system.features.cis.enabled: true`.
|
### 2.4 Disk Encryption (optional)
|
||||||
|
|
||||||
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 | Description | Example Value |
|
||||||
|
| -------------------------- | ----------------------------------------------- | ------------------ |
|
||||||
### Variable Placement
|
| `luks_enabled` | Enable LUKS encryption for the root volume. | `true`, `false` |
|
||||||
|
| `luks_passphrase` | Passphrase used for initial LUKS format/unlock. | `1234` |
|
||||||
| Location | Scope | Typical use |
|
| `luks_mapper_name` | Decrypted mapper name. | `SYSTEM_DECRYPTED` |
|
||||||
| ------------------------ | ----------- | -------------------------------------------------------------- |
|
| `luks_auto_decrypt` | Enable automatic unlock on boot. | `true`, `false` |
|
||||||
| `group_vars/all.yml` | All hosts | Shared `hypervisor`, `system.filesystem`, `boot_iso` |
|
| `luks_auto_decrypt_method` | Auto-unlock method. | `tpm2`, `keyfile` |
|
||||||
| `group_vars/<group>.yml` | Group | Environment-specific defaults |
|
| `luks_tpm2_device` | TPM2 device for enrollment. | `auto` |
|
||||||
| `host_vars/<host>.yml` | Single host | Host-specific overrides (`system.network.ip`, `system.id`, etc.) |
|
| `luks_tpm2_pcrs` | TPM2 PCR list (systemd-cryptenroll). | `7` |
|
||||||
|
| `luks_keyfile_size` | Keyfile size in bytes for initramfs. | `64` |
|
||||||
### Example Inventory
|
| `luks_options` | LUKS options passed to crypttab/kernel. | `discard,tries=3` |
|
||||||
|
| `luks_type` | LUKS format type. | `luks2` |
|
||||||
```yaml
|
| `luks_cipher` | LUKS cipher. | `aes-xts-plain64` |
|
||||||
all:
|
| `luks_hash` | LUKS hash. | `sha512` |
|
||||||
vars:
|
| `luks_iter_time` | LUKS iter time in milliseconds. | `4000` |
|
||||||
system:
|
| `luks_key_size` | LUKS key size in bits. | `512` |
|
||||||
filesystem: btrfs
|
| `luks_pbkdf` | LUKS PBKDF algorithm. | `argon2id` |
|
||||||
boot_iso: "local:iso/archlinux-x86_64.iso"
|
| `luks_use_urandom` | Reserved; module uses cryptsetup defaults. | `true` |
|
||||||
hypervisor:
|
| `luks_verify_passphrase` | Reserved; module uses cryptsetup defaults. | `true` |
|
||||||
type: proxmox
|
|
||||||
url: pve01.example.com
|
To protect sensitive information, such as passwords, API keys, and other confidential variables (e.g., `hypervisor_password`), **it is recommended to use Ansible Vault**.
|
||||||
username: root@pam
|
|
||||||
password: !vault |
|
## 3. Inventory Variables
|
||||||
$ANSIBLE_VAULT...
|
|
||||||
node: pve01
|
Inventory variables are defined for individual hosts or VMs in the inventory file, allowing customization of settings such as the operating system, filesystem, and compliance with CIS benchmarks. These variables can be set globally and overridden for specific hosts or VMs.
|
||||||
storage: local-lvm
|
|
||||||
|
### 3.1 System Identity and OS
|
||||||
children:
|
|
||||||
bootstrap:
|
| Variable | Description | Example Value |
|
||||||
hosts:
|
| ------------ | -------------------------------------- | ---------------------- |
|
||||||
app01.example.com:
|
| `os` | Operating system to be installed. | `ubuntu-lts` |
|
||||||
ansible_host: 10.0.0.10
|
| `filesystem` | Filesystem type for the root volume. | `btrfs`, `ext4`, `xfs` |
|
||||||
system:
|
| `hostname` | The hostname assigned to the system. | `vm01` |
|
||||||
type: virtual
|
|
||||||
os: debian
|
### 3.2 Credentials and Access
|
||||||
version: "12"
|
|
||||||
name: app01.example.com
|
These are prompted by default via `vars_prompt` in `main.yml`, but can be supplied via inventory/vars/`-e` for non-interactive runs.
|
||||||
id: 101
|
|
||||||
cpus: 2
|
| Variable | Description | Example Value |
|
||||||
memory: 4096
|
| ----------------- | ---------------------------------- | ----------------- |
|
||||||
network:
|
| `root_password` | Root password (vault recommended). | `SecurePass123` |
|
||||||
bridge: vmbr0
|
| `user_name` | Username for a user account. | `adminuser` |
|
||||||
ip: 10.0.0.10
|
| `user_password` | Password for the user account. | `UserPass123` |
|
||||||
prefix: 24
|
| `user_public_key` | SSH Key for the user account. | `ssh-ed25519 AAAA` |
|
||||||
gateway: 10.0.0.1
|
|
||||||
dns:
|
### 3.3 Networking
|
||||||
servers: [1.1.1.1, 1.0.0.1]
|
|
||||||
search: [example.com]
|
| Variable | Description | Example Value |
|
||||||
disks:
|
| --------------- | -------------------------------------------------------------- | ----------------- |
|
||||||
- size: 40
|
| `vm_ip` | IP address assigned to the system (omit to use DHCP). | `192.168.0.10` |
|
||||||
- size: 120
|
| `vm_nms` | Netmask bits for static addressing. | `24` |
|
||||||
mount:
|
| `vm_gw` | Default gateway IP address (static only). | `192.168.0.1` |
|
||||||
path: /data
|
| `vm_dns` | DNS server IP address(es). | `1.0.0.1,1.1.1.1` |
|
||||||
fstype: xfs
|
| `vm_dns_search` | DNS search zone(s) for the network configuration. | `example.com` |
|
||||||
users:
|
| `vm_nif` | Network interface/bridge for the VM's network connection. | `vmbr0` |
|
||||||
- name: ops
|
|
||||||
password: !vault |
|
### 3.4 VM Sizing (virtual installs)
|
||||||
$ANSIBLE_VAULT...
|
|
||||||
keys:
|
| Variable | Description | Example Value |
|
||||||
- "ssh-ed25519 AAAA..."
|
| ----------- | --------------------------------- | ------------- |
|
||||||
sudo: true
|
| `vm_id` | Unique identifier for the VM. | `101` |
|
||||||
root:
|
| `vm_size` | Disk size allocated in GB. | `20` |
|
||||||
password: !vault |
|
| `vm_memory` | Amount of memory in MB. | `2048` |
|
||||||
$ANSIBLE_VAULT...
|
| `vm_cpus` | Number of CPU cores. | `4` |
|
||||||
luks:
|
| `vm_ballo` | Ballooning memory size (optional).| `2048` |
|
||||||
enabled: true
|
|
||||||
passphrase: !vault |
|
### 3.5 Post-install Packages
|
||||||
$ANSIBLE_VAULT...
|
|
||||||
method: tpm2
|
| Variable | Description | Example Value |
|
||||||
tpm2:
|
| ------------------------ | --------------------------------------------------------------------- | ------------------ |
|
||||||
pcrs: "7"
|
| `extra_packages` (optional) | Additional packages installed after the first boot into the installed OS. | `["git", "jq"]` |
|
||||||
features:
|
|
||||||
cis:
|
## 4. How to Use the Playbook
|
||||||
enabled: true
|
|
||||||
firewall:
|
### 4.1 Prerequisites
|
||||||
enabled: true
|
|
||||||
backend: firewalld
|
Before running the playbook, ensure you have Ansible installed and configured correctly, and your inventory file is set up with the target systems defined.
|
||||||
toolkit: nftables
|
|
||||||
```
|
### 4.2 Running the Playbook
|
||||||
|
|
||||||
## 4. Variable Reference
|
Execute the playbook using the `ansible-playbook` command, ensuring that all necessary variables are defined, typically by specifying a `vars.yml` file containing the required configurations.
|
||||||
|
|
||||||
### 4.1 Core Variables
|
### 4.3 Example Usage
|
||||||
|
|
||||||
Top-level variables outside `system`/`hypervisor`/`cis`.
|
An effective way to use the playbook involves defining all necessary configurations within a `vars.yml` file. This file should include all relevant global variables tailored to your specific deployment requirements. Additionally, you should prepare an inventory file (`inventory.yml`) that lists all the hosts along with any specific inventory variables they might need. Then, you can run the playbook as follows:
|
||||||
|
|
||||||
| 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
|
|
||||||
|
|
||||||
| 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 | -- | 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 list. When `interfaces` is set, it takes precedence. Each entry supports: `name`, `bridge` (required), `vlan`, `ip`, `prefix`, `gateway`.
|
|
||||||
|
|
||||||
#### `system.users`
|
|
||||||
|
|
||||||
Dict keyed by username. At least one user must have a `password` (used for SSH access during bootstrap). Users without a password get locked accounts (key-only auth).
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
system:
|
|
||||||
users:
|
|
||||||
svcansible:
|
|
||||||
password: "vault_lookup"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
appuser:
|
|
||||||
sudo: "ALL=(ALL) NOPASSWD: ALL"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 BBBB..."
|
|
||||||
```
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
| ---------- | ----------- | ------- | -------------------------------------------------- |
|
|
||||||
| *(dict key)* | string | -- | Username (required) |
|
|
||||||
| `password` | string | -- | User password (required for at least one user) |
|
|
||||||
| `keys` | list | `[]` | SSH public keys |
|
|
||||||
| `sudo` | bool/string | -- | `true` for NOPASSWD ALL, or custom sudoers string |
|
|
||||||
|
|
||||||
Users must be defined in inventory. The dict format enables additive merging across inventory layers with `hash_behaviour=merge`.
|
|
||||||
|
|
||||||
#### `system.root`
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
| ---------- | ------ | ------- | ------------- |
|
|
||||||
| `password` | string | -- | Root password |
|
|
||||||
|
|
||||||
#### `system.luks`
|
|
||||||
|
|
||||||
| 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`
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
| -------- | ------------- | ------- | ---------------------------------------------- |
|
|
||||||
| `device` | string | `auto` | TPM2 device selector |
|
|
||||||
| `pcrs` | string/list | -- | PCR binding policy (e.g. `"7"` or `"0+7"`); empty = no PCR binding |
|
|
||||||
|
|
||||||
**TPM2 auto-unlock:** Uses `systemd-cryptenroll` on all distros. The user-set passphrase
|
|
||||||
remains as a backup unlock method. TPM2 enrollment runs in the chroot during bootstrap;
|
|
||||||
if it fails (e.g. no TPM2 hardware), the system boots with passphrase-only unlock and
|
|
||||||
TPM2 can be enrolled post-deployment via `systemd-cryptenroll --tpm2-device=auto <device>`.
|
|
||||||
|
|
||||||
On Debian/Ubuntu, TPM2 auto-unlock requires dracut (initramfs-tools does not support `tpm2-device`).
|
|
||||||
The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `features.initramfs.generator`.
|
|
||||||
|
|
||||||
#### `system.features`
|
|
||||||
|
|
||||||
| 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` |
|
|
||||||
| `initramfs.generator` | string | auto-detected | Override initramfs generator (see below) |
|
|
||||||
| `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) |
|
|
||||||
|
|
||||||
**Initramfs generator auto-detection:** RedHat → dracut, Arch → mkinitcpio, Debian/Ubuntu → initramfs-tools.
|
|
||||||
Override with `dracut`, `mkinitcpio`, or `initramfs-tools`. When LUKS TPM2 auto-unlock is enabled and the
|
|
||||||
native generator does not support `tpm2-device`, the generator is automatically upgraded to dracut.
|
|
||||||
On distros with older dracut (no `tpm2-tss` module), clevis is used as a fallback for TPM2 binding.
|
|
||||||
|
|
||||||
#### 4.2.5 `system.features.desktop`
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
| ----------------- | ------ | -------------- | ----------------------------------------- |
|
|
||||||
| `enabled` | bool | `false` | Install desktop environment |
|
|
||||||
| `environment` | string | -- | `gnome`, `kde`, `xfce`, `sway`, `hyprland`, `cinnamon`, `mate`, `lxqt`, `budgie` |
|
|
||||||
| `display_manager` | string | auto-detected | Override DM: `gdm`, `sddm`, `lightdm`, `ly`, `greetd` |
|
|
||||||
|
|
||||||
When `enabled: true`, the bootstrap installs the desktop environment packages, enables the display manager
|
|
||||||
and bluetooth services, and sets the systemd default target to `graphical.target`.
|
|
||||||
|
|
||||||
Display manager auto-detection: gnome→gdm, kde→sddm, xfce→lightdm, sway→greetd, hyprland→ly.
|
|
||||||
|
|
||||||
### 4.3 `hypervisor` Dictionary
|
|
||||||
|
|
||||||
| 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 |
|
|
||||||
| `node` | string | -- | Target compute node (Proxmox node / VMware ESXi host; mutually exclusive with `cluster` on VMware) |
|
|
||||||
| `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 `cis` Dictionary
|
|
||||||
|
|
||||||
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 |
|
|
||||||
| ------------------------------- | -------------------------------------------- |
|
|
||||||
| `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 # OS disk
|
|
||||||
- size: 200 # Data disk
|
|
||||||
mount:
|
|
||||||
path: /data
|
|
||||||
fstype: xfs
|
|
||||||
label: DATA
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.7 Advanced Partitioning Overrides
|
|
||||||
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
**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.
|
|
||||||
|
|
||||||
**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).
|
|
||||||
|
|
||||||
### 4.8 Cleanup Defaults
|
|
||||||
|
|
||||||
Post-install verification and recovery settings.
|
|
||||||
|
|
||||||
| 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) |
|
|
||||||
|
|
||||||
## 5. Execution Pipeline
|
|
||||||
|
|
||||||
Roles execute in this order:
|
|
||||||
|
|
||||||
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 -e @vars.yml main.yml
|
||||||
ansible-playbook -i inventory.yml main.yml -e @vars.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
All credentials (`system.users`, `system.root.password`) must be defined in inventory or passed via `-e`.
|
This command prompts Ansible to execute the `main.yml` playbook, applying configurations defined in both `vars.yml` and the inventory file.
|
||||||
|
|
||||||
Example inventory files are included:
|
Use `inventory_example.yml`, `vars_example.yml`, and the bare-metal examples as starting points for new inventories.
|
||||||
|
|
||||||
- `inventory_example.yml` -- Proxmox virtual setup
|
## Notes
|
||||||
- `inventory_libvirt_example.yml` -- libvirt virtual setup
|
|
||||||
- `inventory_baremetal_example.yml` -- bare-metal physical setup
|
|
||||||
|
|
||||||
## 7. Security
|
- `vm_size`/`vm_memory` are required for virtual installs only, physical installs use the full disk.
|
||||||
|
- `vm_dns` and `vm_dns_search` accept comma-separated strings or YAML lists.
|
||||||
Use **Ansible Vault** for all sensitive values (`hypervisor.password`, `system.luks.passphrase`, user passwords in `system.users`, `system.root.password`).
|
- `hypervisor` determines which backend-specific roles run.
|
||||||
|
- Guest tools are installed based on `hypervisor`: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`, otherwise none.
|
||||||
## 8. Safety
|
- With `luks_auto_decrypt_method: tpm2` on virtual installs, the virtualization role enables a TPM2 device (libvirt/proxmox/vmware).
|
||||||
|
- For VMware, `vmware_ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks.
|
||||||
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.
|
- For physical installs, set `ansible_user`/`ansible_password` for the installer environment when it differs from the prompted user credentials.
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[defaults]
|
|
||||||
hash_behaviour = merge
|
|
||||||
interpreter_python = auto_silent
|
|
||||||
deprecation_warnings = False
|
|
||||||
host_key_checking = False
|
|
||||||
|
|
||||||
[ssh_connection]
|
|
||||||
ssh_args = -C -o ControlMaster=auto -o ControlPersist=600s -o ServerAliveInterval=30 -o ServerAliveCountMax=10
|
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
---
|
---
|
||||||
collections:
|
collections:
|
||||||
- name: ansible.posix
|
- name: ansible.posix
|
||||||
version: "2.1.0"
|
|
||||||
- name: community.general
|
- name: community.general
|
||||||
version: "12.3.0"
|
|
||||||
- name: community.libvirt
|
- name: community.libvirt
|
||||||
version: "2.0.0"
|
|
||||||
- name: community.crypto
|
- name: community.crypto
|
||||||
version: "3.1.0"
|
|
||||||
- name: community.proxmox
|
- name: community.proxmox
|
||||||
version: "1.5.0"
|
|
||||||
- name: community.vmware
|
- name: community.vmware
|
||||||
version: "6.2.0"
|
|
||||||
- name: vmware.vmware
|
- name: vmware.vmware
|
||||||
version: "2.7.0"
|
|
||||||
|
|||||||
@@ -1,20 +1,9 @@
|
|||||||
---
|
---
|
||||||
all:
|
all:
|
||||||
vars:
|
|
||||||
hypervisor:
|
|
||||||
type: "none"
|
|
||||||
system:
|
|
||||||
filesystem: "ext4"
|
|
||||||
hosts:
|
hosts:
|
||||||
baremetal01.example.com:
|
baremetal01.example.com:
|
||||||
ansible_host: 10.0.0.162
|
ansible_host: 10.0.0.162
|
||||||
ansible_user: root
|
ansible_user: root
|
||||||
ansible_password: "1234"
|
ansible_password: "1234"
|
||||||
ansible_become_password: "1234"
|
ansible_become_password: "1234"
|
||||||
system:
|
hostname: "baremetal01.example.com"
|
||||||
type: "physical"
|
|
||||||
os: "archlinux"
|
|
||||||
name: "baremetal01.example.com"
|
|
||||||
disks:
|
|
||||||
- device: "/dev/sda"
|
|
||||||
size: 120
|
|
||||||
|
|||||||
@@ -1,133 +1,40 @@
|
|||||||
---
|
---
|
||||||
all:
|
all:
|
||||||
vars:
|
vars:
|
||||||
hypervisor:
|
hypervisor: "proxmox"
|
||||||
type: "proxmox"
|
install_type: "virtual"
|
||||||
url: "pve01.example.com"
|
install_drive: "/dev/sda"
|
||||||
username: "root@pam"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
node: "pve01"
|
|
||||||
storage: "local-lvm"
|
|
||||||
boot_iso: "local:iso/archlinux-x86_64.iso"
|
boot_iso: "local:iso/archlinux-x86_64.iso"
|
||||||
|
vm_nif: "vmbr0"
|
||||||
|
vm_gw: "10.0.0.1"
|
||||||
|
vm_dns:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.0.0.1
|
||||||
|
vm_dns_search:
|
||||||
|
- example.com
|
||||||
children:
|
children:
|
||||||
proxmox:
|
proxmox:
|
||||||
hosts:
|
hosts:
|
||||||
app01.example.com:
|
proxy01.example.com:
|
||||||
ansible_host: 10.0.0.10
|
ansible_host: 10.0.0.10
|
||||||
system:
|
hostname: "proxy01.example.com"
|
||||||
filesystem: "btrfs"
|
vm_id: 100
|
||||||
type: "virtual"
|
os: "archlinux"
|
||||||
os: "archlinux"
|
filesystem: "btrfs"
|
||||||
name: "app01.example.com"
|
vm_memory: 4096
|
||||||
id: 100
|
vm_ballo: 2048
|
||||||
cpus: 2
|
vm_cpus: 2
|
||||||
memory: 4096
|
vm_size: 40
|
||||||
balloon: 0
|
vm_ip: 10.0.0.10
|
||||||
network:
|
database01.example.com:
|
||||||
bridge: "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: 80
|
|
||||||
mount:
|
|
||||||
path: /data
|
|
||||||
fstype: xfs
|
|
||||||
label: DATA
|
|
||||||
opts: defaults
|
|
||||||
users:
|
|
||||||
- name: "ops"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
root:
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
packages:
|
|
||||||
- jq
|
|
||||||
- tmux
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: false
|
|
||||||
selinux:
|
|
||||||
enabled: true
|
|
||||||
firewall:
|
|
||||||
enabled: true
|
|
||||||
backend: "firewalld"
|
|
||||||
toolkit: "nftables"
|
|
||||||
ssh:
|
|
||||||
enabled: true
|
|
||||||
zstd:
|
|
||||||
enabled: true
|
|
||||||
swap:
|
|
||||||
enabled: true
|
|
||||||
banner:
|
|
||||||
motd: true
|
|
||||||
sudo: true
|
|
||||||
chroot:
|
|
||||||
tool: "arch-chroot"
|
|
||||||
db01.example.com:
|
|
||||||
ansible_host: 10.0.0.11
|
ansible_host: 10.0.0.11
|
||||||
|
hostname: "database01.example.com"
|
||||||
|
vm_id: 101
|
||||||
|
os: "rhel9"
|
||||||
|
filesystem: "xfs"
|
||||||
|
vm_memory: 4096
|
||||||
|
vm_ballo: 2048
|
||||||
|
vm_cpus: 4
|
||||||
|
vm_size: 60
|
||||||
|
vm_ip: 10.0.0.11
|
||||||
rhel_iso: "local:iso/rhel-9.4-x86_64-dvd.iso"
|
rhel_iso: "local:iso/rhel-9.4-x86_64-dvd.iso"
|
||||||
system:
|
|
||||||
filesystem: "xfs"
|
|
||||||
type: "virtual"
|
|
||||||
os: "rhel"
|
|
||||||
version: "9"
|
|
||||||
name: "db01.example.com"
|
|
||||||
id: 101
|
|
||||||
cpus: 4
|
|
||||||
memory: 8192
|
|
||||||
network:
|
|
||||||
bridge: "vmbr0"
|
|
||||||
ip: 10.0.0.11
|
|
||||||
prefix: 24
|
|
||||||
gateway: 10.0.0.1
|
|
||||||
dns:
|
|
||||||
servers:
|
|
||||||
- "1.1.1.1"
|
|
||||||
- "1.0.0.1"
|
|
||||||
disks:
|
|
||||||
- size: 80
|
|
||||||
- size: 200
|
|
||||||
mount:
|
|
||||||
path: /srv/data
|
|
||||||
fstype: ext4
|
|
||||||
users:
|
|
||||||
- name: "dbadmin"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
root:
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
luks:
|
|
||||||
enabled: true
|
|
||||||
passphrase: "CHANGE_ME"
|
|
||||||
method: "keyfile"
|
|
||||||
keysize: 128
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: true
|
|
||||||
selinux:
|
|
||||||
enabled: false
|
|
||||||
firewall:
|
|
||||||
enabled: false
|
|
||||||
backend: "firewalld"
|
|
||||||
toolkit: "nftables"
|
|
||||||
ssh:
|
|
||||||
enabled: true
|
|
||||||
zstd:
|
|
||||||
enabled: true
|
|
||||||
swap:
|
|
||||||
enabled: true
|
|
||||||
banner:
|
|
||||||
motd: true
|
|
||||||
sudo: true
|
|
||||||
chroot:
|
|
||||||
tool: "arch-chroot"
|
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
---
|
|
||||||
all:
|
|
||||||
vars:
|
|
||||||
hypervisor:
|
|
||||||
type: "libvirt"
|
|
||||||
url: "localhost"
|
|
||||||
username: ""
|
|
||||||
password: ""
|
|
||||||
host: ""
|
|
||||||
storage: "default"
|
|
||||||
boot_iso: "/var/lib/libvirt/images/archlinux-x86_64.iso"
|
|
||||||
children:
|
|
||||||
libvirt:
|
|
||||||
hosts:
|
|
||||||
web01.local:
|
|
||||||
ansible_host: 192.168.122.20
|
|
||||||
system:
|
|
||||||
filesystem: "ext4"
|
|
||||||
type: "virtual"
|
|
||||||
os: "debian"
|
|
||||||
version: "12"
|
|
||||||
name: "web01.local"
|
|
||||||
cpus: 2
|
|
||||||
memory: 2048
|
|
||||||
network:
|
|
||||||
bridge: "default"
|
|
||||||
ip: 192.168.122.20
|
|
||||||
prefix: 24
|
|
||||||
gateway: 192.168.122.1
|
|
||||||
dns:
|
|
||||||
servers:
|
|
||||||
- 1.1.1.1
|
|
||||||
search:
|
|
||||||
- lab.local
|
|
||||||
path: "/var/lib/libvirt/images"
|
|
||||||
disks:
|
|
||||||
- size: 30
|
|
||||||
- size: 80
|
|
||||||
mount:
|
|
||||||
path: /var/www
|
|
||||||
fstype: xfs
|
|
||||||
users:
|
|
||||||
- name: "web"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
root:
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
packages:
|
|
||||||
- nginx
|
|
||||||
- curl
|
|
||||||
features:
|
|
||||||
firewall:
|
|
||||||
enabled: true
|
|
||||||
backend: "ufw"
|
|
||||||
toolkit: "nftables"
|
|
||||||
db01.local:
|
|
||||||
ansible_host: 192.168.122.21
|
|
||||||
rhel_iso: "/var/lib/libvirt/images/rhel-9.4-x86_64-dvd.iso"
|
|
||||||
system:
|
|
||||||
filesystem: "xfs"
|
|
||||||
type: "virtual"
|
|
||||||
os: "rhel"
|
|
||||||
version: "9"
|
|
||||||
name: "db01.local"
|
|
||||||
cpus: 4
|
|
||||||
memory: 4096
|
|
||||||
network:
|
|
||||||
bridge: "default"
|
|
||||||
ip: 192.168.122.21
|
|
||||||
prefix: 24
|
|
||||||
gateway: 192.168.122.1
|
|
||||||
dns:
|
|
||||||
servers:
|
|
||||||
- 9.9.9.9
|
|
||||||
search:
|
|
||||||
- example.com
|
|
||||||
disks:
|
|
||||||
- size: 60
|
|
||||||
- size: 120
|
|
||||||
mount:
|
|
||||||
path: /data
|
|
||||||
fstype: ext4
|
|
||||||
users:
|
|
||||||
- name: "db"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
root:
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
luks:
|
|
||||||
enabled: true
|
|
||||||
passphrase: "CHANGE_ME"
|
|
||||||
method: "keyfile"
|
|
||||||
features:
|
|
||||||
firewall:
|
|
||||||
enabled: false
|
|
||||||
backend: "firewalld"
|
|
||||||
toolkit: "nftables"
|
|
||||||
compute01.local:
|
|
||||||
ansible_host: 192.168.122.22
|
|
||||||
system:
|
|
||||||
filesystem: "btrfs"
|
|
||||||
type: "virtual"
|
|
||||||
os: "fedora"
|
|
||||||
version: "41"
|
|
||||||
name: "compute01.local"
|
|
||||||
cpus: 8
|
|
||||||
memory: 8192
|
|
||||||
network:
|
|
||||||
bridge: "default"
|
|
||||||
ip: 192.168.122.22
|
|
||||||
prefix: 24
|
|
||||||
gateway: 192.168.122.1
|
|
||||||
dns:
|
|
||||||
servers:
|
|
||||||
- "1.1.1.1"
|
|
||||||
- "1.0.0.1"
|
|
||||||
disks:
|
|
||||||
- size: 80
|
|
||||||
- size: 200
|
|
||||||
mount:
|
|
||||||
path: /data
|
|
||||||
fstype: btrfs
|
|
||||||
users:
|
|
||||||
- name: "compute"
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
keys:
|
|
||||||
- "ssh-ed25519 AAAA..."
|
|
||||||
root:
|
|
||||||
password: "CHANGE_ME"
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: true
|
|
||||||
246
main.yml
246
main.yml
@@ -1,101 +1,120 @@
|
|||||||
---
|
---
|
||||||
# Bootstrap pipeline — role execution order:
|
|
||||||
# 1. global_defaults — normalize + validate system/hypervisor/disk input
|
|
||||||
# 2. system_check — pre-flight hardware/environment safety checks
|
|
||||||
# 3. virtualization — create VM on hypervisor (libvirt/proxmox/vmware/xen)
|
|
||||||
# 4. environment — detect live ISO, configure installer network, install tools
|
|
||||||
# 5. partitioning — partition disk, create FS, LUKS, LVM, mount everything
|
|
||||||
# 6. bootstrap — debootstrap/pacstrap/dnf install the target OS into /mnt
|
|
||||||
# 7. configuration — users, network, encryption, fstab, bootloader, services
|
|
||||||
# 8. cis — CIS hardening (optional, per system.features.cis.enabled)
|
|
||||||
# 9. cleanup — unmount, remove cloud-init artifacts, reboot/shutdown
|
|
||||||
- name: Create and configure VMs
|
- name: Create and configure VMs
|
||||||
hosts: "{{ bootstrap_target | default('all') }}"
|
hosts: all
|
||||||
strategy: free # noqa: run-once[play]
|
strategy: free # noqa: run-once[play]
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
become: true
|
become: true
|
||||||
|
vars_prompt:
|
||||||
|
- name: user_name
|
||||||
|
prompt: |
|
||||||
|
What is your username?
|
||||||
|
private: false
|
||||||
|
|
||||||
|
- name: user_public_key
|
||||||
|
prompt: |
|
||||||
|
What is your ssh key?
|
||||||
|
private: false
|
||||||
|
|
||||||
|
- name: user_password
|
||||||
|
prompt: |
|
||||||
|
What is your password?
|
||||||
|
confirm: true
|
||||||
|
|
||||||
|
- name: root_password
|
||||||
|
prompt: |
|
||||||
|
What is your root password?
|
||||||
|
confirm: true
|
||||||
|
vars_files: vars.yml
|
||||||
pre_tasks:
|
pre_tasks:
|
||||||
- name: Load global defaults
|
- name: Validate variables
|
||||||
ansible.builtin.import_role:
|
ansible.builtin.assert:
|
||||||
name: global_defaults
|
that:
|
||||||
|
- install_type in ["virtual", "physical"]
|
||||||
|
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
|
||||||
|
- filesystem in ["btrfs", "ext4", "xfs"]
|
||||||
|
- install_drive is defined
|
||||||
|
- install_type == "physical" or vm_size is defined
|
||||||
|
- install_type == "physical" or vm_memory is defined
|
||||||
|
- os in ["archlinux", "almalinux", "debian11", "debian12", "debian13", "fedora", "rhel8", "rhel9", "rhel10", "rocky", "ubuntu", "ubuntu-lts"]
|
||||||
|
- os not in ["rhel8", "rhel9", "rhel10"] or rhel_iso is defined
|
||||||
|
- >-
|
||||||
|
install_type == "physical"
|
||||||
|
or (
|
||||||
|
(filesystem == "btrfs" and (vm_size | default(0) | int) >= 10)
|
||||||
|
or (filesystem != "btrfs" and (vm_size | default(0) | int) >= 20)
|
||||||
|
)
|
||||||
|
- >-
|
||||||
|
install_type == "physical"
|
||||||
|
or (
|
||||||
|
(vm_size | default(0) | float)
|
||||||
|
>= (
|
||||||
|
(vm_memory | default(0) | float / 1024 >= 16.0)
|
||||||
|
| ternary(
|
||||||
|
(vm_memory | default(0) | float / 2048),
|
||||||
|
[vm_memory | default(0) | float / 1024, 4.0] | max
|
||||||
|
)
|
||||||
|
+ 16
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fail_msg: Invalid input specified, please try again.
|
||||||
|
|
||||||
- name: Perform safety checks
|
- name: Normalize optional flags
|
||||||
ansible.builtin.import_role:
|
ansible.builtin.set_fact:
|
||||||
name: system_check
|
cis: "{{ cis | default(false) | bool }}"
|
||||||
|
custom_iso: "{{ custom_iso | default(false) | bool }}"
|
||||||
|
is_rhel: "{{ os | default('') | lower in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rhel10', 'rocky'] }}"
|
||||||
|
is_debian: "{{ os | default('') | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts'] }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
tasks:
|
- name: Set Python interpreter for RHEL-based installers
|
||||||
- name: Bootstrap pipeline
|
when:
|
||||||
block:
|
- ansible_python_interpreter is not defined
|
||||||
- name: Record that no pre-existing VM was found
|
- os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"]
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
_vm_absent_before_bootstrap: true
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Create virtual machine
|
- name: Set SSH access
|
||||||
when: system_cfg.type == "virtual"
|
when:
|
||||||
ansible.builtin.include_role:
|
- install_type == "virtual"
|
||||||
name: virtualization
|
- hypervisor != "vmware"
|
||||||
public: true
|
ansible.builtin.set_fact:
|
||||||
vars:
|
ansible_user: "{{ user_name }}"
|
||||||
ansible_connection: local
|
ansible_password: "{{ user_password }}"
|
||||||
ansible_become: false
|
ansible_become_password: "{{ user_password }}"
|
||||||
|
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||||
|
|
||||||
- name: Configure environment
|
- name: Set connection for VMware
|
||||||
ansible.builtin.include_role:
|
when: hypervisor == "vmware"
|
||||||
name: environment
|
ansible.builtin.set_fact:
|
||||||
public: true
|
ansible_connection: vmware_tools
|
||||||
|
|
||||||
- name: Partition disks
|
roles:
|
||||||
ansible.builtin.include_role:
|
- role: virtualization
|
||||||
name: partitioning
|
when: install_type == "virtual"
|
||||||
public: true
|
become: false
|
||||||
vars:
|
vars:
|
||||||
partitioning_boot_partition_suffix: 1
|
ansible_connection: local
|
||||||
partitioning_main_partition_suffix: 2
|
|
||||||
|
|
||||||
- name: Install base system
|
- role: environment
|
||||||
ansible.builtin.include_role:
|
vars:
|
||||||
name: bootstrap
|
ansible_connection: "{{ 'vmware_tools' if hypervisor == 'vmware' else 'ssh' }}"
|
||||||
public: true
|
|
||||||
|
|
||||||
- name: Apply system configuration
|
- role: partitioning
|
||||||
ansible.builtin.include_role:
|
vars:
|
||||||
name: configuration
|
partitioning_boot_partition_suffix: 1
|
||||||
public: true
|
partitioning_main_partition_suffix: 2
|
||||||
|
|
||||||
- name: Apply CIS hardening
|
- role: bootstrap
|
||||||
when: system_cfg.features.cis.enabled | bool
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: cis
|
|
||||||
public: true
|
|
||||||
|
|
||||||
- name: Clean up and finalize
|
- role: configuration
|
||||||
when: system_cfg.type in ["virtual", "physical"]
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: cleanup
|
|
||||||
public: true
|
|
||||||
|
|
||||||
rescue:
|
- role: cis
|
||||||
- name: Delete VM on bootstrap failure
|
when: cis | default(false) | bool
|
||||||
when:
|
|
||||||
- _vm_absent_before_bootstrap | default(false) | bool
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
- system_cfg.type == "virtual"
|
|
||||||
ansible.builtin.include_role:
|
|
||||||
name: virtualization
|
|
||||||
tasks_from: delete
|
|
||||||
vars:
|
|
||||||
ansible_connection: local
|
|
||||||
ansible_become: false
|
|
||||||
tags:
|
|
||||||
- rescue_cleanup
|
|
||||||
|
|
||||||
- name: Fail host after bootstrap rescue
|
- role: cleanup
|
||||||
ansible.builtin.fail:
|
when: install_type in ["virtual", "physical"]
|
||||||
msg: >-
|
become: false
|
||||||
Bootstrap failed for {{ hostname }}.
|
|
||||||
{{ 'VM was deleted to allow clean retry.'
|
|
||||||
if (virtualization_vm_created_in_run | default(false))
|
|
||||||
else 'VM was not created in this run (kept).' }}
|
|
||||||
|
|
||||||
post_tasks:
|
post_tasks:
|
||||||
- name: Set post-reboot connection flags
|
- name: Set post-reboot connection flags
|
||||||
@@ -103,48 +122,45 @@
|
|||||||
post_reboot_can_connect: >-
|
post_reboot_can_connect: >-
|
||||||
{{
|
{{
|
||||||
(ansible_connection | default('ssh')) != 'ssh'
|
(ansible_connection | default('ssh')) != 'ssh'
|
||||||
or ((system_cfg.network.ip | default('') | string | length) > 0)
|
or ((vm_ip | default('') | string | length) > 0)
|
||||||
or (
|
or (
|
||||||
system_cfg.type == 'physical'
|
install_type == 'physical'
|
||||||
and (ansible_host | default('') | string | length) > 0
|
and (ansible_host | default('') | string | length) > 0
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
changed_when: false
|
||||||
- name: Reset SSH connection before post-reboot tasks
|
|
||||||
when:
|
|
||||||
- post_reboot_can_connect | bool
|
|
||||||
ansible.builtin.meta: reset_connection
|
|
||||||
|
|
||||||
- name: Set final SSH credentials for post-reboot tasks
|
- name: Set final SSH credentials for post-reboot tasks
|
||||||
when:
|
when:
|
||||||
- post_reboot_can_connect | bool
|
- post_reboot_can_connect | default(false) | bool
|
||||||
no_log: true
|
|
||||||
vars:
|
|
||||||
_primary: "{{ (system_cfg.users | dict2items | selectattr('value.password', 'defined') | first) }}"
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
ansible_connection: ssh
|
ansible_user: "{{ user_name }}"
|
||||||
ansible_host: "{{ system_cfg.network.ip }}"
|
ansible_password: "{{ user_password }}"
|
||||||
ansible_port: 22
|
ansible_become_password: "{{ user_password }}"
|
||||||
ansible_user: "{{ _primary.key }}"
|
|
||||||
ansible_password: "{{ _primary.value.password }}"
|
|
||||||
ansible_become_password: "{{ _primary.value.password }}"
|
|
||||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||||
ansible_python_interpreter: /usr/bin/python3
|
|
||||||
|
|
||||||
- name: Re-gather facts for target OS after reboot
|
- name: Install post-reboot extra packages
|
||||||
when:
|
when:
|
||||||
- post_reboot_can_connect | bool
|
- extra_packages is defined
|
||||||
ansible.builtin.setup:
|
- post_reboot_can_connect | default(false) | bool
|
||||||
gather_subset:
|
block:
|
||||||
- "!all"
|
- name: Normalize extra package list
|
||||||
- min
|
ansible.builtin.set_fact:
|
||||||
- pkg_mgr
|
post_install_extra_packages: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
extra_packages
|
||||||
|
if (extra_packages is iterable and extra_packages is not string)
|
||||||
|
else (extra_packages | default('') | string).split(',')
|
||||||
|
)
|
||||||
|
| map('trim')
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Install post-reboot packages
|
- name: Install extra packages
|
||||||
when:
|
when: post_install_extra_packages | length > 0
|
||||||
- post_reboot_can_connect | bool
|
ansible.builtin.package:
|
||||||
- system_cfg.packages is defined
|
name: "{{ post_install_extra_packages }}"
|
||||||
- system_cfg.packages | length > 0
|
state: present
|
||||||
ansible.builtin.package:
|
|
||||||
name: "{{ system_cfg.packages }}"
|
|
||||||
state: present
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Molecule converge placeholder
|
|
||||||
hosts: all
|
|
||||||
gather_facts: false
|
|
||||||
tasks:
|
|
||||||
- name: Skip destructive provisioning in Molecule
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: "Molecule scenario is lint-only; run main.yml against disposable hosts."
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
dependency:
|
|
||||||
name: galaxy
|
|
||||||
driver:
|
|
||||||
name: delegated
|
|
||||||
platforms:
|
|
||||||
- name: localhost
|
|
||||||
provisioner:
|
|
||||||
name: ansible
|
|
||||||
playbooks:
|
|
||||||
converge: converge.yml
|
|
||||||
inventory:
|
|
||||||
host_vars:
|
|
||||||
localhost:
|
|
||||||
ansible_connection: local
|
|
||||||
lint:
|
|
||||||
name: ansible-lint
|
|
||||||
verifier:
|
|
||||||
name: ansible
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Molecule verify placeholder
|
|
||||||
hosts: all
|
|
||||||
gather_facts: false
|
|
||||||
tasks:
|
|
||||||
- name: Verify placeholder
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- true
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
# OS → task file mapping for bootstrap dispatch.
|
|
||||||
# Each key matches a supported `os` value; value is the task file to include.
|
|
||||||
bootstrap_os_task_map:
|
|
||||||
almalinux: _dnf_family.yml
|
|
||||||
alpine: alpine.yml
|
|
||||||
archlinux: archlinux.yml
|
|
||||||
debian: debian.yml
|
|
||||||
fedora: _dnf_family.yml
|
|
||||||
opensuse: opensuse.yml
|
|
||||||
rocky: _dnf_family.yml
|
|
||||||
rhel: rhel.yml
|
|
||||||
ubuntu: ubuntu.yml
|
|
||||||
ubuntu-lts: ubuntu.yml
|
|
||||||
void: void.yml
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Load desktop package definitions
|
|
||||||
ansible.builtin.include_vars:
|
|
||||||
file: desktop.yml
|
|
||||||
|
|
||||||
- name: Resolve desktop packages
|
|
||||||
vars:
|
|
||||||
_de: "{{ system_cfg.features.desktop.environment }}"
|
|
||||||
_family_pkgs: "{{ bootstrap_desktop_packages[os_family] | default({}) }}"
|
|
||||||
_de_config: "{{ _family_pkgs[_de] | default({}) }}"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_desktop_groups: "{{ _de_config.groups | default([]) }}"
|
|
||||||
_desktop_packages: "{{ _de_config.packages | default([]) }}"
|
|
||||||
|
|
||||||
- name: Validate desktop environment is supported
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- (_desktop_groups | length > 0) or (_desktop_packages | length > 0)
|
|
||||||
fail_msg: >-
|
|
||||||
Desktop environment '{{ system_cfg.features.desktop.environment }}'
|
|
||||||
is not defined for os_family '{{ os_family }}'.
|
|
||||||
Supported: {{ (bootstrap_desktop_packages[os_family] | default({})).keys() | join(', ') }}
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Install desktop package groups
|
|
||||||
when: _desktop_groups | length > 0
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }}
|
|
||||||
--setopt=install_weak_deps=False group install -y {{ _desktop_groups | join(' ') }}
|
|
||||||
register: _desktop_group_result
|
|
||||||
changed_when: _desktop_group_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install desktop packages
|
|
||||||
when: _desktop_packages | length > 0
|
|
||||||
vars:
|
|
||||||
_install_commands:
|
|
||||||
RedHat: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }}
|
|
||||||
--setopt=install_weak_deps=False install -y {{ _desktop_packages | join(' ') }}
|
|
||||||
Debian: >-
|
|
||||||
{{ chroot_command }} apt install -y {{ _desktop_packages | join(' ') }}
|
|
||||||
Archlinux: >-
|
|
||||||
pacstrap /mnt {{ _desktop_packages | join(' ') }}
|
|
||||||
Suse: >-
|
|
||||||
{{ chroot_command }} zypper install -y {{ _desktop_packages | join(' ') }}
|
|
||||||
ansible.builtin.command: "{{ _install_commands[os_family] }}"
|
|
||||||
register: _desktop_pkg_result
|
|
||||||
changed_when: _desktop_pkg_result.rc == 0
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "Bootstrap {{ os | capitalize }}"
|
|
||||||
vars:
|
|
||||||
_dnf_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
_dnf_repos: "{{ _dnf_config.repos | map('regex_replace', '^', '--repo=') | join(' ') }}"
|
|
||||||
_dnf_groups: "{{ _dnf_config.base | join(' ') }}"
|
|
||||||
_dnf_extra: >-
|
|
||||||
{{
|
|
||||||
((_dnf_config.extra | default([])) + (_dnf_config.conditional | default([])))
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: "Install base system for {{ os | capitalize }}"
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
dnf --releasever={{ os_version }} --best {{ _dnf_repos }}
|
|
||||||
--installroot=/mnt --setopt=install_weak_deps=False
|
|
||||||
groupinstall -y {{ _dnf_groups }}
|
|
||||||
register: bootstrap_dnf_base_result
|
|
||||||
changed_when: bootstrap_dnf_base_result.rc == 0
|
|
||||||
failed_when:
|
|
||||||
- bootstrap_dnf_base_result.rc != 0
|
|
||||||
- "'scriptlet' not in bootstrap_dnf_base_result.stderr"
|
|
||||||
|
|
||||||
- name: Ensure chroot has DNS resolution
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
|
||||||
install -y {{ _dnf_extra }}
|
|
||||||
register: bootstrap_dnf_extra_result
|
|
||||||
changed_when: bootstrap_dnf_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Detect installed kernel package name
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} rpm -q kernel-core"
|
|
||||||
register: bootstrap_dnf_kernel_check
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Reinstall kernel package
|
|
||||||
vars:
|
|
||||||
_kernel_pkg: "{{ 'kernel-core' if bootstrap_dnf_kernel_check.rc == 0 else 'kernel' }}"
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y {{ _kernel_pkg }}"
|
|
||||||
register: bootstrap_dnf_kernel_result
|
|
||||||
changed_when: bootstrap_dnf_kernel_result.rc == 0
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
# Resolve the OS-specific variable namespace and task file for the bootstrap role.
|
|
||||||
- name: Validate OS is supported for bootstrap
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- os is defined
|
|
||||||
- os in bootstrap_os_task_map
|
|
||||||
fail_msg: >-
|
|
||||||
Unsupported OS '{{ os | default("undefined") }}' for bootstrap.
|
|
||||||
Supported: {{ bootstrap_os_task_map | dict2items | map(attribute='key') | join(', ') }}
|
|
||||||
quiet: true
|
|
||||||
20
roles/bootstrap/tasks/almalinux.yml
Normal file
20
roles/bootstrap/tasks/almalinux.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap AlmaLinux 9
|
||||||
|
vars:
|
||||||
|
bootstrap_alma_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- >-
|
||||||
|
dnf --releasever=9 --best --repo=alma-baseos --installroot=/mnt
|
||||||
|
--setopt=install_weak_deps=False groupinstall -y base core
|
||||||
|
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||||
|
- >-
|
||||||
|
arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_alma_extra }}
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap Alpine Linux
|
|
||||||
vars:
|
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
_base_packages: "{{ _config.base | join(' ') }}"
|
|
||||||
_extra_packages: >-
|
|
||||||
{{
|
|
||||||
((_config.extra | default([])) + (_config.conditional | default([])))
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install Alpine Linux base
|
|
||||||
ansible.builtin.command: >
|
|
||||||
apk --root /mnt --no-cache add {{ _base_packages }}
|
|
||||||
register: bootstrap_alpine_bootstrap_result
|
|
||||||
changed_when: bootstrap_alpine_bootstrap_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
when: _extra_packages | trim | length > 0
|
|
||||||
ansible.builtin.command: >
|
|
||||||
apk --root /mnt add {{ _extra_packages }}
|
|
||||||
register: bootstrap_alpine_extra_result
|
|
||||||
changed_when: bootstrap_alpine_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install bootloader
|
|
||||||
ansible.builtin.command: >
|
|
||||||
apk --root /mnt add grub grub-efi efibootmgr
|
|
||||||
register: bootstrap_alpine_bootloader_result
|
|
||||||
changed_when: bootstrap_alpine_bootloader_result.rc == 0
|
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
---
|
---
|
||||||
- name: Bootstrap ArchLinux
|
- name: Bootstrap ArchLinux
|
||||||
vars:
|
vars:
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
bootstrap_archlinux_packages: >-
|
bootstrap_archlinux_packages: >-
|
||||||
{{
|
{{
|
||||||
((_config.base | default([])) + (_config.conditional | default([])))
|
lookup('vars', bootstrap_var_key)
|
||||||
| reject('equalto', '')
|
|
||||||
| list
|
|
||||||
}}
|
}}
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
pacstrap /mnt {{ bootstrap_archlinux_packages | join(' ') }}
|
pacstrap /mnt {{ bootstrap_archlinux_packages | reject('equalto', '') | join(' ') }} --asexplicit
|
||||||
register: bootstrap_result
|
register: bootstrap_result
|
||||||
changed_when: bootstrap_result.rc == 0
|
changed_when: bootstrap_result.rc == 0
|
||||||
|
|||||||
@@ -3,70 +3,27 @@
|
|||||||
vars:
|
vars:
|
||||||
bootstrap_debian_release: >-
|
bootstrap_debian_release: >-
|
||||||
{{
|
{{
|
||||||
'buster' if (os_version | string) == '10'
|
'bullseye' if bootstrap_os_key == 'debian11'
|
||||||
else 'bullseye' if (os_version | string) == '11'
|
else 'bookworm' if bootstrap_os_key == 'debian12'
|
||||||
else 'bookworm' if (os_version | string) == '12'
|
|
||||||
else 'trixie' if (os_version | string) == '13'
|
|
||||||
else 'sid' if (os_version | string) == 'unstable'
|
|
||||||
else 'trixie'
|
else 'trixie'
|
||||||
}}
|
}}
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
bootstrap_debian_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}"
|
||||||
bootstrap_debian_base_csv: "{{ (['ca-certificates'] + _config.base) | unique | join(',') }}"
|
bootstrap_debian_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
|
||||||
bootstrap_debian_extra_args: >-
|
bootstrap_debian_base: "{{ bootstrap_debian_base_list | reject('equalto', '') | join(',') }}"
|
||||||
|
bootstrap_debian_extra: >-
|
||||||
{{
|
{{
|
||||||
((_config.extra | default([])) + (_config.conditional | default([])))
|
(
|
||||||
|
bootstrap_debian_extra_list
|
||||||
|
)
|
||||||
| reject('equalto', '')
|
| reject('equalto', '')
|
||||||
| join(' ')
|
| join(' ')
|
||||||
}}
|
}}
|
||||||
block:
|
ansible.builtin.command: "{{ item }}"
|
||||||
- name: Validate Debian package configuration
|
loop:
|
||||||
ansible.builtin.assert:
|
- >-
|
||||||
that:
|
debootstrap --include={{ bootstrap_debian_base }}
|
||||||
- _config is mapping
|
{{ bootstrap_debian_release }} /mnt http://deb.debian.org/debian/
|
||||||
- _config.base is sequence
|
- "arch-chroot /mnt apt install -y {{ bootstrap_debian_extra }}"
|
||||||
- _config.extra is sequence
|
- arch-chroot /mnt apt remove -y libcups2 libavahi-common3 libavahi-common-data
|
||||||
fail_msg: "{{ bootstrap_var_key }} must be a dict with base/extra/conditional keys."
|
register: bootstrap_result
|
||||||
quiet: true
|
changed_when: bootstrap_result.rc == 0
|
||||||
|
|
||||||
- name: Install Debian base system
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
debootstrap --include={{ bootstrap_debian_base_csv }}
|
|
||||||
{{ bootstrap_debian_release }} /mnt {{ system_cfg.mirror }}
|
|
||||||
register: bootstrap_debian_base_result
|
|
||||||
changed_when: bootstrap_debian_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Write bootstrap sources.list
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: debian.sources.list.j2
|
|
||||||
dest: /mnt/etc/apt/sources.list
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Configure apt performance tuning
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/apt/apt.conf.d/99performance
|
|
||||||
content: |
|
|
||||||
Acquire::Retries "3";
|
|
||||||
Acquire::http::Pipeline-Depth "10";
|
|
||||||
APT::Install-Recommends "false";
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Update package lists
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt update"
|
|
||||||
register: bootstrap_debian_update_result
|
|
||||||
changed_when: bootstrap_debian_update_result.rc == 0
|
|
||||||
|
|
||||||
- name: Upgrade all packages to latest versions
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt full-upgrade -y"
|
|
||||||
register: bootstrap_debian_upgrade_result
|
|
||||||
changed_when: "'0 upgraded' not in bootstrap_debian_upgrade_result.stdout"
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
when: bootstrap_debian_extra_args | trim | length > 0
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_debian_extra_args }}"
|
|
||||||
register: bootstrap_debian_extra_result
|
|
||||||
changed_when: bootstrap_debian_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Remove unnecessary packages
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt remove -y libcups2 libavahi-common3 libavahi-common-data"
|
|
||||||
register: bootstrap_debian_remove_result
|
|
||||||
changed_when: bootstrap_debian_remove_result.rc == 0
|
|
||||||
|
|||||||
22
roles/bootstrap/tasks/fedora.yml
Normal file
22
roles/bootstrap/tasks/fedora.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Fedora 42
|
||||||
|
vars:
|
||||||
|
bootstrap_fedora_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- >-
|
||||||
|
dnf --releasever=42 --best --repo=fedora --repo=fedora-updates
|
||||||
|
--installroot=/mnt --setopt=install_weak_deps=False
|
||||||
|
groupinstall -y critical-path-base core
|
||||||
|
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||||
|
- >-
|
||||||
|
arch-chroot /mnt dnf --releasever=42 --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_fedora_extra }}
|
||||||
|
- arch-chroot /mnt dnf reinstall -y kernel-core
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
@@ -1,46 +1,33 @@
|
|||||||
---
|
---
|
||||||
- name: Validate bootstrap input
|
|
||||||
ansible.builtin.import_tasks: _validate.yml
|
|
||||||
|
|
||||||
- name: Create API filesystem mountpoints in installroot
|
|
||||||
when: os_family == 'RedHat'
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "/mnt/{{ item }}"
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
loop:
|
|
||||||
- dev
|
|
||||||
- proc
|
|
||||||
- sys
|
|
||||||
|
|
||||||
- name: Mount API filesystems into installroot
|
|
||||||
when: os_family == 'RedHat'
|
|
||||||
ansible.posix.mount:
|
|
||||||
src: "{{ item.src }}"
|
|
||||||
path: "/mnt/{{ item.path }}"
|
|
||||||
fstype: "{{ item.fstype }}"
|
|
||||||
opts: "{{ item.opts | default(omit) }}"
|
|
||||||
state: ephemeral
|
|
||||||
loop:
|
|
||||||
- { src: proc, path: proc, fstype: proc }
|
|
||||||
- { src: sysfs, path: sys, fstype: sysfs }
|
|
||||||
- { src: /dev, path: dev, fstype: none, opts: bind }
|
|
||||||
- { src: devpts, path: dev/pts, fstype: devpts, opts: "gid=5,mode=620" }
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path }}"
|
|
||||||
|
|
||||||
- name: Run OS-specific bootstrap process
|
- name: Run OS-specific bootstrap process
|
||||||
vars:
|
vars:
|
||||||
bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}"
|
bootstrap_os_key: "{{ os | lower }}"
|
||||||
ansible.builtin.include_tasks: "{{ bootstrap_os_task_map[os] }}"
|
bootstrap_var_key: "{{ 'bootstrap_' + (os | lower | replace('-', '_')) }}"
|
||||||
|
block:
|
||||||
|
- name: Include AlmaLinux bootstrap tasks
|
||||||
|
when: bootstrap_os_key == 'almalinux'
|
||||||
|
ansible.builtin.include_tasks: almalinux.yml
|
||||||
|
|
||||||
- name: Install desktop environment packages
|
- name: Include ArchLinux bootstrap tasks
|
||||||
when: system_cfg.features.desktop.enabled | bool
|
when: bootstrap_os_key == 'archlinux'
|
||||||
ansible.builtin.include_tasks: _desktop.yml
|
ansible.builtin.include_tasks: archlinux.yml
|
||||||
|
|
||||||
- name: Ensure chroot uses live environment DNS
|
- name: Include Debian bootstrap tasks
|
||||||
ansible.builtin.file:
|
when: bootstrap_os_key in ['debian11', 'debian12', 'debian13']
|
||||||
src: /run/NetworkManager/resolv.conf
|
ansible.builtin.include_tasks: debian.yml
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
- name: Include Fedora bootstrap tasks
|
||||||
force: true
|
when: bootstrap_os_key == 'fedora'
|
||||||
|
ansible.builtin.include_tasks: fedora.yml
|
||||||
|
|
||||||
|
- name: Include Rocky bootstrap tasks
|
||||||
|
when: bootstrap_os_key == 'rocky'
|
||||||
|
ansible.builtin.include_tasks: rocky.yml
|
||||||
|
|
||||||
|
- name: Include RHEL bootstrap tasks
|
||||||
|
when: bootstrap_os_key in ['rhel8', 'rhel9', 'rhel10']
|
||||||
|
ansible.builtin.include_tasks: rhel.yml
|
||||||
|
|
||||||
|
- name: Include Ubuntu bootstrap tasks
|
||||||
|
when: bootstrap_os_key in ['ubuntu', 'ubuntu-lts']
|
||||||
|
ansible.builtin.include_tasks: ubuntu.yml
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap openSUSE
|
|
||||||
vars:
|
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
_base_patterns: "{{ _config.base | join(' ') }}"
|
|
||||||
_extra_packages: >-
|
|
||||||
{{
|
|
||||||
((_config.extra | default([])) + (_config.conditional | default([])))
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install openSUSE base patterns
|
|
||||||
ansible.builtin.command: >
|
|
||||||
zypper --root /mnt --non-interactive install -t pattern {{ _base_patterns }}
|
|
||||||
register: bootstrap_opensuse_base_result
|
|
||||||
changed_when: bootstrap_opensuse_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
when: _extra_packages | trim | length > 0
|
|
||||||
ansible.builtin.command: >
|
|
||||||
zypper --root /mnt --non-interactive install {{ _extra_packages }}
|
|
||||||
register: bootstrap_opensuse_extra_result
|
|
||||||
changed_when: bootstrap_opensuse_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install bootloader
|
|
||||||
ansible.builtin.command: >
|
|
||||||
zypper --root /mnt --non-interactive install grub2 grub2-efi efibootmgr
|
|
||||||
register: bootstrap_opensuse_bootloader_result
|
|
||||||
changed_when: bootstrap_opensuse_bootloader_result.rc == 0
|
|
||||||
@@ -1,27 +1,23 @@
|
|||||||
---
|
---
|
||||||
- name: Bootstrap RHEL System
|
- name: Bootstrap RHEL System
|
||||||
vars:
|
|
||||||
_rhel_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
_rhel_repos: "{{ _rhel_config.repos | map('regex_replace', '^', '--repo=') | join(' ') }}"
|
|
||||||
_rhel_groups: "{{ _rhel_config.base | join(' ') }}"
|
|
||||||
_rhel_extra: >-
|
|
||||||
{{
|
|
||||||
((_rhel_config.extra | default([])) + (_rhel_config.conditional | default([])))
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
block:
|
||||||
- name: Install base packages in chroot environment
|
- name: Install base packages in chroot environment
|
||||||
|
vars:
|
||||||
|
bootstrap_rhel_release: "{{ bootstrap_os_key | replace('rhel', '') }}"
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
dnf --releasever={{ os_version_major }} --best {{ _rhel_repos }}
|
dnf --releasever={{ bootstrap_rhel_release }} --repo={{ bootstrap_os_key }}-baseos
|
||||||
--installroot=/mnt
|
--installroot=/mnt
|
||||||
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
||||||
groupinstall -y {{ _rhel_groups }}
|
groupinstall -y core base standard
|
||||||
register: bootstrap_result
|
register: bootstrap_result
|
||||||
changed_when: bootstrap_result.rc == 0
|
changed_when: bootstrap_result.rc == 0
|
||||||
failed_when:
|
|
||||||
- bootstrap_result.rc != 0
|
- name: Ensure chroot has resolv.conf
|
||||||
- "'grub2-common' not in (bootstrap_result.stderr | default(''))"
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
- name: Ensure chroot RHEL DVD directory exists
|
- name: Ensure chroot RHEL DVD directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -35,23 +31,36 @@
|
|||||||
path: /mnt/usr/local/install/redhat/dvd
|
path: /mnt/usr/local/install/redhat/dvd
|
||||||
fstype: none
|
fstype: none
|
||||||
opts: bind
|
opts: bind
|
||||||
state: ephemeral
|
state: mounted
|
||||||
|
|
||||||
- name: Rebuild RPM database inside chroot
|
- name: Rebuild RPM database inside chroot
|
||||||
ansible.builtin.command: "{{ chroot_command }} rpm --rebuilddb"
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- arch-chroot
|
||||||
|
- /mnt
|
||||||
|
- rpm
|
||||||
|
- --rebuilddb
|
||||||
register: bootstrap_rpm_rebuild_result
|
register: bootstrap_rpm_rebuild_result
|
||||||
changed_when: bootstrap_rpm_rebuild_result.rc == 0
|
changed_when: bootstrap_rpm_rebuild_result.rc == 0
|
||||||
|
|
||||||
- name: Copy RHEL repo file into chroot environment
|
- name: Copy RHEL repo file into chroot environment
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: /etc/yum.repos.d/rhel.repo
|
src: /etc/yum.repos.d/{{ bootstrap_os_key }}.repo
|
||||||
dest: /mnt/etc/yum.repos.d/redhat.repo
|
dest: /mnt/etc/yum.repos.d/redhat.repo
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
|
|
||||||
- name: Install additional packages in chroot
|
- name: Install additional packages in chroot
|
||||||
|
vars:
|
||||||
|
bootstrap_rhel_release: "{{ bootstrap_os_key | replace('rhel', '') }}"
|
||||||
|
bootstrap_rhel_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version_major }} --best
|
arch-chroot /mnt dnf --releasever={{ bootstrap_rhel_release }}
|
||||||
--setopt=install_weak_deps=False install -y {{ _rhel_extra }}
|
--setopt=install_weak_deps=False install -y {{ bootstrap_rhel_extra }}
|
||||||
register: bootstrap_result
|
register: bootstrap_result
|
||||||
changed_when: bootstrap_result.rc == 0
|
changed_when: bootstrap_result.rc == 0
|
||||||
|
|||||||
21
roles/bootstrap/tasks/rocky.yml
Normal file
21
roles/bootstrap/tasks/rocky.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap RockyLinux 9
|
||||||
|
vars:
|
||||||
|
bootstrap_rocky_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- >-
|
||||||
|
dnf --releasever=9 --best --repo=rocky-baseos --installroot=/mnt
|
||||||
|
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
||||||
|
groupinstall -y base core
|
||||||
|
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||||
|
- >-
|
||||||
|
arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_rocky_extra }}
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
@@ -1,66 +1,27 @@
|
|||||||
---
|
---
|
||||||
- name: Bootstrap Ubuntu System
|
- name: Bootstrap Ubuntu System
|
||||||
vars:
|
vars:
|
||||||
# ubuntu = latest non-LTS, ubuntu-lts = latest LTS
|
bootstrap_ubuntu_release: >-
|
||||||
bootstrap_ubuntu_release_map:
|
{{ 'plucky' if bootstrap_os_key == 'ubuntu' else 'noble' }}
|
||||||
ubuntu: questing
|
bootstrap_ubuntu_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}"
|
||||||
ubuntu-lts: noble
|
bootstrap_ubuntu_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
|
||||||
bootstrap_ubuntu_release: "{{ bootstrap_ubuntu_release_map[os] | default('noble') }}"
|
bootstrap_ubuntu_base: "{{ bootstrap_ubuntu_base_list | reject('equalto', '') | join(',') }}"
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
bootstrap_ubuntu_extra: >-
|
||||||
bootstrap_ubuntu_base_csv: "{{ (['ca-certificates'] + _config.base) | unique | join(',') }}"
|
|
||||||
bootstrap_ubuntu_extra_args: >-
|
|
||||||
{{
|
{{
|
||||||
((_config.extra | default([])) + (_config.conditional | default([])))
|
(
|
||||||
|
bootstrap_ubuntu_extra_list
|
||||||
|
)
|
||||||
| reject('equalto', '')
|
| reject('equalto', '')
|
||||||
| join(' ')
|
| join(' ')
|
||||||
}}
|
}}
|
||||||
block:
|
ansible.builtin.command: "{{ item }}"
|
||||||
- name: Validate Ubuntu package configuration
|
loop:
|
||||||
ansible.builtin.assert:
|
- >-
|
||||||
that:
|
debootstrap --include={{ bootstrap_ubuntu_base }}
|
||||||
- _config is mapping
|
{{ bootstrap_ubuntu_release }} /mnt http://archive.ubuntu.com/ubuntu/
|
||||||
- _config.base is sequence
|
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||||
- _config.extra is sequence
|
- arch-chroot /mnt sed -i '1s|$| universe|' /etc/apt/sources.list
|
||||||
fail_msg: "{{ bootstrap_var_key }} must be a dict with base/extra/conditional keys."
|
- arch-chroot /mnt apt update
|
||||||
quiet: true
|
- "arch-chroot /mnt apt install -y {{ bootstrap_ubuntu_extra }}"
|
||||||
|
register: bootstrap_result
|
||||||
- name: Install Ubuntu base system
|
changed_when: bootstrap_result.rc == 0
|
||||||
ansible.builtin.command: >-
|
|
||||||
debootstrap
|
|
||||||
--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg
|
|
||||||
--include={{ bootstrap_ubuntu_base_csv }}
|
|
||||||
{{ bootstrap_ubuntu_release }} /mnt
|
|
||||||
{{ system_cfg.mirror }}
|
|
||||||
register: bootstrap_ubuntu_base_result
|
|
||||||
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Write bootstrap sources.list
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: ubuntu.sources.list.j2
|
|
||||||
dest: /mnt/etc/apt/sources.list
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Configure apt performance tuning
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/apt/apt.conf.d/99performance
|
|
||||||
content: |
|
|
||||||
Acquire::Retries "3";
|
|
||||||
Acquire::http::Pipeline-Depth "10";
|
|
||||||
APT::Install-Recommends "false";
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Update package lists
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt update"
|
|
||||||
register: bootstrap_ubuntu_update_result
|
|
||||||
changed_when: bootstrap_ubuntu_update_result.rc == 0
|
|
||||||
|
|
||||||
- name: Upgrade all packages to latest versions
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt full-upgrade -y"
|
|
||||||
register: bootstrap_ubuntu_upgrade_result
|
|
||||||
changed_when: "'0 upgraded' not in bootstrap_ubuntu_upgrade_result.stdout"
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
when: bootstrap_ubuntu_extra_args | trim | length > 0
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_ubuntu_extra_args }}"
|
|
||||||
register: bootstrap_ubuntu_extra_result
|
|
||||||
changed_when: bootstrap_ubuntu_extra_result.rc == 0
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Bootstrap Void Linux
|
|
||||||
vars:
|
|
||||||
_config: "{{ lookup('vars', bootstrap_var_key) }}"
|
|
||||||
_base_packages: "{{ _config.base | join(' ') }}"
|
|
||||||
_extra_packages: >-
|
|
||||||
{{
|
|
||||||
((_config.extra | default([])) + (_config.conditional | default([])))
|
|
||||||
| reject('equalto', '')
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Install Void Linux base
|
|
||||||
ansible.builtin.command: >
|
|
||||||
xbps-install -Sy -r /mnt -R https://repo-default.voidlinux.org/current {{ _base_packages }}
|
|
||||||
register: bootstrap_void_base_result
|
|
||||||
changed_when: bootstrap_void_base_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install extra packages
|
|
||||||
when: _extra_packages | trim | length > 0
|
|
||||||
ansible.builtin.command: >
|
|
||||||
xbps-install -Su -r /mnt {{ _extra_packages }}
|
|
||||||
register: bootstrap_void_extra_result
|
|
||||||
changed_when: bootstrap_void_extra_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install bootloader
|
|
||||||
ansible.builtin.command: >
|
|
||||||
xbps-install -Sy -r /mnt grub-x86_64-efi efibootmgr
|
|
||||||
register: bootstrap_void_bootloader_result
|
|
||||||
changed_when: bootstrap_void_bootloader_result.rc == 0
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Managed by Ansible.
|
|
||||||
{% set release = bootstrap_debian_release %}
|
|
||||||
{% set mirror = system_cfg.mirror %}
|
|
||||||
{% set components = 'main contrib non-free' ~ (' non-free-firmware' if (os_version | string) not in ['10', '11'] else '') %}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }} {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }} {{ components }}
|
|
||||||
{% if release != 'sid' %}
|
|
||||||
|
|
||||||
deb https://security.debian.org/debian-security {{ release }}-security {{ components }}
|
|
||||||
deb-src https://security.debian.org/debian-security {{ release }}-security {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Managed by Ansible.
|
|
||||||
{% set release = bootstrap_ubuntu_release %}
|
|
||||||
{% set mirror = system_cfg.mirror %}
|
|
||||||
{% set components = 'main restricted universe multiverse' %}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }} {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }} {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-security {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-security {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-backports {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-backports {{ components }}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
---
|
|
||||||
# Per-family desktop environment package definitions.
|
|
||||||
# Keyed by os_family -> environment -> groups (dnf groupinstall) / packages.
|
|
||||||
# Kept intentionally minimal: base DE + essential tools, no full suites.
|
|
||||||
bootstrap_desktop_packages:
|
|
||||||
RedHat:
|
|
||||||
gnome:
|
|
||||||
groups:
|
|
||||||
- workstation-product-environment
|
|
||||||
packages: []
|
|
||||||
kde:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- plasma-desktop
|
|
||||||
- plasma-nm
|
|
||||||
- plasma-pa
|
|
||||||
- plasma-systemmonitor
|
|
||||||
- sddm
|
|
||||||
- konsole
|
|
||||||
- dolphin
|
|
||||||
- kate
|
|
||||||
- kscreen
|
|
||||||
- kde-gtk-config
|
|
||||||
- xdg-user-dirs
|
|
||||||
- xdg-desktop-portal-kde
|
|
||||||
- bluez
|
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups:
|
|
||||||
- xfce-desktop-environment
|
|
||||||
packages:
|
|
||||||
- lightdm
|
|
||||||
Debian:
|
|
||||||
gnome:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- gnome-core
|
|
||||||
- gdm3
|
|
||||||
- gnome-tweaks
|
|
||||||
- xdg-user-dirs
|
|
||||||
kde:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- plasma-desktop
|
|
||||||
- plasma-nm
|
|
||||||
- plasma-pa
|
|
||||||
- sddm
|
|
||||||
- konsole
|
|
||||||
- dolphin
|
|
||||||
- kate
|
|
||||||
- kscreen
|
|
||||||
- xdg-user-dirs
|
|
||||||
- xdg-desktop-portal-kde
|
|
||||||
- bluez
|
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- xfce4
|
|
||||||
- xfce4-goodies
|
|
||||||
- lightdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
Archlinux:
|
|
||||||
gnome:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- gnome
|
|
||||||
- gdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
kde:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- plasma-desktop
|
|
||||||
- plasma-nm
|
|
||||||
- plasma-pa
|
|
||||||
- sddm
|
|
||||||
- konsole
|
|
||||||
- dolphin
|
|
||||||
- kate
|
|
||||||
- kscreen
|
|
||||||
- kde-gtk-config
|
|
||||||
- xdg-user-dirs
|
|
||||||
- xdg-desktop-portal-kde
|
|
||||||
- bluez
|
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
xfce:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- xfce4
|
|
||||||
- xfce4-goodies
|
|
||||||
- lightdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
sway:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- sway
|
|
||||||
- waybar
|
|
||||||
- foot
|
|
||||||
- wofi
|
|
||||||
- greetd
|
|
||||||
- xdg-user-dirs
|
|
||||||
- xdg-desktop-portal-wlr
|
|
||||||
- bluez
|
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
hyprland:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- hyprland
|
|
||||||
- kitty
|
|
||||||
- wofi
|
|
||||||
- waybar
|
|
||||||
- ly
|
|
||||||
- xdg-user-dirs
|
|
||||||
- xdg-desktop-portal-hyprland
|
|
||||||
- polkit-kde-agent
|
|
||||||
- qt5-wayland
|
|
||||||
- qt6-wayland
|
|
||||||
- bluez
|
|
||||||
- pipewire
|
|
||||||
- wireplumber
|
|
||||||
Suse:
|
|
||||||
gnome:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- patterns-gnome-gnome_basic
|
|
||||||
- gdm
|
|
||||||
- xdg-user-dirs
|
|
||||||
kde:
|
|
||||||
groups: []
|
|
||||||
packages:
|
|
||||||
- patterns-kde-kde_plasma
|
|
||||||
- sddm
|
|
||||||
- xdg-user-dirs
|
|
||||||
|
|
||||||
# Display manager auto-detection from desktop environment.
|
|
||||||
bootstrap_desktop_dm_map:
|
|
||||||
gnome: gdm
|
|
||||||
kde: sddm
|
|
||||||
xfce: lightdm
|
|
||||||
sway: greetd
|
|
||||||
hyprland: ly@tty2
|
|
||||||
cinnamon: lightdm
|
|
||||||
mate: lightdm
|
|
||||||
lxqt: sddm
|
|
||||||
budgie: gdm
|
|
||||||
@@ -1,248 +1,387 @@
|
|||||||
---
|
---
|
||||||
# Feature-gated packages shared across all distros.
|
|
||||||
# Arch has special nftables handling and composes this differently.
|
|
||||||
bootstrap_common_conditional: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(['firewalld'] if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ (['iptables'] if system_cfg.features.firewall.toolkit == 'iptables' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ (['nftables'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ (['cryptsetup', 'tpm2-tools'] if system_cfg.luks.enabled | bool else [])
|
|
||||||
+ (['qemu-guest-agent'] if hypervisor_type in ['libvirt', 'proxmox'] else [])
|
|
||||||
+ (['open-vm-tools'] if hypervisor_type == 'vmware' else [])
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Per-OS package definitions: base (rootfs/group install), extra (post-base),
|
|
||||||
# conditional (feature/version-gated, appended by task files).
|
|
||||||
# DNF-based distros also carry repos (dnf --repo) and use base as group names.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
bootstrap_rhel:
|
|
||||||
repos:
|
|
||||||
- "rhel{{ os_version_major }}-baseos"
|
|
||||||
base:
|
|
||||||
- core
|
|
||||||
- base
|
|
||||||
- standard
|
|
||||||
extra:
|
|
||||||
- bind-utils
|
|
||||||
- efibootmgr
|
|
||||||
- glibc-langpack-de
|
|
||||||
- glibc-langpack-en
|
|
||||||
- grub2
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- mtr
|
|
||||||
- ncurses-term
|
|
||||||
- nfs-utils
|
|
||||||
- policycoreutils-python-utils
|
|
||||||
- shim
|
|
||||||
- tmux
|
|
||||||
- vim
|
|
||||||
- zstd
|
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['grub2-efi-x64'] if os_version_major | default('') == '8' else ['grub2-efi'])
|
|
||||||
+ (['grub2-tools-extra'] if os_version_major | default('') in ['8', '9'] else [])
|
|
||||||
+ (['dhcp-client'] if (os_version_major | default('9') | int) < 10 else [])
|
|
||||||
+ (['python39'] if os_version_major | default('') == '8' else ['python'])
|
|
||||||
+ (['kernel'] if os_version_major | default('') == '10' else [])
|
|
||||||
+ (['zram-generator'] if os_version_major | default('') in ['9', '10'] else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_almalinux:
|
bootstrap_almalinux:
|
||||||
repos:
|
- bind-utils
|
||||||
- baseos
|
- dbus-daemon
|
||||||
- appstream
|
- dhcp-client
|
||||||
base:
|
- efibootmgr
|
||||||
- core
|
- glibc-langpack-de
|
||||||
extra:
|
- glibc-langpack-en
|
||||||
- bind-utils
|
- grub2
|
||||||
- efibootmgr
|
- grub2-efi
|
||||||
- glibc-langpack-de
|
- lrzsz
|
||||||
- glibc-langpack-en
|
- lvm2
|
||||||
- grub2
|
- nc
|
||||||
- grub2-efi
|
- nfs-utils
|
||||||
- kernel
|
- nfsv4-client-utils
|
||||||
- lrzsz
|
- mtr
|
||||||
- lvm2
|
- ppp
|
||||||
- mtr
|
- shim
|
||||||
- nc
|
- tmux
|
||||||
- ncurses-term
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
- nfs-utils
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
- nfsv4-client-utils
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
- policycoreutils-python-utils
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
- ppp
|
- vim
|
||||||
- python3
|
- wget
|
||||||
- shim
|
- zram-generator
|
||||||
- tmux
|
- zstd
|
||||||
- vim
|
|
||||||
- zram-generator
|
|
||||||
- zstd
|
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['dbus-daemon'] if (os_version_major | default('10') | int) >= 9 else [])
|
|
||||||
+ (['dhcp-client'] if (os_version_major | default('10') | int) < 10 else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_rocky:
|
bootstrap_archlinux:
|
||||||
repos:
|
- base
|
||||||
- baseos
|
- btrfs-progs
|
||||||
- appstream
|
- cronie
|
||||||
base:
|
- dhcpcd
|
||||||
- core
|
- efibootmgr
|
||||||
extra:
|
- fastfetch
|
||||||
- bind-utils
|
- firewalld
|
||||||
- efibootmgr
|
- fish
|
||||||
- glibc-langpack-de
|
- fzf
|
||||||
- glibc-langpack-en
|
- grub
|
||||||
- grub2
|
- htop
|
||||||
- grub2-efi
|
- libpwquality
|
||||||
- kernel
|
- linux
|
||||||
- lrzsz
|
- logrotate
|
||||||
- lvm2
|
- lrzsz
|
||||||
- mtr
|
- lsof
|
||||||
- nc
|
- lvm2
|
||||||
- ncurses-term
|
- ncdu
|
||||||
- nfs-utils
|
- networkmanager
|
||||||
- nfsv4-client-utils
|
- nfs-utils
|
||||||
- policycoreutils-python-utils
|
- openssh
|
||||||
- ppp
|
- ppp
|
||||||
- python3
|
- prometheus-node-exporter
|
||||||
- shim
|
- python-psycopg2
|
||||||
- telnet
|
- reflector
|
||||||
- tmux
|
- rsync
|
||||||
- util-linux-core
|
- sudo
|
||||||
- vim
|
- tldr
|
||||||
- wget
|
- tmux
|
||||||
- zram-generator
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
- zstd
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
conditional: >-
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
{{
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
(['dhcp-client'] if (os_version_major | default('9') | int) < 10 else [])
|
- vim
|
||||||
+ bootstrap_common_conditional
|
- wireguard-tools
|
||||||
}}
|
- zram-generator
|
||||||
|
|
||||||
bootstrap_fedora:
|
bootstrap_debian11:
|
||||||
repos:
|
|
||||||
- fedora
|
|
||||||
- fedora-updates
|
|
||||||
base:
|
|
||||||
- critical-path-base
|
|
||||||
- core
|
|
||||||
extra:
|
|
||||||
- bat
|
|
||||||
- bind-utils
|
|
||||||
- btrfs-progs
|
|
||||||
- cronie
|
|
||||||
- dhcp-client
|
|
||||||
- duf
|
|
||||||
- efibootmgr
|
|
||||||
- entr
|
|
||||||
- fish
|
|
||||||
- fzf
|
|
||||||
- glibc-langpack-de
|
|
||||||
- glibc-langpack-en
|
|
||||||
- grub2
|
|
||||||
- grub2-efi
|
|
||||||
- htop
|
|
||||||
- iperf3
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nc
|
|
||||||
- nfs-utils
|
|
||||||
- nfsv4-client-utils
|
|
||||||
- polkit
|
|
||||||
- ppp
|
|
||||||
- python3
|
|
||||||
- ripgrep
|
|
||||||
- shim
|
|
||||||
- tmux
|
|
||||||
- vim-default-editor
|
|
||||||
- wget
|
|
||||||
- zoxide
|
|
||||||
- zram-generator
|
|
||||||
- zstd
|
|
||||||
conditional: "{{ bootstrap_common_conditional }}"
|
|
||||||
|
|
||||||
bootstrap_debian:
|
|
||||||
base:
|
base:
|
||||||
|
- apparmor-utils
|
||||||
- btrfs-progs
|
- btrfs-progs
|
||||||
|
- chrony
|
||||||
- cron
|
- cron
|
||||||
- cryptsetup-initramfs
|
|
||||||
- gnupg
|
- gnupg
|
||||||
- grub-efi
|
- grub-efi
|
||||||
- grub-efi-amd64-signed
|
- grub-efi-amd64-signed
|
||||||
- grub2-common
|
- grub2-common
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'cryptsetup-initramfs' if luks_enabled | default(false) else '' }}"
|
||||||
|
- linux-image-amd64
|
||||||
- locales
|
- locales
|
||||||
- logrotate
|
- logrotate
|
||||||
- lvm2
|
- lvm2
|
||||||
|
- net-tools
|
||||||
- openssh-server
|
- openssh-server
|
||||||
- python3
|
- python3
|
||||||
|
- sudo
|
||||||
- xfsprogs
|
- xfsprogs
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
- apparmor-utils
|
|
||||||
- bat
|
- bat
|
||||||
- chrony
|
|
||||||
- curl
|
- curl
|
||||||
- entr
|
- entr
|
||||||
|
- firewalld
|
||||||
- fish
|
- fish
|
||||||
- fzf
|
- fzf
|
||||||
- htop
|
- htop
|
||||||
- jq
|
- jq
|
||||||
- libpam-pwquality
|
- libpam-pwquality
|
||||||
- linux-image-amd64
|
|
||||||
- lrzsz
|
- lrzsz
|
||||||
- mtr
|
- mtr
|
||||||
- ncdu
|
- ncdu
|
||||||
- needrestart
|
- neofetch
|
||||||
- net-tools
|
|
||||||
- network-manager
|
- network-manager
|
||||||
- python-is-python3
|
- python-is-python3
|
||||||
- ripgrep
|
- ripgrep
|
||||||
- rsync
|
- rsync
|
||||||
- screen
|
- screen
|
||||||
- sudo
|
- software-properties-common
|
||||||
- syslog-ng
|
- syslog-ng
|
||||||
- tcpd
|
- tcpd
|
||||||
|
- tldr
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
- vim
|
- vim
|
||||||
- wget
|
- wget
|
||||||
- zstd
|
- zstd
|
||||||
conditional: >-
|
|
||||||
{{
|
bootstrap_debian12:
|
||||||
(['duf'] if (os_version | string) not in ['10', '11'] else [])
|
base:
|
||||||
+ (['fastfetch'] if (os_version | string) in ['13', 'unstable'] else [])
|
- btrfs-progs
|
||||||
+ (['neofetch'] if (os_version | string) == '12' else [])
|
- cron
|
||||||
+ (['software-properties-common'] if (os_version | string) not in ['13', 'unstable'] else [])
|
- gnupg
|
||||||
+ (['systemd-zram-generator'] if (os_version | string) not in ['10', '11'] else [])
|
- grub-efi
|
||||||
+ (['tldr'] if (os_version | string) not in ['13', 'unstable'] else [])
|
- grub-efi-amd64-signed
|
||||||
+ (['shim-signed'] if system_cfg.features.secure_boot.enabled | bool else [])
|
- grub2-common
|
||||||
+ bootstrap_common_conditional
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
}}
|
- "{{ 'cryptsetup-initramfs' if luks_enabled | default(false) else '' }}"
|
||||||
|
- linux-image-amd64
|
||||||
|
- locales
|
||||||
|
- logrotate
|
||||||
|
- lvm2
|
||||||
|
- xfsprogs
|
||||||
|
|
||||||
|
extra:
|
||||||
|
- apparmor-utils
|
||||||
|
- bat
|
||||||
|
- chrony
|
||||||
|
- curl
|
||||||
|
- duf
|
||||||
|
- entr
|
||||||
|
- firewalld
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- htop
|
||||||
|
- jq
|
||||||
|
- libpam-pwquality
|
||||||
|
- logrotate
|
||||||
|
- lrzsz
|
||||||
|
- mtr
|
||||||
|
- ncdu
|
||||||
|
- neofetch
|
||||||
|
- net-tools
|
||||||
|
- network-manager
|
||||||
|
- openssh-server
|
||||||
|
- python-is-python3
|
||||||
|
- python3
|
||||||
|
- ripgrep
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- software-properties-common
|
||||||
|
- sudo
|
||||||
|
- syslog-ng
|
||||||
|
- systemd-zram-generator
|
||||||
|
- tcpd
|
||||||
|
- tldr
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- wget
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_debian13:
|
||||||
|
base:
|
||||||
|
- btrfs-progs
|
||||||
|
- cron
|
||||||
|
- gnupg
|
||||||
|
- grub-efi
|
||||||
|
- grub-efi-amd64-signed
|
||||||
|
- grub2-common
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'cryptsetup-initramfs' if luks_enabled | default(false) else '' }}"
|
||||||
|
- linux-image-amd64
|
||||||
|
- locales
|
||||||
|
- logrotate
|
||||||
|
- lvm2
|
||||||
|
- xfsprogs
|
||||||
|
|
||||||
|
extra:
|
||||||
|
- apparmor-utils
|
||||||
|
- bat
|
||||||
|
- chrony
|
||||||
|
- curl
|
||||||
|
- duf
|
||||||
|
- entr
|
||||||
|
- fastfetch
|
||||||
|
- firewalld
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- htop
|
||||||
|
- jq
|
||||||
|
- libpam-pwquality
|
||||||
|
- logrotate
|
||||||
|
- lrzsz
|
||||||
|
- mtr
|
||||||
|
- ncdu
|
||||||
|
- net-tools
|
||||||
|
- network-manager
|
||||||
|
- openssh-server
|
||||||
|
- python-is-python3
|
||||||
|
- python3
|
||||||
|
- ripgrep
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- sudo
|
||||||
|
- syslog-ng
|
||||||
|
- systemd-zram-generator
|
||||||
|
- tcpd
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- wget
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_fedora:
|
||||||
|
- bat
|
||||||
|
- bind-utils
|
||||||
|
- btrfs-progs
|
||||||
|
- cronie
|
||||||
|
- dhcp-client
|
||||||
|
- duf
|
||||||
|
- efibootmgr
|
||||||
|
- entr
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- grub2
|
||||||
|
- grub2-efi
|
||||||
|
- htop
|
||||||
|
- iperf3
|
||||||
|
- logrotate
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- nc
|
||||||
|
- nfs-utils
|
||||||
|
- nfsv4-client-utils
|
||||||
|
- polkit
|
||||||
|
- ppp
|
||||||
|
- ripgrep
|
||||||
|
- shim
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim-default-editor
|
||||||
|
- wget
|
||||||
|
- zoxide
|
||||||
|
- zram-generator
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_rhel8:
|
||||||
|
- bind-utils
|
||||||
|
- dhcp-client
|
||||||
|
- efibootmgr
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- grub2
|
||||||
|
- grub2-efi-x64
|
||||||
|
- grub2-tools-extra
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- mtr
|
||||||
|
- ncurses-term
|
||||||
|
- nfs-utils
|
||||||
|
- policycoreutils-python-utils
|
||||||
|
- python39
|
||||||
|
- shim
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_rhel9:
|
||||||
|
- bind-utils
|
||||||
|
- dhcp-client
|
||||||
|
- efibootmgr
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- grub2
|
||||||
|
- grub2-efi
|
||||||
|
- grub2-tools-extra
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- mtr
|
||||||
|
- ncurses-term
|
||||||
|
- nfs-utils
|
||||||
|
- policycoreutils-python-utils
|
||||||
|
- python
|
||||||
|
- shim
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- zram-generator
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_rhel10:
|
||||||
|
- bind-utils
|
||||||
|
- efibootmgr
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- grub2
|
||||||
|
- grub2-efi
|
||||||
|
- kernel
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- mtr
|
||||||
|
- ncurses-term
|
||||||
|
- nfs-utils
|
||||||
|
- policycoreutils-python-utils
|
||||||
|
- python
|
||||||
|
- shim
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- zram-generator
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_rocky:
|
||||||
|
- bind-utils
|
||||||
|
- dbus-daemon
|
||||||
|
- dhcp-client
|
||||||
|
- efibootmgr
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- grub2
|
||||||
|
- grub2-efi
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- mtr
|
||||||
|
- nc
|
||||||
|
- nfs-utils
|
||||||
|
- nfsv4-client-utils
|
||||||
|
- ppp
|
||||||
|
- shim
|
||||||
|
- telnet
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- util-linux-core
|
||||||
|
- vim
|
||||||
|
- wget
|
||||||
|
- zram-generator
|
||||||
|
- zstd
|
||||||
|
|
||||||
bootstrap_ubuntu:
|
bootstrap_ubuntu:
|
||||||
base:
|
base:
|
||||||
- btrfs-progs
|
- btrfs-progs
|
||||||
- cron
|
- cron
|
||||||
- cryptsetup-initramfs
|
|
||||||
- gnupg
|
- gnupg
|
||||||
- grub-efi
|
- grub-efi
|
||||||
- grub-efi-amd64-signed
|
- grub-efi-amd64-signed
|
||||||
- grub2-common
|
- grub2-common
|
||||||
- initramfs-tools
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'cryptsetup-initramfs' if luks_enabled | default(false) else '' }}"
|
||||||
- linux-image-generic
|
- linux-image-generic
|
||||||
- locales
|
- locales
|
||||||
- logrotate
|
|
||||||
- lvm2
|
- lvm2
|
||||||
- openssh-server
|
|
||||||
- python3
|
|
||||||
- xfsprogs
|
- xfsprogs
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
- apparmor-utils
|
- apparmor-utils
|
||||||
- bash-completion
|
- bash-completion
|
||||||
@@ -255,19 +394,21 @@ bootstrap_ubuntu:
|
|||||||
- eza
|
- eza
|
||||||
- fdupes
|
- fdupes
|
||||||
- fio
|
- fio
|
||||||
|
- firewalld
|
||||||
- fish
|
- fish
|
||||||
- fzf
|
|
||||||
- htop
|
- htop
|
||||||
- jq
|
- jq
|
||||||
- libpam-pwquality
|
- libpam-pwquality
|
||||||
|
- logrotate
|
||||||
- lrzsz
|
- lrzsz
|
||||||
- mtr
|
- mtr
|
||||||
- ncdu
|
- ncdu
|
||||||
- ncurses-term
|
- ncurses-term
|
||||||
- needrestart
|
|
||||||
- net-tools
|
- net-tools
|
||||||
- network-manager
|
- network-manager
|
||||||
|
- openssh-server
|
||||||
- python-is-python3
|
- python-is-python3
|
||||||
|
- python3
|
||||||
- ripgrep
|
- ripgrep
|
||||||
- rsync
|
- rsync
|
||||||
- screen
|
- screen
|
||||||
@@ -276,6 +417,11 @@ bootstrap_ubuntu:
|
|||||||
- syslog-ng
|
- syslog-ng
|
||||||
- systemd-zram-generator
|
- systemd-zram-generator
|
||||||
- tcpd
|
- tcpd
|
||||||
|
- tldr
|
||||||
|
- tmux
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
- traceroute
|
- traceroute
|
||||||
- util-linux-extra
|
- util-linux-extra
|
||||||
- vim
|
- vim
|
||||||
@@ -283,118 +429,66 @@ bootstrap_ubuntu:
|
|||||||
- yq
|
- yq
|
||||||
- zoxide
|
- zoxide
|
||||||
- zstd
|
- zstd
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['tldr'] if (os_version | default('') | string | length) > 0 else [])
|
|
||||||
+ (['shim-signed'] if system_cfg.features.secure_boot.enabled | bool else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_archlinux:
|
bootstrap_ubuntu_lts:
|
||||||
base:
|
base:
|
||||||
- base
|
|
||||||
- btrfs-progs
|
- btrfs-progs
|
||||||
- cronie
|
- cron
|
||||||
- dhcpcd
|
- gnupg
|
||||||
- efibootmgr
|
- grub-efi
|
||||||
- fastfetch
|
- grub-efi-amd64-signed
|
||||||
|
- grub2-common
|
||||||
|
- "{{ 'cryptsetup' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'cryptsetup-initramfs' if luks_enabled | default(false) else '' }}"
|
||||||
|
- linux-image-generic
|
||||||
|
- locales
|
||||||
|
- lvm2
|
||||||
|
- xfsprogs
|
||||||
|
|
||||||
|
extra:
|
||||||
|
- apparmor-utils
|
||||||
|
- bash-completion
|
||||||
|
- bat
|
||||||
|
- chrony
|
||||||
|
- curl
|
||||||
|
- dnsutils
|
||||||
|
- duf
|
||||||
|
- entr
|
||||||
|
- eza
|
||||||
|
- fdupes
|
||||||
|
- fio
|
||||||
|
- firewalld
|
||||||
- fish
|
- fish
|
||||||
- fzf
|
|
||||||
- grub
|
|
||||||
- htop
|
- htop
|
||||||
- libpwquality
|
- jq
|
||||||
- linux
|
- libpam-pwquality
|
||||||
- logrotate
|
- logrotate
|
||||||
- lrzsz
|
- lrzsz
|
||||||
- lsof
|
- mtr
|
||||||
- lvm2
|
|
||||||
- ncdu
|
- ncdu
|
||||||
- networkmanager
|
- ncurses-term
|
||||||
- nfs-utils
|
- net-tools
|
||||||
- ppp
|
- network-manager
|
||||||
- python
|
- openssh-server
|
||||||
- reflector
|
- python-is-python3
|
||||||
|
- python3
|
||||||
|
- ripgrep
|
||||||
- rsync
|
- rsync
|
||||||
|
- screen
|
||||||
|
- software-properties-common
|
||||||
- sudo
|
- sudo
|
||||||
|
- syslog-ng
|
||||||
|
- systemd-zram-generator
|
||||||
|
- tcpd
|
||||||
- tldr
|
- tldr
|
||||||
- tmux
|
- tmux
|
||||||
|
- "{{ 'tpm2-tools' if luks_enabled | default(false) else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor | default('none') | lower in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor | default('none') | lower == 'vmware' else '' }}"
|
||||||
|
- traceroute
|
||||||
|
- util-linux-extra
|
||||||
- vim
|
- vim
|
||||||
- zram-generator
|
- wget
|
||||||
extra: []
|
- yq
|
||||||
conditional: >-
|
- zoxide
|
||||||
{{
|
- zstd
|
||||||
(['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ (['iptables-nft'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ (['sbctl'] if system_cfg.features.secure_boot.enabled | bool else [])
|
|
||||||
+ (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_alpine:
|
|
||||||
base:
|
|
||||||
- alpine-base
|
|
||||||
extra:
|
|
||||||
- btrfs-progs
|
|
||||||
- chrony
|
|
||||||
- curl
|
|
||||||
- e2fsprogs
|
|
||||||
- linux-lts
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- sudo
|
|
||||||
- util-linux
|
|
||||||
- vim
|
|
||||||
- xfsprogs
|
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_opensuse:
|
|
||||||
base:
|
|
||||||
- patterns-base-base
|
|
||||||
extra:
|
|
||||||
- btrfs-progs
|
|
||||||
- chrony
|
|
||||||
- curl
|
|
||||||
- e2fsprogs
|
|
||||||
- glibc-locale
|
|
||||||
- kernel-default
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- NetworkManager
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- sudo
|
|
||||||
- vim
|
|
||||||
- xfsprogs
|
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|
||||||
bootstrap_void:
|
|
||||||
base:
|
|
||||||
- base-system
|
|
||||||
- void-repo-nonfree
|
|
||||||
extra:
|
|
||||||
- btrfs-progs
|
|
||||||
- chrony
|
|
||||||
- curl
|
|
||||||
- dhcpcd
|
|
||||||
- e2fsprogs
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- sudo
|
|
||||||
- vim
|
|
||||||
- xfsprogs
|
|
||||||
conditional: >-
|
|
||||||
{{
|
|
||||||
(['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ bootstrap_common_conditional
|
|
||||||
}}
|
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
---
|
|
||||||
# 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
|
|
||||||
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: {}
|
|
||||||
|
|
||||||
cis_permission_targets:
|
|
||||||
- { path: "/mnt/etc/ssh/sshd_config", mode: "0600" }
|
|
||||||
- { path: "/mnt/etc/cron.hourly", mode: "0700" }
|
|
||||||
- { path: "/mnt/etc/cron.daily", mode: "0700" }
|
|
||||||
- { path: "/mnt/etc/cron.weekly", mode: "0700" }
|
|
||||||
- { path: "/mnt/etc/cron.monthly", mode: "0700" }
|
|
||||||
- { path: "/mnt/etc/cron.d", mode: "0700" }
|
|
||||||
- { path: "/mnt/etc/crontab", mode: "0600" }
|
|
||||||
- { path: "/mnt/etc/logrotate.conf", mode: "0644" }
|
|
||||||
- { path: "/mnt/usr/sbin/pppd", mode: "0754" }
|
|
||||||
- { path: "/mnt/usr/bin/{{ cis_fusermount_binary }}", mode: "0755" }
|
|
||||||
- { path: "/mnt/usr/bin/{{ cis_write_binary }}", mode: "0755" }
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Normalize CIS input
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
cis_enabled: "{{ cis is defined and (cis is mapping or cis | bool) }}"
|
|
||||||
cis_input: "{{ cis if cis is mapping else {} }}"
|
|
||||||
|
|
||||||
- name: Normalize CIS configuration
|
|
||||||
when: cis_enabled and cis_cfg is not defined
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
cis_cfg: "{{ cis_defaults | combine(cis_input, recursive=True) }}"
|
|
||||||
@@ -3,21 +3,13 @@
|
|||||||
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 {{ cis_cfg.umask_profile }}"
|
line: "umask 027"
|
||||||
|
|
||||||
# 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
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "\\s*nullok"
|
regexp: "\\s*nullok"
|
||||||
replace: ""
|
replace: ""
|
||||||
loop: >-
|
loop:
|
||||||
{{
|
- /mnt/etc/pam.d/system-auth
|
||||||
['/mnt/etc/pam.d/system-auth', '/mnt/etc/pam.d/password-auth']
|
- /mnt/etc/pam.d/password-auth
|
||||||
if is_rhel | bool
|
|
||||||
else (
|
|
||||||
['/mnt/etc/pam.d/common-auth', '/mnt/etc/pam.d/common-password']
|
|
||||||
if is_debian | bool
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
---
|
---
|
||||||
# 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 ["almalinux", "rhel9", "rhel10", "rocky"]
|
||||||
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
ansible.builtin.command: arch-chroot /mnt /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1
|
||||||
register: cis_crypto_policy_result
|
register: cis_crypto_policy_result
|
||||||
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
|
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
|
||||||
|
|
||||||
- name: Mask Systemd Services
|
- name: Mask Systemd Services
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
{{ chroot_command }} systemctl mask {{ 'nftables' if system_cfg.features.firewall.toolkit == 'iptables' else 'iptables' }} bluetooth rpcbind
|
arch-chroot /mnt systemctl mask nftables bluetooth rpcbind
|
||||||
register: cis_mask_services_result
|
register: cis_mask_services_result
|
||||||
changed_when: "'Created symlink' in cis_mask_services_result.stderr"
|
changed_when: cis_mask_services_result.rc == 0
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: Normalize CIS configuration
|
- name: Include CIS hardening tasks
|
||||||
ansible.builtin.import_tasks: _normalize.yml
|
ansible.builtin.include_tasks: "{{ cis_task }}"
|
||||||
|
loop:
|
||||||
- name: Apply CIS hardening
|
- modules.yml
|
||||||
when: cis_enabled
|
- sysctl.yml
|
||||||
block:
|
- auth.yml
|
||||||
- name: Include CIS hardening tasks
|
- crypto.yml
|
||||||
ansible.builtin.include_tasks: "{{ cis_task }}"
|
- files.yml
|
||||||
loop:
|
- security_lines.yml
|
||||||
- modules.yml
|
- permissions.yml
|
||||||
- sysctl.yml
|
- sshd.yml
|
||||||
- auth.yml
|
loop_control:
|
||||||
- crypto.yml
|
loop_var: cis_task
|
||||||
- files.yml
|
|
||||||
- security_lines.yml
|
|
||||||
- permissions.yml
|
|
||||||
- sshd.yml
|
|
||||||
loop_control:
|
|
||||||
loop_var: cis_task
|
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
---
|
---
|
||||||
- name: Disable Kernel Modules
|
- name: Disable Kernel Modules
|
||||||
vars:
|
|
||||||
# Ubuntu uses squashfs for snap packages — blacklisting it breaks snap entirely
|
|
||||||
cis_modules_squashfs: "{{ [] if os in ['ubuntu', 'ubuntu-lts'] else ['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"
|
||||||
content: |
|
content: |
|
||||||
# CIS LVL 3 Restrictions
|
# CIS LVL 3 Restrictions
|
||||||
{% for mod in cis_modules_all %}
|
install freevxfs /bin/false
|
||||||
install {{ mod }}{{ ' ' * (16 - mod | length) }}/bin/false
|
install jffs2 /bin/false
|
||||||
{% endfor %}
|
install hfs /bin/false
|
||||||
|
install hfsplus /bin/false
|
||||||
|
install cramfs /bin/false
|
||||||
|
install squashfs /bin/false
|
||||||
|
install udf /bin/false
|
||||||
|
install usb-storage /bin/false
|
||||||
|
install dccp /bin/false
|
||||||
|
install sctp /bin/false
|
||||||
|
install rds /bin/false
|
||||||
|
install tipc /bin/false
|
||||||
|
|
||||||
- name: Remove old USB rules file
|
- name: Remove legacy USB rules file
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh
|
path: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh
|
||||||
state: absent
|
state: absent
|
||||||
|
|||||||
@@ -1,10 +1,29 @@
|
|||||||
---
|
---
|
||||||
|
- name: Build CIS permission targets
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cis_permission_targets: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
{ "path": "/mnt/etc/ssh/sshd_config", "mode": "0600" },
|
||||||
|
{ "path": "/mnt/etc/cron.hourly", "mode": "0700" },
|
||||||
|
{ "path": "/mnt/etc/cron.daily", "mode": "0700" },
|
||||||
|
{ "path": "/mnt/etc/cron.weekly", "mode": "0700" },
|
||||||
|
{ "path": "/mnt/etc/cron.monthly", "mode": "0700" },
|
||||||
|
{ "path": "/mnt/etc/cron.d", "mode": "0700" },
|
||||||
|
{ "path": "/mnt/etc/crontab", "mode": "0600" },
|
||||||
|
{ "path": "/mnt/etc/logrotate.conf", "mode": "0644" },
|
||||||
|
{ "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os not in ["rhel8", "rhel9", "rhel10"] else None,
|
||||||
|
{ "path": "/mnt/usr/bin/" + ("fusermount3" if os in ["archlinux", "debian12", "fedora", "rhel9",
|
||||||
|
"rhel10", "rocky"] else "fusermount"), "mode": "755" },
|
||||||
|
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" }
|
||||||
|
] | reject("none")
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Check CIS permission targets
|
- name: Check CIS permission targets
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
loop: "{{ cis_permission_targets }}"
|
loop: "{{ cis_permission_targets }}"
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path }}"
|
|
||||||
register: cis_permission_stats
|
register: cis_permission_stats
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
@@ -15,6 +34,4 @@
|
|||||||
group: "{{ item.item.group | default(omit) }}"
|
group: "{{ item.item.group | default(omit) }}"
|
||||||
mode: "{{ item.item.mode }}"
|
mode: "{{ item.item.mode }}"
|
||||||
loop: "{{ cis_permission_stats.results }}"
|
loop: "{{ cis_permission_stats.results }}"
|
||||||
loop_control:
|
|
||||||
label: "{{ item.item.path }}"
|
|
||||||
when: item.stat.exists
|
when: item.stat.exists
|
||||||
|
|||||||
@@ -2,61 +2,45 @@
|
|||||||
- name: Add Security related lines into config files
|
- name: Add Security related lines into config files
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
regexp: "{{ item.regexp }}"
|
|
||||||
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, content: "* hard core 0"}
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*minlen\s*=', content: "minlen = {{ cis_cfg.pwquality_minlen }}" }
|
- {path: /mnt/etc/security/pwquality.conf, content: minlen = 14}
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*dcredit\s*=', content: dcredit = -1 }
|
- {path: /mnt/etc/security/pwquality.conf, content: dcredit = -1}
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ucredit\s*=', content: ucredit = -1 }
|
- {path: /mnt/etc/security/pwquality.conf, content: ucredit = -1}
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*ocredit\s*=', content: ocredit = -1 }
|
- {path: /mnt/etc/security/pwquality.conf, content: ocredit = -1}
|
||||||
- { path: /mnt/etc/security/pwquality.conf, regexp: '^\s*#?\s*lcredit\s*=', content: lcredit = -1 }
|
- {path: /mnt/etc/security/pwquality.conf, content: lcredit = -1}
|
||||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
- {path: '/mnt/etc/{{ "bashrc" if is_rhel | default(false) else "bash.bashrc" }}', content: umask 077}
|
||||||
regexp: '^\s*umask\s+\d+'
|
- {path: '/mnt/etc/{{ "bashrc" if is_rhel | default(false) else "bash.bashrc" }}', content: export TMOUT=3000}
|
||||||
content: "umask {{ cis_cfg.umask }}"
|
- {path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent}
|
||||||
- path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}'
|
- {path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log"}
|
||||||
regexp: '^\s*(export\s+)?TMOUT='
|
- {path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so}
|
||||||
content: "export TMOUT={{ cis_cfg.tmout }}"
|
|
||||||
- path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}'
|
|
||||||
regexp: '^\s*#?\s*Storage='
|
|
||||||
content: Storage=persistent
|
|
||||||
- path: /mnt/etc/sudoers
|
|
||||||
regexp: '^\s*Defaults\s+logfile='
|
|
||||||
content: Defaults logfile="/var/log/sudo.log"
|
|
||||||
- path: /mnt/etc/pam.d/su
|
|
||||||
regexp: '^\s*#?\s*auth\s+required\s+pam_wheel\.so'
|
|
||||||
content: auth required pam_wheel.so
|
|
||||||
- path: >-
|
- path: >-
|
||||||
/mnt/etc/{{
|
/mnt/etc/{{
|
||||||
"pam.d/common-auth"
|
"pam.d/common-auth"
|
||||||
if is_debian | bool
|
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"]
|
||||||
else "authselect/system-auth"
|
else "authselect/system-auth"
|
||||||
if os == "fedora"
|
if os == "fedora"
|
||||||
else "pam.d/system-auth"
|
else "pam.d/system-auth"
|
||||||
}}
|
}}
|
||||||
regexp: '^\s*auth\s+required\s+pam_faillock\.so'
|
|
||||||
content: >-
|
content: >-
|
||||||
auth required pam_faillock.so onerr=fail audit silent deny={{ cis_cfg.faillock_deny }} unlock_time={{ cis_cfg.faillock_unlock_time }}
|
auth required pam_faillock.so onerr=fail audit silent deny=5 unlock_time=900
|
||||||
- path: >-
|
- path: >-
|
||||||
/mnt/etc/{{
|
/mnt/etc/{{
|
||||||
"pam.d/common-account"
|
"pam.d/common-account"
|
||||||
if is_debian | bool
|
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"]
|
||||||
else "authselect/system-auth"
|
else "authselect/system-auth"
|
||||||
if os == "fedora"
|
if os == "fedora"
|
||||||
else "pam.d/system-auth"
|
else "pam.d/system-auth"
|
||||||
}}
|
}}
|
||||||
regexp: '^\s*account\s+required\s+pam_faillock\.so'
|
|
||||||
content: account required pam_faillock.so
|
content: account required pam_faillock.so
|
||||||
- path: >-
|
- path: >-
|
||||||
/mnt/etc/pam.d/{{
|
/mnt/etc/pam.d/{{
|
||||||
"common-password"
|
"common-password"
|
||||||
if is_debian | bool
|
if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"]
|
||||||
else "passwd"
|
else "passwd"
|
||||||
}}
|
}}
|
||||||
regexp: '^\s*password\s+\[success=1.*\]\s+pam_unix\.so'
|
|
||||||
content: >-
|
content: >-
|
||||||
password [success=1 default=ignore] pam_unix.so obscure sha512 remember={{ cis_cfg.password_remember }}
|
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
|
||||||
- { path: /mnt/etc/hosts.deny, regexp: '^ALL:\s*ALL', content: "ALL: ALL" }
|
- {path: /mnt/etc/hosts.deny, content: "ALL: ALL"}
|
||||||
- { path: /mnt/etc/hosts.allow, regexp: '^sshd:\s*ALL', content: "sshd: ALL" }
|
- {path: /mnt/etc/hosts.allow, content: "sshd: ALL"}
|
||||||
loop_control:
|
|
||||||
label: "{{ item.content }}"
|
|
||||||
|
|||||||
@@ -4,37 +4,48 @@
|
|||||||
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: "{{ cis_cfg.sshd_options }}"
|
loop:
|
||||||
loop_control:
|
- {option: LogLevel, value: VERBOSE}
|
||||||
label: "{{ item.option }}"
|
- {option: LoginGraceTime, value: "60"}
|
||||||
|
- {option: PermitRootLogin, value: "no"}
|
||||||
- name: Detect target OpenSSH version
|
- {option: StrictModes, value: "yes"}
|
||||||
ansible.builtin.shell: >-
|
- {option: MaxAuthTries, value: "4"}
|
||||||
set -o pipefail && {{ chroot_command }} ssh -V 2>&1 | grep -oP 'OpenSSH_\K[0-9]+\.[0-9]+'
|
- {option: MaxSessions, value: "10"}
|
||||||
args:
|
- {option: MaxStartups, value: "10:30:60"}
|
||||||
executable: /bin/bash
|
- {option: PubkeyAuthentication, value: "yes"}
|
||||||
register: cis_sshd_openssh_version
|
- {option: HostbasedAuthentication, value: "no"}
|
||||||
changed_when: false
|
- {option: IgnoreRhosts, value: "yes"}
|
||||||
failed_when: false
|
- {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: ChallengeResponseAuthentication, 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}
|
||||||
|
|
||||||
- name: Append CIS specific configurations to sshd_config
|
- name: Append CIS specific configurations to sshd_config
|
||||||
vars:
|
|
||||||
cis_sshd_has_mlkem: "{{ (cis_sshd_openssh_version.stdout | default('0.0') is version('9.9', '>=')) }}"
|
|
||||||
cis_sshd_kex: >-
|
|
||||||
{{
|
|
||||||
(['mlkem768x25519-sha256'] if cis_sshd_has_mlkem | bool else [])
|
|
||||||
+ ['curve25519-sha256@libssh.org', 'ecdh-sha2-nistp521', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256']
|
|
||||||
}}
|
|
||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /mnt/etc/ssh/sshd_config
|
path: /mnt/etc/ssh/sshd_config
|
||||||
marker: "# {mark} CIS SSH HARDENING"
|
marker: "# {mark} CIS SSH HARDENING"
|
||||||
block: |-
|
block: |-
|
||||||
## CIS Specific
|
## CIS Specific
|
||||||
|
Protocol 2
|
||||||
### Ciphers and keying ###
|
### Ciphers and keying ###
|
||||||
RekeyLimit 512M 6h
|
RekeyLimit 512M 6h
|
||||||
KexAlgorithms {{ cis_sshd_kex | join(',') }}
|
KexAlgorithms mlkem768x25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256
|
||||||
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
||||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
||||||
###########################
|
###########################
|
||||||
AllowStreamLocalForwarding no
|
AllowStreamLocalForwarding no
|
||||||
PermitUserRC no
|
PermitUserRC no
|
||||||
|
AllowUsers *
|
||||||
|
AllowGroups *
|
||||||
|
DenyUsers nobody
|
||||||
|
DenyGroups nobody
|
||||||
|
|||||||
@@ -5,6 +5,26 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: |
|
content: |
|
||||||
## CIS Sysctl configurations
|
## CIS Sysctl configurations
|
||||||
{% for key, value in cis_cfg.sysctl | dictsort %}
|
kernel.yama.ptrace_scope=1
|
||||||
{{ key }}={{ value }}
|
kernel.randomize_va_space=2
|
||||||
{% endfor %}
|
# Network
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
# OS-specific binary names for CIS permission targets.
|
|
||||||
# fusermount3 is the modern name; older distros still use fusermount.
|
|
||||||
cis_fusermount_binary: >-
|
|
||||||
{{
|
|
||||||
'fusermount3'
|
|
||||||
if (
|
|
||||||
os in ['archlinux', 'fedora', 'rocky', 'rhel']
|
|
||||||
or (os == 'debian' and (os_version | string) not in ['10', '11'])
|
|
||||||
or (os == 'almalinux')
|
|
||||||
)
|
|
||||||
else 'fusermount'
|
|
||||||
}}
|
|
||||||
|
|
||||||
# write.ul is the Debian 11 name; all others use write.
|
|
||||||
cis_write_binary: >-
|
|
||||||
{{
|
|
||||||
'write.ul'
|
|
||||||
if (os == 'debian' and (os_version | string) == '11')
|
|
||||||
else 'write'
|
|
||||||
}}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
# Post-reboot verification
|
|
||||||
cleanup_verify_boot: true
|
|
||||||
cleanup_boot_timeout: 300
|
|
||||||
cleanup_remove_on_failure: true
|
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Remove Archiso and cloud-init disks
|
- name: Remove Archiso and cloud-init disks
|
||||||
when: hypervisor_type == "libvirt"
|
when: hypervisor == "libvirt"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
block:
|
block:
|
||||||
|
- name: Set libvirt image paths
|
||||||
|
vars:
|
||||||
|
cleanup_libvirt_image_dir_value: "{{ vm_path | default('/var/lib/libvirt/images') }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_image_dir: "{{ cleanup_libvirt_image_dir_value }}"
|
||||||
|
cleanup_libvirt_cloudinit_path: >-
|
||||||
|
{{ [cleanup_libvirt_image_dir_value, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Read current VM XML definition
|
- name: Read current VM XML definition
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
command: get_xml
|
command: get_xml
|
||||||
@@ -14,21 +23,9 @@
|
|||||||
- name: Initialize cleaned VM XML
|
- name: Initialize cleaned VM XML
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_get_xml.get_xml }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_get_xml.get_xml }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Remove boot ISO device from VM XML (source match)
|
- name: Remove boot ISO device from VM XML (target match)
|
||||||
when: boot_iso is defined and boot_iso | length > 0
|
|
||||||
community.general.xml:
|
|
||||||
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
|
||||||
xpath: "/domain/devices/disk[contains(source/@file, '{{ boot_iso | basename }}')]"
|
|
||||||
state: absent
|
|
||||||
register: cleanup_libvirt_xml_strip_boot_source
|
|
||||||
|
|
||||||
- name: Update cleaned VM XML after removing boot ISO source match
|
|
||||||
when: boot_iso is defined and boot_iso | length > 0
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot_source.xmlstring }}"
|
|
||||||
|
|
||||||
- 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='sda']"
|
xpath: "/domain/devices/disk[target/@dev='sda']"
|
||||||
@@ -38,6 +35,33 @@
|
|||||||
- name: Update cleaned VM XML after removing boot 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_boot.xmlstring }}"
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot.xmlstring }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Remove boot ISO device from VM XML (source match)
|
||||||
|
when: boot_iso is defined and (boot_iso | length > 0)
|
||||||
|
community.general.xml:
|
||||||
|
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
||||||
|
xpath: "/domain/devices/disk[contains(source/@file, '{{ boot_iso | basename }}')]"
|
||||||
|
state: absent
|
||||||
|
register: cleanup_libvirt_xml_strip_boot_source
|
||||||
|
|
||||||
|
- name: Update cleaned VM XML after removing boot ISO source match
|
||||||
|
when: boot_iso is defined and (boot_iso | length > 0)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot_source.xmlstring }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Remove cloud-init ISO device from VM XML (target match)
|
||||||
|
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: Remove cloud-init ISO device from VM XML (source match)
|
- name: Remove cloud-init ISO device from VM XML (source match)
|
||||||
community.general.xml:
|
community.general.xml:
|
||||||
@@ -49,17 +73,7 @@
|
|||||||
- name: Update cleaned VM XML after removing cloud-init ISO source match
|
- name: Update cleaned VM XML after removing cloud-init ISO source match
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
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
|
||||||
- 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 }}"
|
|
||||||
|
|
||||||
- name: Strip XML declaration for libvirt define
|
- name: Strip XML declaration for libvirt define
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
@@ -71,12 +85,7 @@
|
|||||||
| regex_replace("(?i)encoding=[\"'][^\"']+[\"']", "")
|
| regex_replace("(?i)encoding=[\"'][^\"']+[\"']", "")
|
||||||
| trim
|
| trim
|
||||||
}}
|
}}
|
||||||
|
changed_when: false
|
||||||
- name: Ensure boot device is set to hard disk in VM XML
|
|
||||||
when: "'<boot ' not in cleanup_libvirt_domain_xml_clean"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
cleanup_libvirt_domain_xml_clean: >-
|
|
||||||
{{ cleanup_libvirt_domain_xml_clean | regex_replace('(</type>)', '\1\n <boot dev="hd"/>') }}
|
|
||||||
|
|
||||||
- name: Update VM definition without installer media
|
- name: Update VM definition without installer media
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
@@ -85,53 +94,20 @@
|
|||||||
|
|
||||||
- name: Remove cloud-init disk
|
- name: Remove cloud-init disk
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ virtualization_libvirt_cloudinit_path }}"
|
path: "{{ cleanup_libvirt_cloudinit_path }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Ensure VM is powered off before restart
|
- name: Ensure VM is powered off before restart
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: destroyed
|
state: destroyed
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Enroll Secure Boot keys in VM NVRAM
|
|
||||||
when:
|
|
||||||
- system_cfg.features.secure_boot.enabled | default(false) | bool
|
|
||||||
- os != 'archlinux'
|
|
||||||
block:
|
|
||||||
- name: Find VM NVRAM file path
|
|
||||||
ansible.builtin.shell:
|
|
||||||
cmd: >-
|
|
||||||
set -o pipefail &&
|
|
||||||
virsh -c {{ libvirt_uri | default('qemu:///system') }} dumpxml {{ hostname }}
|
|
||||||
| grep -oP '<nvram[^>]*>\K[^<]+'
|
|
||||||
executable: /bin/bash
|
|
||||||
register: _sb_nvram_path
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Enroll Secure Boot keys via virt-fw-vars
|
|
||||||
when: _sb_nvram_path.stdout | default('') | length > 0
|
|
||||||
ansible.builtin.command:
|
|
||||||
argv:
|
|
||||||
- virt-fw-vars
|
|
||||||
- --inplace
|
|
||||||
- "{{ _sb_nvram_path.stdout | trim }}"
|
|
||||||
- --enroll-redhat
|
|
||||||
- --secure-boot
|
|
||||||
register: _sb_enroll_result
|
|
||||||
changed_when: _sb_enroll_result.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Start the VM
|
- name: Start the VM
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: running
|
state: running
|
||||||
|
|
||||||
# delegate_to inventory_hostname: overrides play-level localhost to run wait_for_connection against the VM
|
|
||||||
- name: Wait for VM to boot up
|
- name: Wait for VM to boot up
|
||||||
delegate_to: "{{ inventory_hostname }}"
|
delegate_to: "{{ inventory_hostname }}"
|
||||||
ansible.builtin.wait_for_connection:
|
ansible.builtin.wait_for_connection:
|
||||||
timeout: 300
|
timeout: 300
|
||||||
failed_when: false
|
|
||||||
changed_when: false
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
- name: Cleanup physical install
|
- name: Cleanup physical install
|
||||||
when: system_cfg.type == "physical"
|
when: install_type == "physical"
|
||||||
ansible.builtin.include_tasks: physical.yml
|
ansible.builtin.include_tasks: physical.yml
|
||||||
|
|
||||||
- name: Cleanup virtual install
|
- name: Cleanup virtual install
|
||||||
when: system_cfg.type == "virtual"
|
when: install_type == "virtual"
|
||||||
ansible.builtin.include_tasks: virtual.yml
|
ansible.builtin.include_tasks: virtual.yml
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
---
|
---
|
||||||
- name: Setup Cleanup
|
- name: Setup Cleanup
|
||||||
when: hypervisor_type == "proxmox"
|
when: hypervisor == "proxmox"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
module_defaults:
|
|
||||||
community.proxmox.proxmox_disk: "{{ _proxmox_auth }}"
|
|
||||||
community.proxmox.proxmox_kvm: "{{ _proxmox_auth_node }}"
|
|
||||||
block:
|
block:
|
||||||
- name: Cleanup Setup Disks
|
- name: Cleanup Setup Disks
|
||||||
community.proxmox.proxmox_disk:
|
community.proxmox.proxmox_disk:
|
||||||
|
api_host: "{{ hypervisor_url }}"
|
||||||
|
api_user: "{{ hypervisor_username }}"
|
||||||
|
api_password: "{{ hypervisor_password }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ vm_id }}"
|
||||||
disk: "{{ item }}"
|
disk: "{{ item }}"
|
||||||
state: absent
|
state: absent
|
||||||
loop: >-
|
loop:
|
||||||
{{
|
- ide0
|
||||||
['ide0', 'ide2']
|
- ide2
|
||||||
+ (['ide1'] if not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso') else [])
|
|
||||||
}}
|
|
||||||
failed_when: false
|
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Start the VM
|
- name: Start the VM
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
vmid: "{{ system_cfg.id }}"
|
api_host: "{{ hypervisor_url }}"
|
||||||
|
api_user: "{{ hypervisor_username }}"
|
||||||
|
api_password: "{{ hypervisor_password }}"
|
||||||
|
node: "{{ hypervisor_node }}"
|
||||||
|
vmid: "{{ vm_id }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
no_log: true
|
|
||||||
|
|||||||
@@ -6,152 +6,10 @@
|
|||||||
ansible.builtin.include_tasks: shutdown.yml
|
ansible.builtin.include_tasks: shutdown.yml
|
||||||
|
|
||||||
- name: Cleanup hypervisor resources
|
- name: Cleanup hypervisor resources
|
||||||
ansible.builtin.include_tasks: "{{ hypervisor_type }}.yml"
|
ansible.builtin.include_tasks: proxmox.yml
|
||||||
|
|
||||||
- name: Determine post-reboot connectivity
|
- name: Cleanup vCenter resources
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.include_tasks: vmware.yml
|
||||||
cleanup_post_reboot_can_connect: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
post_reboot_can_connect
|
|
||||||
if post_reboot_can_connect is defined
|
|
||||||
else (
|
|
||||||
(ansible_connection | default('ssh')) != 'ssh'
|
|
||||||
or ((system_cfg.network.ip | default('') | string | length) > 0)
|
|
||||||
or (
|
|
||||||
system_cfg.type == 'physical'
|
|
||||||
and (ansible_host | default('') | string | length) > 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) | bool
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Check VM accessibility after reboot
|
- name: Cleanup libvirt resources
|
||||||
when:
|
ansible.builtin.include_tasks: libvirt.yml
|
||||||
- cleanup_verify_boot | bool
|
|
||||||
- system_cfg.type == "virtual"
|
|
||||||
- cleanup_post_reboot_can_connect | bool
|
|
||||||
block:
|
|
||||||
- name: Attempt to connect to VM
|
|
||||||
delegate_to: "{{ inventory_hostname }}"
|
|
||||||
ansible.builtin.wait_for_connection:
|
|
||||||
timeout: "{{ cleanup_boot_timeout }}"
|
|
||||||
register: cleanup_vm_connection_check
|
|
||||||
failed_when: false
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: VM failed to boot - initiate cleanup
|
|
||||||
when:
|
|
||||||
- cleanup_remove_on_failure | bool
|
|
||||||
- cleanup_vm_connection_check is defined
|
|
||||||
- cleanup_vm_connection_check.failed | bool
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
block:
|
|
||||||
- name: VM boot failure detected - removing VM
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: |
|
|
||||||
VM {{ hostname }} failed to boot after provisioning.
|
|
||||||
This VM was created in the current playbook run and will be removed
|
|
||||||
to prevent orphaned resources.
|
|
||||||
|
|
||||||
- name: Remove failed libvirt VM
|
|
||||||
when: hypervisor_type == "libvirt"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
block:
|
|
||||||
- name: Destroy libvirt VM
|
|
||||||
community.libvirt.virt:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
state: destroyed
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Undefine libvirt VM
|
|
||||||
community.libvirt.virt:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
command: undefine
|
|
||||||
|
|
||||||
- name: Remove libvirt VM disks
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
state: absent
|
|
||||||
loop: "{{ virtualization_libvirt_disks | default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path }}"
|
|
||||||
|
|
||||||
- name: Remove libvirt cloud-init disk
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ virtualization_libvirt_cloudinit_path }}"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Remove failed Proxmox VM
|
|
||||||
when: hypervisor_type == "proxmox"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
module_defaults:
|
|
||||||
community.proxmox.proxmox_kvm: "{{ _proxmox_auth_node }}"
|
|
||||||
no_log: true
|
|
||||||
block:
|
|
||||||
- name: Stop Proxmox VM
|
|
||||||
community.proxmox.proxmox_kvm:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
vmid: "{{ system_cfg.id }}"
|
|
||||||
state: stopped
|
|
||||||
|
|
||||||
- name: Delete Proxmox VM
|
|
||||||
community.proxmox.proxmox_kvm:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
vmid: "{{ system_cfg.id }}"
|
|
||||||
state: absent
|
|
||||||
unprivileged: false
|
|
||||||
|
|
||||||
- name: Remove failed VMware VM
|
|
||||||
when: hypervisor_type == "vmware"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
module_defaults:
|
|
||||||
community.vmware.vmware_guest: "{{ _vmware_auth }}"
|
|
||||||
no_log: true
|
|
||||||
block:
|
|
||||||
- name: Power off VMware VM
|
|
||||||
community.vmware.vmware_guest:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
folder: "{{ system_cfg.path | default('/') }}"
|
|
||||||
state: poweredoff
|
|
||||||
|
|
||||||
- name: Delete VMware VM
|
|
||||||
community.vmware.vmware_guest:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
folder: "{{ system_cfg.path | default('/') }}"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Remove failed Xen VM
|
|
||||||
when: hypervisor_type == "xen"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
block:
|
|
||||||
- name: Destroy Xen VM if running
|
|
||||||
ansible.builtin.command:
|
|
||||||
argv:
|
|
||||||
- xl
|
|
||||||
- destroy
|
|
||||||
- "{{ hostname }}"
|
|
||||||
register: cleanup_xen_destroy
|
|
||||||
failed_when: false
|
|
||||||
changed_when: cleanup_xen_destroy.rc == 0
|
|
||||||
|
|
||||||
- name: Remove Xen VM disks
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
state: absent
|
|
||||||
loop: "{{ virtualization_xen_disks | default([]) }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path }}"
|
|
||||||
|
|
||||||
- name: Remove Xen VM config file
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "/tmp/xen-{{ hostname }}.cfg"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: VM cleanup completed
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: VM {{ hostname }} has been successfully removed due to boot failure.
|
|
||||||
|
|||||||
@@ -1,47 +1,40 @@
|
|||||||
---
|
---
|
||||||
- name: Clean vCenter VM
|
- name: Clean vCenter VM
|
||||||
when: hypervisor_type == "vmware"
|
when: hypervisor == "vmware"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
module_defaults:
|
|
||||||
community.vmware.vmware_guest: "{{ _vmware_auth }}"
|
|
||||||
vmware.vmware.vm_powerstate: "{{ _vmware_auth }}"
|
|
||||||
no_log: true
|
|
||||||
block:
|
block:
|
||||||
- name: Remove CD-ROM from VM in vCenter
|
- name: Remove CD-ROM from VM in vCenter
|
||||||
|
when: hypervisor == "vmware"
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
|
hostname: "{{ hypervisor_url }}"
|
||||||
|
username: "{{ hypervisor_username }}"
|
||||||
|
password: "{{ hypervisor_password }}"
|
||||||
|
validate_certs: false
|
||||||
|
datacenter: "{{ hypervisor_datacenter }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
cdrom: >-
|
cdrom:
|
||||||
{{
|
- controller_number: 0
|
||||||
[
|
unit_number: 0
|
||||||
{
|
controller_type: sata
|
||||||
'controller_number': 0,
|
type: iso
|
||||||
'unit_number': 0,
|
iso_path: "{{ boot_iso }}"
|
||||||
'controller_type': 'sata',
|
state: absent
|
||||||
'type': 'iso',
|
- controller_number: 0
|
||||||
'iso_path': boot_iso,
|
unit_number: 1
|
||||||
'state': 'absent'
|
controller_type: sata
|
||||||
}
|
type: iso
|
||||||
]
|
iso_path: "{{ rhel_iso | default(omit) }}"
|
||||||
+ (
|
state: absent
|
||||||
[
|
|
||||||
{
|
|
||||||
'controller_number': 0,
|
|
||||||
'unit_number': 1,
|
|
||||||
'controller_type': 'sata',
|
|
||||||
'type': 'iso',
|
|
||||||
'iso_path': rhel_iso,
|
|
||||||
'state': 'absent'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
if (rhel_iso is defined and rhel_iso | length > 0
|
|
||||||
and not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso'))
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Start VM in vCenter
|
- name: Start VM in vCenter
|
||||||
|
when: hypervisor == "vmware"
|
||||||
vmware.vmware.vm_powerstate:
|
vmware.vmware.vm_powerstate:
|
||||||
|
hostname: "{{ hypervisor_url }}"
|
||||||
|
username: "{{ hypervisor_username }}"
|
||||||
|
password: "{{ hypervisor_password }}"
|
||||||
|
validate_certs: false
|
||||||
|
datacenter: "{{ hypervisor_datacenter }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: powered-on
|
state: powered-on
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Cleanup Xen installer media
|
|
||||||
when: hypervisor_type == "xen"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
vars:
|
|
||||||
xen_installer_media_enabled: "{{ xen_installer_media_enabled | default(false) }}"
|
|
||||||
block:
|
|
||||||
- name: Ensure Xen disk definitions exist
|
|
||||||
ansible.builtin.include_tasks: ../../virtualization/tasks/_xen_disks.yml
|
|
||||||
|
|
||||||
- name: Render Xen VM configuration without installer media
|
|
||||||
vars:
|
|
||||||
xen_installer_media_enabled: false
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: xen.cfg.j2
|
|
||||||
dest: /tmp/xen-{{ hostname }}.cfg
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Destroy Xen VM if running
|
|
||||||
ansible.builtin.command:
|
|
||||||
argv:
|
|
||||||
- xl
|
|
||||||
- destroy
|
|
||||||
- "{{ hostname }}"
|
|
||||||
register: cleanup_xen_destroy
|
|
||||||
failed_when: false
|
|
||||||
changed_when: cleanup_xen_destroy.rc == 0
|
|
||||||
|
|
||||||
- name: Start Xen VM without installer media
|
|
||||||
ansible.builtin.command:
|
|
||||||
argv:
|
|
||||||
- xl
|
|
||||||
- create
|
|
||||||
- /tmp/xen-{{ hostname }}.cfg
|
|
||||||
register: cleanup_xen_start_result
|
|
||||||
changed_when: cleanup_xen_start_result.rc == 0
|
|
||||||
|
|
||||||
- name: Remove temporary Xen configuration file
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /tmp/xen-{{ hostname }}.cfg
|
|
||||||
state: absent
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
# Network configuration dispatch — maps OS name to the task file
|
|
||||||
# that writes network config. Default (NetworkManager) applies to
|
|
||||||
# all OSes not explicitly listed.
|
|
||||||
configuration_network_task_map:
|
|
||||||
alpine: network_alpine.yml
|
|
||||||
void: network_void.yml
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
# Shared task: update BLS (Boot Loader Specification) entries with kernel cmdline.
|
|
||||||
# Expects variable: _bls_cmdline (the kernel command line string)
|
|
||||||
- name: Find BLS entries
|
|
||||||
ansible.builtin.find:
|
|
||||||
paths: /mnt/boot/loader/entries
|
|
||||||
patterns: "*.conf"
|
|
||||||
register: _bls_entries
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Update BLS options
|
|
||||||
when: _bls_entries.files | length > 0
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
regexp: "^options "
|
|
||||||
line: "options {{ _bls_cmdline }}"
|
|
||||||
loop: "{{ _bls_entries.files }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path }}"
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
# Resolve platform-specific configuration for the target OS family.
|
|
||||||
# Sets _configuration_platform from configuration_platform_config[os_family].
|
|
||||||
- name: Resolve platform-specific configuration
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- os_family is defined
|
|
||||||
- os_family in configuration_platform_config
|
|
||||||
fail_msg: >-
|
|
||||||
Unsupported os_family '{{ os_family | default("undefined") }}'.
|
|
||||||
Extend configuration_platform_config in vars/main.yml.
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Set platform configuration
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_configuration_platform: "{{ configuration_platform_config[os_family] }}"
|
|
||||||
|
|
||||||
- name: Override EFI loader to shim for Secure Boot
|
|
||||||
when:
|
|
||||||
- system_cfg.features.secure_boot.enabled | bool
|
|
||||||
- _configuration_platform.efi_loader != 'shimx64.efi'
|
|
||||||
- os != 'archlinux'
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_configuration_platform: >-
|
|
||||||
{{ _configuration_platform | combine({'efi_loader': 'shimx64.efi'}) }}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Configure MOTD
|
|
||||||
when: system_cfg.features.banner.motd | bool
|
|
||||||
block:
|
|
||||||
- name: Create MOTD file
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: |
|
|
||||||
********************************************************************
|
|
||||||
* AUTHORIZED ACCESS ONLY. ALL ACTIVITIES ARE MONITORED AND LOGGED. *
|
|
||||||
********************************************************************
|
|
||||||
dest: /mnt/etc/motd
|
|
||||||
mode: "0644"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
|
|
||||||
- name: Remove other MOTD files
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- /mnt/etc/motd.d/99-motd
|
|
||||||
- /mnt/etc/motd.d/cockpit
|
|
||||||
- /mnt/etc/motd.d/insights-client
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Create login banner
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: "{{ item }}"
|
|
||||||
content: |
|
|
||||||
**************************************************************
|
|
||||||
* WARNING: Unauthorized access to this system is prohibited. *
|
|
||||||
* All activities are monitored and logged. *
|
|
||||||
* Disconnect immediately if you are not an authorized user. *
|
|
||||||
**************************************************************
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0644"
|
|
||||||
loop:
|
|
||||||
- /mnt/etc/issue
|
|
||||||
- /mnt/etc/issue.net
|
|
||||||
|
|
||||||
- name: Configure sudo banner
|
|
||||||
when: system_cfg.features.banner.sudo | bool
|
|
||||||
block:
|
|
||||||
- name: Create sudo lecture file
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: |
|
|
||||||
I am Groot, and I know what I'm doing.
|
|
||||||
dest: /mnt/etc/sudo_lecture
|
|
||||||
mode: "0644"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
|
|
||||||
- name: Enable sudo lecture in sudoers
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/sudoers
|
|
||||||
line: "{{ item }}"
|
|
||||||
state: present
|
|
||||||
create: true
|
|
||||||
mode: "0440"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
validate: "/usr/sbin/visudo --check --file=%s"
|
|
||||||
loop:
|
|
||||||
- "Defaults lecture=always"
|
|
||||||
- "Defaults lecture_file=/etc/sudo_lecture"
|
|
||||||
@@ -1,119 +1,65 @@
|
|||||||
---
|
---
|
||||||
- name: Configure Bootloader
|
- name: Configure Bootloader
|
||||||
vars:
|
|
||||||
_efi_vendor: >-
|
|
||||||
{{
|
|
||||||
"redhat" if os == "rhel"
|
|
||||||
else ("ubuntu" if os in ["ubuntu", "ubuntu-lts"] else os)
|
|
||||||
}}
|
|
||||||
_efi_loader: "{{ _configuration_platform.efi_loader }}"
|
|
||||||
block:
|
block:
|
||||||
- name: Install GRUB EFI binary
|
- name: Install Bootloader
|
||||||
when: _configuration_platform.grub_install
|
vars:
|
||||||
ansible.builtin.command: >-
|
configuration_use_efibootmgr: "{{ is_rhel | default(false) }}"
|
||||||
{{ chroot_command }} /usr/sbin/grub-install --target=x86_64-efi
|
configuration_efi_dir: >-
|
||||||
--efi-directory={{ partitioning_efi_mountpoint }}
|
{{ "/boot/efi" if (is_debian | default(false) or is_rhel | default(false)) else "/boot" }}
|
||||||
--bootloader-id={{ _efi_vendor }}
|
configuration_bootloader_id: >-
|
||||||
--no-nvram
|
{{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }}
|
||||||
|
configuration_efi_vendor: >-
|
||||||
|
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }}
|
||||||
|
configuration_efibootmgr_cmd: >-
|
||||||
|
/usr/sbin/efibootmgr -c -L '{{ os }}' -d "{{ install_drive }}" -p 1
|
||||||
|
-l '\efi\EFI\{{ configuration_efi_vendor }}\shimx64.efi'
|
||||||
|
configuration_grub_cmd: >-
|
||||||
|
/usr/sbin/grub-install --target=x86_64-efi
|
||||||
|
--efi-directory={{ configuration_efi_dir }}
|
||||||
|
--bootloader-id={{ configuration_bootloader_id }}
|
||||||
|
configuration_bootloader_cmd: >-
|
||||||
|
{{ configuration_efibootmgr_cmd if configuration_use_efibootmgr else configuration_grub_cmd }}
|
||||||
|
ansible.builtin.command: "arch-chroot /mnt {{ configuration_bootloader_cmd }}"
|
||||||
register: configuration_bootloader_result
|
register: configuration_bootloader_result
|
||||||
changed_when: configuration_bootloader_result.rc == 0
|
changed_when: configuration_bootloader_result.rc == 0
|
||||||
|
|
||||||
- name: Check existing EFI boot entries
|
|
||||||
ansible.builtin.command: efibootmgr
|
|
||||||
register: configuration_efi_entries
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Ensure EFI boot entry exists
|
|
||||||
when: ('* ' + _efi_vendor) not in configuration_efi_entries.stdout
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
efibootmgr -c
|
|
||||||
-L '{{ _efi_vendor }}'
|
|
||||||
-d '{{ install_drive }}'
|
|
||||||
-p 1
|
|
||||||
-l '\EFI\{{ _efi_vendor }}\{{ _efi_loader }}'
|
|
||||||
register: configuration_efi_entry_result
|
|
||||||
changed_when: configuration_efi_entry_result.rc == 0
|
|
||||||
|
|
||||||
- name: Set installed OS as first EFI boot entry
|
|
||||||
ansible.builtin.shell:
|
|
||||||
cmd: >-
|
|
||||||
set -o pipefail &&
|
|
||||||
efibootmgr | grep -i '{{ _efi_vendor }}' | grep -oP 'Boot\K[0-9A-F]+' | head -1
|
|
||||||
| xargs -I{} efibootmgr -o {}
|
|
||||||
executable: /bin/bash
|
|
||||||
register: _efi_bootorder_result
|
|
||||||
changed_when: _efi_bootorder_result.rc == 0
|
|
||||||
|
|
||||||
- name: Ensure lvm2 for non btrfs filesystems
|
- name: Ensure lvm2 for non btrfs filesystems
|
||||||
when: os == "archlinux" and system_cfg.filesystem != "btrfs"
|
when: os | lower == "archlinux" and filesystem != "btrfs"
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/etc/mkinitcpio.conf
|
path: /mnt/etc/mkinitcpio.conf
|
||||||
regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)"
|
regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)"
|
||||||
line: "\\1 lvm2\\2"
|
line: '\1 lvm2\2'
|
||||||
backrefs: true
|
backrefs: true
|
||||||
|
|
||||||
- name: Regenerate initramfs
|
- name: Regenerate initramfs
|
||||||
when: _configuration_platform.initramfs_cmd | length > 0
|
vars:
|
||||||
ansible.builtin.command: "{{ chroot_command }} {{ _configuration_platform.initramfs_cmd }}"
|
configuration_initramfs_cmd: >-
|
||||||
|
{{
|
||||||
|
'/usr/sbin/mkinitcpio -P'
|
||||||
|
if os | lower == "archlinux"
|
||||||
|
else (
|
||||||
|
'/usr/sbin/update-initramfs -u -k all'
|
||||||
|
if is_debian | default(false)
|
||||||
|
else '/usr/bin/dracut --regenerate-all --force'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "arch-chroot /mnt {{ configuration_initramfs_cmd }}"
|
||||||
register: configuration_initramfs_result
|
register: configuration_initramfs_result
|
||||||
changed_when: configuration_initramfs_result.rc == 0
|
changed_when: configuration_initramfs_result.rc == 0
|
||||||
|
|
||||||
- name: Generate grub config (RedHat)
|
- name: Generate grub config
|
||||||
when: os_family == 'RedHat'
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} /usr/sbin/{{ _configuration_platform.grub_mkconfig_prefix }}
|
|
||||||
-o /boot/grub2/grub.cfg
|
|
||||||
register: configuration_grub_result
|
|
||||||
changed_when: configuration_grub_result.rc == 0
|
|
||||||
|
|
||||||
- name: Fix btrfs BLS boot variable in grub config
|
|
||||||
when:
|
|
||||||
- os_family == 'RedHat'
|
|
||||||
- system_cfg.filesystem == 'btrfs'
|
|
||||||
ansible.builtin.replace:
|
|
||||||
path: /mnt/boot/grub2/grub.cfg
|
|
||||||
regexp: 'search --no-floppy --fs-uuid --set=boot \S+'
|
|
||||||
replace: 'set boot=$root'
|
|
||||||
|
|
||||||
- name: Create EFI grub.cfg wrapper for RedHat
|
|
||||||
when: os_family == 'RedHat'
|
|
||||||
vars:
|
vars:
|
||||||
_grub2_path: >-
|
configuration_efi_vendor: >-
|
||||||
{{
|
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }}
|
||||||
'/grub2'
|
configuration_grub_cfg_cmd: >-
|
||||||
if (partitioning_separate_boot | bool)
|
{{ '/usr/sbin/grub2-mkconfig -o /boot/efi/EFI/' + configuration_efi_vendor + '/grub.cfg'
|
||||||
else ('/@/boot/grub2' if system_cfg.filesystem == 'btrfs' else '/boot/grub2')
|
if is_rhel | default(false)
|
||||||
|
else '/usr/sbin/grub-mkconfig -o ' + (
|
||||||
|
'/boot/efi/EFI/ubuntu/grub.cfg'
|
||||||
|
if os | lower in ["ubuntu", "ubuntu-lts"]
|
||||||
|
else '/boot/grub/grub.cfg'
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
ansible.builtin.shell:
|
ansible.builtin.command: "arch-chroot /mnt {{ configuration_grub_cfg_cmd }}"
|
||||||
cmd: |
|
|
||||||
set -o pipefail
|
|
||||||
uuid=$(grep -m1 'search.*--set=root' /mnt/boot/grub2/grub.cfg | grep -oP '[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}')
|
|
||||||
cat > /mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/grub.cfg <<GRUBEOF
|
|
||||||
search --no-floppy --fs-uuid --set=dev $uuid
|
|
||||||
set prefix=(\$dev){{ _grub2_path }}
|
|
||||||
export \$prefix
|
|
||||||
configfile \$prefix/grub.cfg
|
|
||||||
GRUBEOF
|
|
||||||
executable: /bin/bash
|
|
||||||
register: _grub_wrapper_result
|
|
||||||
changed_when: _grub_wrapper_result.rc == 0
|
|
||||||
|
|
||||||
- name: Generate grub config (non-RedHat)
|
|
||||||
when: os_family != 'RedHat'
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg"
|
|
||||||
register: configuration_grub_result
|
register: configuration_grub_result
|
||||||
changed_when: configuration_grub_result.rc == 0
|
changed_when: configuration_grub_result.rc == 0
|
||||||
|
|
||||||
- name: Rebuild GRUB as standalone EFI for Secure Boot
|
|
||||||
when:
|
|
||||||
- system_cfg.features.secure_boot.enabled | default(false) | bool
|
|
||||||
- os == 'archlinux'
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} grub-mkstandalone
|
|
||||||
-d /usr/lib/grub/x86_64-efi
|
|
||||||
-O x86_64-efi
|
|
||||||
--disable-shim-lock
|
|
||||||
-o {{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/grubx64.efi
|
|
||||||
boot/grub/grub.cfg=/boot/grub/grub.cfg
|
|
||||||
register: _grub_standalone_result
|
|
||||||
changed_when: _grub_standalone_result.rc == 0
|
|
||||||
|
|||||||
@@ -1,58 +1,60 @@
|
|||||||
---
|
---
|
||||||
- name: Configure disk encryption
|
- name: Configure disk encryption
|
||||||
when: system_cfg.luks.enabled | bool
|
when: partitioning_luks_enabled | default(luks_enabled | default(false)) | bool
|
||||||
no_log: true
|
|
||||||
vars:
|
vars:
|
||||||
configuration_luks_passphrase: >-
|
configuration_luks_passphrase_effective: >-
|
||||||
{{ system_cfg.luks.passphrase | string }}
|
{{ (partitioning_luks_passphrase | default(luks_passphrase | default(''))) | string }}
|
||||||
block:
|
block:
|
||||||
- name: Set LUKS configuration facts
|
- name: Set LUKS configuration facts
|
||||||
vars:
|
vars:
|
||||||
_raw_pcrs: >-
|
configuration_luks_mapper_name_value: >-
|
||||||
|
{{
|
||||||
|
partitioning_luks_mapper_name
|
||||||
|
| default(luks_mapper_name | default('SYSTEM_DECRYPTED'))
|
||||||
|
}}
|
||||||
|
configuration_luks_device_value: >-
|
||||||
|
{{
|
||||||
|
partitioning_luks_device
|
||||||
|
| default(
|
||||||
|
install_drive
|
||||||
|
~ (partitioning_main_partition_suffix | default(2) | string)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
configuration_luks_tpm2_pcrs_raw: >-
|
||||||
|
{{ partitioning_luks_tpm2_pcrs | default(luks_tpm2_pcrs | default('')) }}
|
||||||
|
configuration_luks_tpm2_pcrs_effective_value: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
system_cfg.luks.tpm2.pcrs
|
configuration_luks_tpm2_pcrs_raw
|
||||||
if system_cfg.luks.tpm2.pcrs is string
|
if configuration_luks_tpm2_pcrs_raw is string
|
||||||
else (system_cfg.luks.tpm2.pcrs | map('string') | join('+'))
|
else (configuration_luks_tpm2_pcrs_raw | map('string') | join('+'))
|
||||||
)
|
)
|
||||||
| string
|
| string
|
||||||
| replace(',', '+')
|
| replace(',', '+')
|
||||||
| regex_replace('\\s+', '')
|
| regex_replace('\\s+', '')
|
||||||
| regex_replace('^\\+|\\+$', '')
|
| regex_replace('^\\+|\\+$', '')
|
||||||
}}
|
}}
|
||||||
_sb_pcr7_safe: >-
|
|
||||||
{{
|
|
||||||
system_cfg.features.secure_boot.enabled | bool
|
|
||||||
and system_cfg.type | default('virtual') != 'virtual'
|
|
||||||
}}
|
|
||||||
luks_tpm2_pcrs: >-
|
|
||||||
{{
|
|
||||||
_raw_pcrs
|
|
||||||
if _raw_pcrs | length > 0
|
|
||||||
else ('7' if (_sb_pcr7_safe | bool) else '')
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_luks_mapper_name: "{{ system_cfg.luks.mapper }}"
|
configuration_luks_mapper_name: "{{ configuration_luks_mapper_name_value }}"
|
||||||
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
|
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
|
||||||
configuration_luks_device: "{{ partitioning_luks_device }}"
|
configuration_luks_device: "{{ configuration_luks_device_value }}"
|
||||||
configuration_luks_options: "{{ system_cfg.luks.options }}"
|
configuration_luks_options: >-
|
||||||
|
{{ partitioning_luks_options | default(luks_options | default('discard,tries=3')) }}
|
||||||
configuration_luks_auto_method: >-
|
configuration_luks_auto_method: >-
|
||||||
{{
|
{{
|
||||||
(system_cfg.luks.auto | bool)
|
(partitioning_luks_auto_decrypt | default(luks_auto_decrypt | default(true)) | bool)
|
||||||
| ternary(
|
| ternary(
|
||||||
system_cfg.luks.method,
|
partitioning_luks_auto_decrypt_method | default(luks_auto_decrypt_method | default('tpm2')),
|
||||||
'manual'
|
'manual'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
configuration_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}"
|
configuration_luks_tpm2_device: >-
|
||||||
configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}"
|
{{ partitioning_luks_tpm2_device | default(luks_tpm2_device | default('auto')) }}
|
||||||
configuration_luks_keyfile_path: "/etc/cryptsetup-keys.d/{{ system_cfg.luks.mapper }}.key"
|
configuration_luks_tpm2_pcrs: "{{ configuration_luks_tpm2_pcrs_raw }}"
|
||||||
configuration_luks_tpm2_token_lib: >-
|
configuration_luks_tpm2_pcrs_effective: "{{ configuration_luks_tpm2_pcrs_effective_value }}"
|
||||||
{{
|
configuration_luks_keyfile_path: >-
|
||||||
'/usr/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-systemd-tpm2.so'
|
/etc/cryptsetup-keys.d/{{ configuration_luks_mapper_name_value }}.key
|
||||||
if os_family == 'Debian'
|
changed_when: false
|
||||||
else '/usr/lib64/cryptsetup/libcryptsetup-token-systemd-tpm2.so'
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Validate LUKS UUID is available
|
- name: Validate LUKS UUID is available
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
@@ -64,79 +66,68 @@
|
|||||||
when: configuration_luks_auto_method in ['tpm2', 'keyfile']
|
when: configuration_luks_auto_method in ['tpm2', 'keyfile']
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- configuration_luks_passphrase | length > 0
|
- configuration_luks_passphrase_effective | length > 0
|
||||||
fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt.
|
fail_msg: luks_passphrase (or partitioning_luks_passphrase) must be set for LUKS auto-decrypt.
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
- name: Detect TPM2 unlock method
|
- name: Enroll TPM2 for LUKS
|
||||||
ansible.builtin.include_tasks: encryption/initramfs_detect.yml
|
when: configuration_luks_auto_method == 'tpm2'
|
||||||
|
|
||||||
- name: Enroll TPM2 via systemd-cryptenroll
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('systemd-cryptenroll') == 'systemd-cryptenroll'
|
|
||||||
ansible.builtin.include_tasks: encryption/tpm2.yml
|
ansible.builtin.include_tasks: encryption/tpm2.yml
|
||||||
|
|
||||||
- name: Configure LUKS keyfile auto-decrypt
|
- name: Configure LUKS keyfile auto-decrypt
|
||||||
when: configuration_luks_auto_method == 'keyfile'
|
when: configuration_luks_auto_method == 'keyfile'
|
||||||
ansible.builtin.include_tasks: encryption/keyfile.yml
|
ansible.builtin.include_tasks: encryption/keyfile.yml
|
||||||
|
|
||||||
- name: Record final LUKS auto-decrypt method
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
configuration_luks_final_method: "{{ configuration_luks_auto_method }}"
|
|
||||||
|
|
||||||
- name: Report LUKS auto-decrypt configuration
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: "LUKS auto-decrypt method: {{ configuration_luks_final_method }}"
|
|
||||||
|
|
||||||
- name: Build LUKS parameters
|
- name: Build LUKS parameters
|
||||||
vars:
|
vars:
|
||||||
luks_keyfile_in_use: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
configuration_luks_keyfile_in_use_value: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
||||||
luks_option_list: >-
|
configuration_luks_option_list_value: >-
|
||||||
{{
|
{{
|
||||||
(configuration_luks_options | trim).split(',')
|
(configuration_luks_options | trim).split(',')
|
||||||
if configuration_luks_options | trim | length > 0
|
if configuration_luks_options | trim | length > 0
|
||||||
else []
|
else []
|
||||||
}}
|
}}
|
||||||
luks_tpm2_option_list: >-
|
configuration_luks_tpm2_option_list_value: >-
|
||||||
{{
|
{{
|
||||||
(configuration_luks_auto_method == 'tpm2' and (_tpm2_method | default('systemd-cryptenroll')) == 'systemd-cryptenroll')
|
(configuration_luks_auto_method == 'tpm2')
|
||||||
| ternary(
|
| ternary(
|
||||||
['tpm2-device=' + configuration_luks_tpm2_device]
|
['tpm2-device=' + configuration_luks_tpm2_device]
|
||||||
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
|
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective]
|
||||||
if configuration_luks_tpm2_pcrs | length > 0 else []),
|
if configuration_luks_tpm2_pcrs_effective | length > 0 else []),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
luks_crypttab_keyfile: "{{ configuration_luks_keyfile_path if luks_keyfile_in_use else 'none' }}"
|
configuration_luks_crypttab_keyfile_value: >-
|
||||||
luks_crypttab_options: >-
|
{{ configuration_luks_keyfile_path if configuration_luks_keyfile_in_use_value else 'none' }}
|
||||||
|
configuration_luks_crypttab_options_value: >-
|
||||||
{{
|
{{
|
||||||
(['luks'] + luks_option_list + luks_tpm2_option_list)
|
(['luks'] + configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value)
|
||||||
| join(',')
|
| join(',')
|
||||||
}}
|
}}
|
||||||
luks_rd_options: "{{ (luks_option_list + luks_tpm2_option_list) | join(',') }}"
|
configuration_luks_rd_options_value: >-
|
||||||
luks_kernel_args: >-
|
{{ (configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value) | join(',') }}
|
||||||
|
configuration_luks_kernel_args_value: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name]
|
['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name]
|
||||||
+ (
|
+ (
|
||||||
['rd.luks.options=' + configuration_luks_uuid + '=' + luks_rd_options]
|
['rd.luks.options=' + configuration_luks_uuid + '=' + configuration_luks_rd_options_value]
|
||||||
if luks_rd_options | length > 0 else []
|
if configuration_luks_rd_options_value | length > 0 else []
|
||||||
)
|
)
|
||||||
+ (
|
+ (
|
||||||
['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path]
|
['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path]
|
||||||
if luks_keyfile_in_use else []
|
if configuration_luks_keyfile_in_use_value else []
|
||||||
)
|
)
|
||||||
) | join(' ')
|
) | join(' ')
|
||||||
}}
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_luks_keyfile_in_use: "{{ luks_keyfile_in_use }}"
|
configuration_luks_keyfile_in_use: "{{ configuration_luks_keyfile_in_use_value }}"
|
||||||
configuration_luks_option_list: "{{ luks_option_list }}"
|
configuration_luks_option_list: "{{ configuration_luks_option_list_value }}"
|
||||||
configuration_luks_tpm2_option_list: "{{ luks_tpm2_option_list }}"
|
configuration_luks_tpm2_option_list: "{{ configuration_luks_tpm2_option_list_value }}"
|
||||||
configuration_luks_crypttab_keyfile: "{{ luks_crypttab_keyfile }}"
|
configuration_luks_crypttab_keyfile: "{{ configuration_luks_crypttab_keyfile_value }}"
|
||||||
configuration_luks_crypttab_options: "{{ luks_crypttab_options }}"
|
configuration_luks_crypttab_options: "{{ configuration_luks_crypttab_options_value }}"
|
||||||
configuration_luks_rd_options: "{{ luks_rd_options }}"
|
configuration_luks_rd_options: "{{ configuration_luks_rd_options_value }}"
|
||||||
configuration_luks_kernel_args: "{{ luks_kernel_args }}"
|
configuration_luks_kernel_args: "{{ configuration_luks_kernel_args_value }}"
|
||||||
|
|
||||||
- name: Remove LUKS keyfile if TPM2 auto-decrypt is active
|
- name: Remove LUKS keyfile if TPM2 auto-decrypt is active
|
||||||
when: configuration_luks_auto_method == 'tpm2'
|
when: configuration_luks_auto_method == 'tpm2'
|
||||||
@@ -144,16 +135,231 @@
|
|||||||
path: /mnt{{ configuration_luks_keyfile_path }}
|
path: /mnt{{ configuration_luks_keyfile_path }}
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Configure initramfs for LUKS
|
- name: Write crypttab entry
|
||||||
ansible.builtin.include_tasks: encryption/initramfs.yml
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/crypttab
|
||||||
|
regexp: "^{{ configuration_luks_mapper_name }}\\s"
|
||||||
|
line: >-
|
||||||
|
{{ configuration_luks_mapper_name }} UUID={{ configuration_luks_uuid }}
|
||||||
|
{{ configuration_luks_crypttab_keyfile }} {{ configuration_luks_crypttab_options }}
|
||||||
|
create: true
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
- name: Configure crypttab
|
- name: Ensure keyfile pattern for initramfs-tools
|
||||||
ansible.builtin.include_tasks: encryption/crypttab.yml
|
when:
|
||||||
|
- is_debian | default(false)
|
||||||
|
- configuration_luks_keyfile_in_use
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/cryptsetup-initramfs/conf-hook
|
||||||
|
regexp: '^KEYFILE_PATTERN='
|
||||||
|
line: 'KEYFILE_PATTERN=/etc/cryptsetup-keys.d/*.key'
|
||||||
|
create: true
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Configure mkinitcpio hooks for LUKS
|
||||||
|
when: os | lower == 'archlinux'
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/mkinitcpio.conf
|
||||||
|
regexp: '^HOOKS='
|
||||||
|
line: >-
|
||||||
|
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
|
||||||
|
block sd-encrypt lvm2 filesystems fsck)
|
||||||
|
|
||||||
|
- name: Read mkinitcpio configuration
|
||||||
|
when: os | lower == 'archlinux'
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /mnt/etc/mkinitcpio.conf
|
||||||
|
register: configuration_mkinitcpio_slurp
|
||||||
|
|
||||||
|
- name: Build mkinitcpio FILES list
|
||||||
|
when: os | lower == 'archlinux'
|
||||||
|
vars:
|
||||||
|
configuration_mkinitcpio_files_list_value: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
configuration_mkinitcpio_slurp.content | b64decode
|
||||||
|
| regex_findall('^FILES=\\(([^)]*)\\)', multiline=True)
|
||||||
|
| default([])
|
||||||
|
| first
|
||||||
|
| default('')
|
||||||
|
).split()
|
||||||
|
}}
|
||||||
|
configuration_mkinitcpio_files_list_new_value: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(configuration_mkinitcpio_files_list_value + [configuration_luks_keyfile_path])
|
||||||
|
if configuration_luks_keyfile_in_use
|
||||||
|
else (
|
||||||
|
configuration_mkinitcpio_files_list_value
|
||||||
|
| reject('equalto', configuration_luks_keyfile_path)
|
||||||
|
| list
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| unique
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_mkinitcpio_files_list_new: "{{ configuration_mkinitcpio_files_list_new_value }}"
|
||||||
|
|
||||||
|
- name: Configure mkinitcpio FILES list
|
||||||
|
when: os | lower == 'archlinux'
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/mkinitcpio.conf
|
||||||
|
regexp: '^FILES='
|
||||||
|
line: >-
|
||||||
|
FILES=({{
|
||||||
|
configuration_mkinitcpio_files_list_new | join(' ')
|
||||||
|
}})
|
||||||
|
|
||||||
|
- name: Ensure dracut config directory exists
|
||||||
|
when: is_rhel | default(false)
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/dracut.conf.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
- name: Configure dracut for LUKS
|
- name: Configure dracut for LUKS
|
||||||
when: _initramfs_generator | default('') == 'dracut'
|
when: is_rhel | default(false)
|
||||||
ansible.builtin.include_tasks: encryption/dracut.yml
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/dracut.conf.d/crypt.conf
|
||||||
|
content: |
|
||||||
|
add_dracutmodules+=" crypt "
|
||||||
|
{% if configuration_luks_keyfile_in_use %}
|
||||||
|
install_items+=" {{ configuration_luks_keyfile_path }} "
|
||||||
|
{% endif %}
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
- name: Configure GRUB for LUKS
|
- name: Read kernel cmdline defaults
|
||||||
when: _initramfs_generator | default('') != 'dracut' or os_family != 'RedHat'
|
when: is_rhel | default(false)
|
||||||
ansible.builtin.include_tasks: encryption/grub.yml
|
ansible.builtin.slurp:
|
||||||
|
src: /mnt/etc/kernel/cmdline
|
||||||
|
register: configuration_kernel_cmdline_slurp
|
||||||
|
|
||||||
|
- name: Build kernel cmdline with LUKS args
|
||||||
|
when: is_rhel | default(false)
|
||||||
|
vars:
|
||||||
|
configuration_kernel_cmdline_current_value: >-
|
||||||
|
{{ configuration_kernel_cmdline_slurp.content | b64decode | trim }}
|
||||||
|
configuration_kernel_cmdline_list_value: >-
|
||||||
|
{{
|
||||||
|
configuration_kernel_cmdline_current_value.split()
|
||||||
|
if configuration_kernel_cmdline_current_value | length > 0 else []
|
||||||
|
}}
|
||||||
|
configuration_kernel_cmdline_filtered_value: >-
|
||||||
|
{{
|
||||||
|
configuration_kernel_cmdline_list_value
|
||||||
|
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
configuration_kernel_cmdline_new_value: >-
|
||||||
|
{{
|
||||||
|
(configuration_kernel_cmdline_filtered_value + configuration_luks_kernel_args.split())
|
||||||
|
| unique
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_kernel_cmdline_new: "{{ configuration_kernel_cmdline_new_value }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Write kernel cmdline with LUKS args
|
||||||
|
when: is_rhel | default(false)
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/kernel/cmdline
|
||||||
|
mode: "0644"
|
||||||
|
content: "{{ configuration_kernel_cmdline_new }}\n"
|
||||||
|
|
||||||
|
- name: Find BLS entries
|
||||||
|
when: is_rhel | default(false)
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: /mnt/boot/loader/entries
|
||||||
|
patterns: "*.conf"
|
||||||
|
register: configuration_kernel_bls_entries
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Update BLS options with LUKS args
|
||||||
|
when:
|
||||||
|
- is_rhel | default(false)
|
||||||
|
- configuration_kernel_bls_entries.files | length > 0
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
regexp: '^options '
|
||||||
|
line: "options {{ configuration_kernel_cmdline_new }}"
|
||||||
|
loop: "{{ configuration_kernel_bls_entries.files }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
|
- name: Read grub defaults
|
||||||
|
when: not is_rhel | default(false)
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /mnt/etc/default/grub
|
||||||
|
register: configuration_grub_slurp
|
||||||
|
|
||||||
|
- name: Build grub command lines with LUKS args
|
||||||
|
when: not is_rhel | default(false)
|
||||||
|
vars:
|
||||||
|
configuration_grub_content_value: "{{ configuration_grub_slurp.content | b64decode }}"
|
||||||
|
configuration_grub_cmdline_linux_value: >-
|
||||||
|
{{
|
||||||
|
configuration_grub_content_value
|
||||||
|
| regex_findall('^GRUB_CMDLINE_LINUX=\"(.*)\"', multiline=True)
|
||||||
|
| default([])
|
||||||
|
| first
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
configuration_grub_cmdline_default_value: >-
|
||||||
|
{{
|
||||||
|
configuration_grub_content_value
|
||||||
|
| regex_findall('^GRUB_CMDLINE_LINUX_DEFAULT=\"(.*)\"', multiline=True)
|
||||||
|
| default([])
|
||||||
|
| first
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
configuration_grub_cmdline_linux_list_value: >-
|
||||||
|
{{
|
||||||
|
configuration_grub_cmdline_linux_value.split()
|
||||||
|
if configuration_grub_cmdline_linux_value | length > 0 else []
|
||||||
|
}}
|
||||||
|
configuration_grub_cmdline_default_list_value: >-
|
||||||
|
{{
|
||||||
|
configuration_grub_cmdline_default_value.split()
|
||||||
|
if configuration_grub_cmdline_default_value | length > 0 else []
|
||||||
|
}}
|
||||||
|
configuration_luks_kernel_args_list_value: "{{ configuration_luks_kernel_args.split() }}"
|
||||||
|
configuration_grub_cmdline_linux_new_value: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(
|
||||||
|
configuration_grub_cmdline_linux_list_value
|
||||||
|
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||||
|
| list
|
||||||
|
)
|
||||||
|
+ configuration_luks_kernel_args_list_value
|
||||||
|
)
|
||||||
|
| unique
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
configuration_grub_cmdline_default_new_value: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(
|
||||||
|
configuration_grub_cmdline_default_list_value
|
||||||
|
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||||
|
| list
|
||||||
|
)
|
||||||
|
+ configuration_luks_kernel_args_list_value
|
||||||
|
)
|
||||||
|
| unique
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_grub_content: "{{ configuration_grub_content_value }}"
|
||||||
|
configuration_grub_cmdline_linux: "{{ configuration_grub_cmdline_linux_value }}"
|
||||||
|
configuration_grub_cmdline_default: "{{ configuration_grub_cmdline_default_value }}"
|
||||||
|
configuration_grub_cmdline_linux_new: "{{ configuration_grub_cmdline_linux_new_value }}"
|
||||||
|
configuration_grub_cmdline_default_new: "{{ configuration_grub_cmdline_default_new_value }}"
|
||||||
|
|
||||||
|
- name: Update GRUB_CMDLINE_LINUX_DEFAULT for LUKS
|
||||||
|
when: not is_rhel | default(false)
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/default/grub
|
||||||
|
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT='
|
||||||
|
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"'
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Write crypttab entry
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/crypttab
|
|
||||||
regexp: "^{{ configuration_luks_mapper_name }}\\s"
|
|
||||||
line: >-
|
|
||||||
{{ configuration_luks_mapper_name }} UUID={{ configuration_luks_uuid }}
|
|
||||||
{{ configuration_luks_crypttab_keyfile }} {{ configuration_luks_crypttab_options }}
|
|
||||||
create: true
|
|
||||||
mode: "0600"
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Ensure dracut config directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/dracut.conf.d
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Configure dracut for LUKS
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/dracut.conf.d/crypt.conf
|
|
||||||
content: |
|
|
||||||
add_dracutmodules+=" crypt systemd "
|
|
||||||
{% if configuration_luks_keyfile_in_use | default(false) %}
|
|
||||||
install_items+=" {{ configuration_luks_keyfile_path }} "
|
|
||||||
{% endif %}
|
|
||||||
{% if configuration_luks_auto_method == 'tpm2' %}
|
|
||||||
install_items+=" {{ configuration_luks_tpm2_token_lib | default('') }} "
|
|
||||||
{% endif %}
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
# --- Kernel cmdline: write rd.luks.* args for dracut ---
|
|
||||||
- name: Ensure kernel cmdline directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/kernel
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Read existing kernel cmdline
|
|
||||||
ansible.builtin.slurp:
|
|
||||||
src: /mnt/etc/kernel/cmdline
|
|
||||||
register: _kernel_cmdline_slurp
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Build kernel cmdline with LUKS args
|
|
||||||
vars:
|
|
||||||
_cmdline_current: >-
|
|
||||||
{{ (_kernel_cmdline_slurp.content | default('') | b64decode | default('')) | trim }}
|
|
||||||
_cmdline_list: >-
|
|
||||||
{{ _cmdline_current.split() if _cmdline_current | length > 0 else [] }}
|
|
||||||
_cmdline_filtered: >-
|
|
||||||
{{
|
|
||||||
_cmdline_list
|
|
||||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
_cmdline_new: >-
|
|
||||||
{{
|
|
||||||
(_cmdline_filtered + configuration_luks_kernel_args.split())
|
|
||||||
| unique
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_dracut_kernel_cmdline: "{{ _cmdline_new }}"
|
|
||||||
|
|
||||||
- name: Write kernel cmdline with LUKS args
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/kernel/cmdline
|
|
||||||
mode: "0644"
|
|
||||||
content: "{{ _dracut_kernel_cmdline }}\n"
|
|
||||||
|
|
||||||
# --- BLS entries: RedHat-specific ---
|
|
||||||
- name: Update BLS entries with LUKS kernel cmdline
|
|
||||||
when: os_family == 'RedHat'
|
|
||||||
vars:
|
|
||||||
_bls_cmdline: "{{ _dracut_kernel_cmdline }}"
|
|
||||||
ansible.builtin.include_tasks: ../_bls_update.yml
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Read grub defaults
|
|
||||||
ansible.builtin.slurp:
|
|
||||||
src: /mnt/etc/default/grub
|
|
||||||
register: configuration_grub_slurp
|
|
||||||
|
|
||||||
- name: Build grub command lines with LUKS args
|
|
||||||
vars:
|
|
||||||
grub_content: "{{ configuration_grub_slurp.content | b64decode }}"
|
|
||||||
grub_cmdline_linux: >-
|
|
||||||
{{
|
|
||||||
grub_content
|
|
||||||
| regex_findall('^GRUB_CMDLINE_LINUX=\"(.*)\"', multiline=True)
|
|
||||||
| default([])
|
|
||||||
| first
|
|
||||||
| default('')
|
|
||||||
}}
|
|
||||||
grub_cmdline_default: >-
|
|
||||||
{{
|
|
||||||
grub_content
|
|
||||||
| regex_findall('^GRUB_CMDLINE_LINUX_DEFAULT=\"(.*)\"', multiline=True)
|
|
||||||
| default([])
|
|
||||||
| first
|
|
||||||
| default('')
|
|
||||||
}}
|
|
||||||
grub_cmdline_linux_list: >-
|
|
||||||
{{
|
|
||||||
grub_cmdline_linux.split()
|
|
||||||
if grub_cmdline_linux | length > 0 else []
|
|
||||||
}}
|
|
||||||
grub_cmdline_default_list: >-
|
|
||||||
{{
|
|
||||||
grub_cmdline_default.split()
|
|
||||||
if grub_cmdline_default | length > 0 else []
|
|
||||||
}}
|
|
||||||
luks_kernel_args_list: "{{ configuration_luks_kernel_args.split() }}"
|
|
||||||
grub_cmdline_linux_new: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(
|
|
||||||
grub_cmdline_linux_list
|
|
||||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
|
||||||
| list
|
|
||||||
)
|
|
||||||
+ luks_kernel_args_list
|
|
||||||
)
|
|
||||||
| unique
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
grub_cmdline_default_new: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(
|
|
||||||
grub_cmdline_default_list
|
|
||||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
|
||||||
| list
|
|
||||||
)
|
|
||||||
+ luks_kernel_args_list
|
|
||||||
)
|
|
||||||
| unique
|
|
||||||
| join(' ')
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
configuration_grub_content: "{{ grub_content }}"
|
|
||||||
configuration_grub_cmdline_linux: "{{ grub_cmdline_linux }}"
|
|
||||||
configuration_grub_cmdline_default: "{{ grub_cmdline_default }}"
|
|
||||||
configuration_grub_cmdline_linux_new: "{{ grub_cmdline_linux_new }}"
|
|
||||||
configuration_grub_cmdline_default_new: "{{ grub_cmdline_default_new }}"
|
|
||||||
|
|
||||||
- name: Update GRUB_CMDLINE_LINUX_DEFAULT for LUKS
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/default/grub
|
|
||||||
regexp: "^GRUB_CMDLINE_LINUX_DEFAULT="
|
|
||||||
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"'
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
---
|
|
||||||
# Initramfs configuration for LUKS auto-unlock.
|
|
||||||
# Runs AFTER Build LUKS parameters (so configuration_luks_keyfile_in_use is set).
|
|
||||||
# _initramfs_generator and _tpm2_method are set by initramfs_detect.yml.
|
|
||||||
|
|
||||||
# --- clevis: install and bind TPM2 ---
|
|
||||||
- name: Install clevis in target system
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} apt install -y clevis clevis-luks clevis-tpm2 clevis-initramfs tpm2-tools
|
|
||||||
register: _clevis_install_result
|
|
||||||
changed_when: _clevis_install_result.rc == 0
|
|
||||||
|
|
||||||
- name: Install clevis on installer for LUKS binding
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
community.general.pacman:
|
|
||||||
name:
|
|
||||||
- clevis
|
|
||||||
- tpm2-tools
|
|
||||||
state: present
|
|
||||||
retries: 3
|
|
||||||
delay: 5
|
|
||||||
|
|
||||||
- name: Create clevis passphrase file
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/root/.luks-enroll-key
|
|
||||||
content: "{{ configuration_luks_passphrase }}"
|
|
||||||
mode: "0600"
|
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Ensure TPM device accessible for clevis
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
ansible.builtin.shell: >-
|
|
||||||
ls /mnt/dev/tpmrm0 2>/dev/null
|
|
||||||
|| (ls /dev/tpmrm0 && cp -a /dev/tpmrm0 /mnt/dev/tpmrm0)
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Bind LUKS to TPM2 via clevis
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
vars:
|
|
||||||
_clevis_config: >-
|
|
||||||
{{
|
|
||||||
'{"pcr_ids":"' + configuration_luks_tpm2_pcrs + '"}'
|
|
||||||
if configuration_luks_tpm2_pcrs | length > 0
|
|
||||||
else '{}'
|
|
||||||
}}
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
clevis luks bind -f -k /mnt/root/.luks-enroll-key
|
|
||||||
-d {{ configuration_luks_device }} tpm2 '{{ _clevis_config }}'
|
|
||||||
register: _clevis_bind_result
|
|
||||||
changed_when: _clevis_bind_result.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
# Initramfs regeneration is handled by the bootloader task which runs after
|
|
||||||
# encryption configuration. Clevis hooks are included automatically by
|
|
||||||
# update-initramfs when clevis-initramfs is installed.
|
|
||||||
|
|
||||||
- name: Remove clevis passphrase file
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/root/.luks-enroll-key
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Report clevis binding result
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method | default('') == 'clevis'
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: >-
|
|
||||||
{{ 'Clevis TPM2 binding succeeded' if (_clevis_bind_result.rc | default(1)) == 0
|
|
||||||
else 'Clevis TPM2 binding failed: ' + (_clevis_bind_result.stderr | default('unknown')) + '. System will require passphrase at boot.' }}
|
|
||||||
|
|
||||||
# --- initramfs-tools: keyfile support (non-TPM2) ---
|
|
||||||
- name: Configure initramfs-tools keyfile pattern
|
|
||||||
when:
|
|
||||||
- _initramfs_generator | default('') == 'initramfs-tools'
|
|
||||||
- configuration_luks_keyfile_in_use | default(false) | bool
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/cryptsetup-initramfs/conf-hook
|
|
||||||
regexp: "^KEYFILE_PATTERN="
|
|
||||||
line: "KEYFILE_PATTERN=/etc/cryptsetup-keys.d/*.key"
|
|
||||||
create: true
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
# --- mkinitcpio: systemd + sd-encrypt hooks ---
|
|
||||||
- name: Configure mkinitcpio hooks for LUKS
|
|
||||||
when: _initramfs_generator | default('') == 'mkinitcpio'
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/mkinitcpio.conf
|
|
||||||
regexp: "^HOOKS="
|
|
||||||
line: >-
|
|
||||||
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
|
|
||||||
block sd-encrypt{{ ' lvm2' if system_cfg.filesystem != 'btrfs' else '' }} filesystems fsck)
|
|
||||||
|
|
||||||
- name: Read mkinitcpio configuration
|
|
||||||
when: _initramfs_generator | default('') == 'mkinitcpio'
|
|
||||||
ansible.builtin.slurp:
|
|
||||||
src: /mnt/etc/mkinitcpio.conf
|
|
||||||
register: configuration_mkinitcpio_slurp
|
|
||||||
|
|
||||||
- name: Build mkinitcpio FILES list
|
|
||||||
when: _initramfs_generator | default('') == 'mkinitcpio'
|
|
||||||
vars:
|
|
||||||
mkinitcpio_files_list: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
configuration_mkinitcpio_slurp.content | b64decode
|
|
||||||
| regex_findall('^FILES=\\(([^)]*)\\)', multiline=True)
|
|
||||||
| default([])
|
|
||||||
| first
|
|
||||||
| default('')
|
|
||||||
).split()
|
|
||||||
}}
|
|
||||||
mkinitcpio_files_list_new: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(mkinitcpio_files_list + [configuration_luks_keyfile_path])
|
|
||||||
if (configuration_luks_keyfile_in_use | default(false))
|
|
||||||
else (
|
|
||||||
mkinitcpio_files_list
|
|
||||||
| reject('equalto', configuration_luks_keyfile_path)
|
|
||||||
| list
|
|
||||||
)
|
|
||||||
)
|
|
||||||
| unique
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
configuration_mkinitcpio_files_list_new: "{{ mkinitcpio_files_list_new }}"
|
|
||||||
|
|
||||||
- name: Configure mkinitcpio FILES list
|
|
||||||
when: _initramfs_generator | default('') == 'mkinitcpio'
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/mkinitcpio.conf
|
|
||||||
regexp: "^FILES="
|
|
||||||
line: >-
|
|
||||||
FILES=({{
|
|
||||||
configuration_mkinitcpio_files_list_new | join(' ')
|
|
||||||
}})
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
---
|
|
||||||
# Resolve initramfs generator and TPM2 unlock method.
|
|
||||||
# Sets _initramfs_generator and _tpm2_method facts.
|
|
||||||
#
|
|
||||||
# Generator detection: derived from the platform's initramfs_cmd
|
|
||||||
# (dracut → dracut, mkinitcpio → mkinitcpio, else → initramfs-tools)
|
|
||||||
# TPM2 method: systemd-cryptenroll when generator supports tpm2-device,
|
|
||||||
# clevis fallback otherwise. Non-native dracut installed automatically.
|
|
||||||
|
|
||||||
- name: Resolve initramfs generator
|
|
||||||
vars:
|
|
||||||
_user_generator: "{{ system_cfg.features.initramfs.generator | default('') }}"
|
|
||||||
_native_generator: >-
|
|
||||||
{{
|
|
||||||
'dracut' if _configuration_platform.initramfs_cmd is search('dracut')
|
|
||||||
else ('mkinitcpio' if _configuration_platform.initramfs_cmd is search('mkinitcpio')
|
|
||||||
else 'initramfs-tools')
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_initramfs_generator: >-
|
|
||||||
{{ _user_generator if _user_generator | length > 0 else _native_generator }}
|
|
||||||
_initramfs_native_generator: "{{ _native_generator }}"
|
|
||||||
|
|
||||||
# --- Install non-native dracut if overridden or needed ---
|
|
||||||
- name: Install dracut in chroot when not native
|
|
||||||
when:
|
|
||||||
- _initramfs_generator == 'dracut'
|
|
||||||
- _initramfs_native_generator != 'dracut'
|
|
||||||
ansible.builtin.shell: >-
|
|
||||||
{{ chroot_command }} sh -c '
|
|
||||||
command -v apt >/dev/null 2>&1 && apt install -y dracut ||
|
|
||||||
command -v pacman >/dev/null 2>&1 && pacman -S --noconfirm dracut ||
|
|
||||||
command -v dnf >/dev/null 2>&1 && dnf install -y dracut
|
|
||||||
'
|
|
||||||
register: _dracut_install_result
|
|
||||||
changed_when: _dracut_install_result.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Override initramfs command to dracut
|
|
||||||
when:
|
|
||||||
- _initramfs_generator == 'dracut'
|
|
||||||
- _initramfs_native_generator != 'dracut'
|
|
||||||
vars:
|
|
||||||
# Generate dracut initramfs with output name matching what GRUB expects:
|
|
||||||
# mkinitcpio native: /boot/initramfs-linux.img (Arch convention)
|
|
||||||
# initramfs-tools native: /boot/initrd.img-<kver> (Debian convention)
|
|
||||||
_dracut_cmd: >-
|
|
||||||
{{
|
|
||||||
'bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initramfs-linux.img $kver; done"'
|
|
||||||
if _initramfs_native_generator == 'mkinitcpio'
|
|
||||||
else 'bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initrd.img-$kver $kver; done"'
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_configuration_platform: >-
|
|
||||||
{{ _configuration_platform | combine({'initramfs_cmd': _dracut_cmd}) }}
|
|
||||||
|
|
||||||
# --- TPM2 method detection ---
|
|
||||||
- name: Probe dracut for TPM2 module support
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _initramfs_generator != 'mkinitcpio'
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} dracut --list-modules"
|
|
||||||
register: _dracut_modules_check
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Resolve TPM2 unlock method
|
|
||||||
when: configuration_luks_auto_method == 'tpm2'
|
|
||||||
vars:
|
|
||||||
# mkinitcpio sd-encrypt supports tpm2-device natively
|
|
||||||
# dracut with tpm2-tss module supports tpm2-device natively
|
|
||||||
# everything else needs clevis
|
|
||||||
_supports_tpm2_native: >-
|
|
||||||
{{
|
|
||||||
_initramfs_generator == 'mkinitcpio'
|
|
||||||
or ('tpm2-tss' in (_dracut_modules_check.stdout | default('')))
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_tpm2_method: "{{ 'systemd-cryptenroll' if _supports_tpm2_native | bool else 'clevis' }}"
|
|
||||||
|
|
||||||
# --- Auto-upgrade to dracut when tpm2-tss available but generator isn't dracut ---
|
|
||||||
- name: Switch to dracut for TPM2 support
|
|
||||||
when:
|
|
||||||
- configuration_luks_auto_method == 'tpm2'
|
|
||||||
- _tpm2_method == 'systemd-cryptenroll'
|
|
||||||
- _initramfs_generator not in ['dracut', 'mkinitcpio']
|
|
||||||
vars:
|
|
||||||
_dracut_cmd: >-
|
|
||||||
bash -c "for kver in /lib/modules/*/; do kver=$(basename $kver); dracut --force /boot/initrd.img-$kver $kver; done"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_initramfs_generator: dracut
|
|
||||||
_configuration_platform: >-
|
|
||||||
{{ _configuration_platform | combine({'initramfs_cmd': _dracut_cmd}) }}
|
|
||||||
|
|
||||||
- name: Report TPM2 configuration
|
|
||||||
when: configuration_luks_auto_method == 'tpm2'
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: "TPM2 unlock: {{ _tpm2_method | default('none') }} | initramfs: {{ _initramfs_generator }}"
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
{{
|
{{
|
||||||
lookup(
|
lookup(
|
||||||
'community.general.random_string',
|
'community.general.random_string',
|
||||||
length=(system_cfg.luks.keysize | int),
|
length=(partitioning_luks_keyfile_size | default(luks_keyfile_size | default(64)) | int),
|
||||||
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
when: configuration_luks_keyfile_unlock_test.rc != 0
|
when: configuration_luks_keyfile_unlock_test.rc != 0
|
||||||
community.crypto.luks_device:
|
community.crypto.luks_device:
|
||||||
device: "{{ configuration_luks_device }}"
|
device: "{{ configuration_luks_device }}"
|
||||||
passphrase: "{{ configuration_luks_passphrase }}"
|
passphrase: "{{ configuration_luks_passphrase_effective }}"
|
||||||
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
register: configuration_luks_addkey_result
|
register: configuration_luks_addkey_result
|
||||||
failed_when: false
|
failed_when: false
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
- name: Regenerate keyfile and retry adding to LUKS header
|
- name: Regenerate keyfile and retry adding to LUKS header
|
||||||
when:
|
when:
|
||||||
- configuration_luks_keyfile_unlock_test.rc != 0
|
- configuration_luks_keyfile_unlock_test.rc != 0
|
||||||
- configuration_luks_keyfile_copy is defined and configuration_luks_keyfile_copy.changed | bool
|
- configuration_luks_keyfile_copy.changed | default(false) | bool
|
||||||
- configuration_luks_addkey_result is failed
|
- configuration_luks_addkey_result is failed
|
||||||
block:
|
block:
|
||||||
- name: Regenerate LUKS keyfile
|
- name: Regenerate LUKS keyfile
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
{{
|
{{
|
||||||
lookup(
|
lookup(
|
||||||
'community.general.random_string',
|
'community.general.random_string',
|
||||||
length=(system_cfg.luks.keysize | int),
|
length=(partitioning_luks_keyfile_size | default(luks_keyfile_size | default(64)) | int),
|
||||||
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -84,8 +84,9 @@
|
|||||||
- name: Retry adding keyfile to LUKS header
|
- name: Retry adding keyfile to LUKS header
|
||||||
community.crypto.luks_device:
|
community.crypto.luks_device:
|
||||||
device: "{{ configuration_luks_device }}"
|
device: "{{ configuration_luks_device }}"
|
||||||
passphrase: "{{ configuration_luks_passphrase }}"
|
passphrase: "{{ configuration_luks_passphrase_effective }}"
|
||||||
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
|
register: configuration_luks_addkey_retry
|
||||||
failed_when: false
|
failed_when: false
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
@@ -103,13 +104,6 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
- name: Warn about keyfile enrollment failure
|
|
||||||
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: >-
|
|
||||||
LUKS keyfile enrollment failed — falling back to manual unlock at boot.
|
|
||||||
The system will prompt for the LUKS passphrase during startup.
|
|
||||||
|
|
||||||
- name: Fallback to manual LUKS unlock if keyfile enrollment failed
|
- name: Fallback to manual LUKS unlock if keyfile enrollment failed
|
||||||
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
|
|||||||
@@ -1,35 +1,25 @@
|
|||||||
---
|
---
|
||||||
# TPM2 enrollment via systemd-cryptenroll.
|
|
||||||
# Works with dracut and mkinitcpio (sd-encrypt). The user-set passphrase
|
|
||||||
# remains as a backup unlock method — no auto-generated keyfiles.
|
|
||||||
- name: Enroll TPM2 for LUKS
|
- name: Enroll TPM2 for LUKS
|
||||||
block:
|
block:
|
||||||
- name: Create temporary passphrase file for TPM2 enrollment
|
- name: Create temporary passphrase file for TPM2 enrollment
|
||||||
ansible.builtin.tempfile:
|
ansible.builtin.tempfile:
|
||||||
path: /mnt/root
|
path: /mnt/tmp
|
||||||
prefix: luks-passphrase-
|
prefix: luks-passphrase-
|
||||||
state: file
|
state: file
|
||||||
register: _tpm2_passphrase_tempfile
|
register: configuration_luks_tpm2_passphrase_tempfile
|
||||||
|
|
||||||
- name: Write passphrase into temporary file
|
- name: Write passphrase into temporary file for TPM2 enrollment
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ _tpm2_passphrase_tempfile.path }}"
|
dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
||||||
content: "{{ configuration_luks_passphrase }}"
|
content: "{{ configuration_luks_passphrase_effective }}"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
- name: Ensure TPM device is accessible in chroot
|
- name: Enroll TPM2 token
|
||||||
ansible.builtin.shell: >-
|
|
||||||
ls /mnt/dev/tpmrm0 2>/dev/null
|
|
||||||
|| (ls /dev/tpmrm0 && cp -a /dev/tpmrm0 /mnt/dev/tpmrm0)
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Enroll TPM2 token via systemd-cryptenroll
|
|
||||||
vars:
|
vars:
|
||||||
_enroll_args: >-
|
configuration_luks_enroll_args: >-
|
||||||
{{
|
{{
|
||||||
[
|
[
|
||||||
'/usr/bin/systemd-cryptenroll',
|
'/usr/bin/systemd-cryptenroll',
|
||||||
@@ -37,28 +27,64 @@
|
|||||||
'--tpm2-with-pin=false',
|
'--tpm2-with-pin=false',
|
||||||
'--wipe-slot=tpm2',
|
'--wipe-slot=tpm2',
|
||||||
'--unlock-key-file=' + (
|
'--unlock-key-file=' + (
|
||||||
_tpm2_passphrase_tempfile.path | regex_replace('^/mnt', '')
|
configuration_luks_tpm2_passphrase_tempfile.path
|
||||||
|
| regex_replace('^/mnt', '')
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
|
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective]
|
||||||
if configuration_luks_tpm2_pcrs | length > 0 else [])
|
if configuration_luks_tpm2_pcrs_effective | length > 0 else [])
|
||||||
+ [configuration_luks_device]
|
+ [configuration_luks_device]
|
||||||
}}
|
}}
|
||||||
ansible.builtin.command: "{{ chroot_command }} {{ _enroll_args | join(' ') }}"
|
configuration_luks_enroll_chroot_args: "{{ ['arch-chroot', '/mnt'] + configuration_luks_enroll_args }}"
|
||||||
register: _tpm2_enroll_result
|
ansible.builtin.command:
|
||||||
changed_when: _tpm2_enroll_result.rc == 0
|
argv: "{{ configuration_luks_enroll_chroot_args }}"
|
||||||
|
register: configuration_luks_tpm2_enroll_chroot
|
||||||
|
changed_when: configuration_luks_tpm2_enroll_chroot.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Retry TPM2 enrollment in installer environment
|
||||||
|
when:
|
||||||
|
- (configuration_luks_tpm2_enroll_chroot.rc | default(1)) != 0
|
||||||
|
vars:
|
||||||
|
configuration_luks_enroll_args: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
'/usr/bin/systemd-cryptenroll',
|
||||||
|
'--tpm2-device=' + configuration_luks_tpm2_device,
|
||||||
|
'--tpm2-with-pin=false',
|
||||||
|
'--wipe-slot=tpm2',
|
||||||
|
'--unlock-key-file=' + configuration_luks_tpm2_passphrase_tempfile.path
|
||||||
|
]
|
||||||
|
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective]
|
||||||
|
if configuration_luks_tpm2_pcrs_effective | length > 0 else [])
|
||||||
|
+ [configuration_luks_device]
|
||||||
|
}}
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: "{{ configuration_luks_enroll_args }}"
|
||||||
|
register: configuration_luks_tpm2_enroll_host
|
||||||
|
changed_when: configuration_luks_tpm2_enroll_host.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Validate TPM2 enrollment succeeded
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- >-
|
||||||
|
(configuration_luks_tpm2_enroll_chroot.rc | default(1)) == 0
|
||||||
|
or (configuration_luks_tpm2_enroll_host.rc | default(1)) == 0
|
||||||
|
fail_msg: >-
|
||||||
|
TPM2 enrollment failed.
|
||||||
|
chroot rc={{ configuration_luks_tpm2_enroll_chroot.rc | default('n/a') }},
|
||||||
|
host rc={{ configuration_luks_tpm2_enroll_host.rc | default('n/a') }},
|
||||||
|
chroot stderr={{ configuration_luks_tpm2_enroll_chroot.stderr | default('') }},
|
||||||
|
host stderr={{ configuration_luks_tpm2_enroll_host.stderr | default('') }}
|
||||||
rescue:
|
rescue:
|
||||||
- name: TPM2 enrollment failed
|
- name: Fallback to keyfile auto-decrypt
|
||||||
ansible.builtin.debug:
|
ansible.builtin.set_fact:
|
||||||
msg: >-
|
configuration_luks_auto_method: keyfile
|
||||||
TPM2 enrollment failed: {{ _tpm2_enroll_result.stderr | default('unknown') }}.
|
|
||||||
The system will require the passphrase for LUKS unlock on boot.
|
|
||||||
TPM2 can be enrolled post-deployment via: systemd-cryptenroll --tpm2-device=auto {{ configuration_luks_device }}
|
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: Remove temporary passphrase file
|
- name: Remove TPM2 enrollment passphrase file
|
||||||
when: _tpm2_passphrase_tempfile.path is defined
|
when: configuration_luks_tpm2_passphrase_tempfile.path is defined
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ _tpm2_passphrase_tempfile.path }}"
|
path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
changed_when: false
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
- name: Append vim configurations to vimrc
|
- name: Append vim configurations to vimrc
|
||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: "{{ '/mnt/etc/vim/vimrc' if os_family == 'Debian' else '/mnt/etc/vimrc' }}"
|
path: "{{ '/mnt/etc/vim/vimrc' if is_debian | default(false) else '/mnt/etc/vimrc' }}"
|
||||||
block: |
|
block: |
|
||||||
set encoding=utf-8
|
set encoding=utf-8
|
||||||
set number
|
set number
|
||||||
@@ -9,11 +9,9 @@
|
|||||||
set smartindent
|
set smartindent
|
||||||
set mouse=a
|
set mouse=a
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
marker: "\" {mark} CUSTOM VIM CONFIG"
|
marker: ""
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
# Tuned for VM workloads: low swappiness, aggressive writeback, large page-cluster
|
|
||||||
# for zram. Override post-bootstrap via the linux role or sysctl if needed.
|
|
||||||
- name: Add memory tuning parameters
|
- name: Add memory tuning parameters
|
||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /mnt/etc/sysctl.d/90-memory.conf
|
path: /mnt/etc/sysctl.d/90-memory.conf
|
||||||
@@ -24,26 +22,48 @@
|
|||||||
vm.dirty_background_ratio=1
|
vm.dirty_background_ratio=1
|
||||||
vm.dirty_ratio=10
|
vm.dirty_ratio=10
|
||||||
vm.page-cluster=10
|
vm.page-cluster=10
|
||||||
marker: "# {mark} MEMORY TUNING"
|
marker: ""
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Create zram config
|
- name: Create zram config
|
||||||
when:
|
when: os | lower not in ['debian11', 'rhel8']
|
||||||
- (os != "debian" or (os_version | string) != "11") and os != "rhel"
|
|
||||||
- os not in ["alpine", "void"]
|
|
||||||
- system_cfg.features.swap.enabled | bool
|
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: /mnt/etc/systemd/zram-generator.conf
|
dest: /mnt/etc/systemd/zram-generator.conf
|
||||||
content: |
|
content: |
|
||||||
[zram0]
|
[zram0]
|
||||||
zram-size = ram / 2
|
zram-size = ram / 2
|
||||||
compression-algorithm = {{ 'zstd' if system_cfg.features.zstd.enabled | bool else 'lz4' }}
|
compression-algorithm = zstd
|
||||||
swap-priority = 100
|
swap-priority = 100
|
||||||
fs-type = swap
|
fs-type = swap
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Copy Custom Shell config
|
- name: Copy Custom Shell config
|
||||||
ansible.builtin.copy:
|
ansible.builtin.template:
|
||||||
src: custom.sh
|
src: custom.sh.j2
|
||||||
dest: /mnt/etc/profile.d/custom.sh
|
dest: /mnt/etc/profile.d/custom.sh
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Create login banner
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ item }}"
|
||||||
|
content: |
|
||||||
|
**************************************************************
|
||||||
|
* WARNING: Unauthorized access to this system is prohibited. *
|
||||||
|
* All activities are monitored and logged. *
|
||||||
|
* Disconnect immediately if you are not an authorized user. *
|
||||||
|
**************************************************************
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/issue
|
||||||
|
- /mnt/etc/issue.net
|
||||||
|
|
||||||
|
- name: Remove motd files
|
||||||
|
when: os | lower in ["rhel8", "rhel9", "rhel10"]
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/motd.d/cockpit
|
||||||
|
- /mnt/etc/motd.d/insights-client
|
||||||
|
|||||||
@@ -16,44 +16,30 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Adjust XFS mount options and disable large extent
|
- name: Remove deprecated attr2 and disable large extent
|
||||||
when: os in ["almalinux", "rocky", "rhel"] and system_cfg.filesystem == "xfs"
|
when: os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"] and filesystem == "xfs"
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: /mnt/etc/fstab
|
path: /mnt/etc/fstab
|
||||||
regexp: "(xfs.*?)(attr2)"
|
regexp: "(xfs.*?)(attr2)"
|
||||||
replace: "\\1allocsize=64m"
|
replace: '\1allocsize=64m'
|
||||||
|
|
||||||
- name: Remove RHEL ISO fstab entry when not using local repo
|
|
||||||
when:
|
|
||||||
- os == "rhel"
|
|
||||||
- system_cfg.features.rhel_repo.source != "iso"
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/fstab
|
|
||||||
regexp: "^.*\\/dvd.*$"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
||||||
when:
|
when: os in ["rhel8", "rhel9", "rhel10"]
|
||||||
- os == "rhel"
|
|
||||||
- system_cfg.features.rhel_repo.source == "iso"
|
|
||||||
vars:
|
vars:
|
||||||
configuration_fstab_dvd_line: >-
|
configuration_fstab_dvd_line: >-
|
||||||
{{
|
{{
|
||||||
'/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0'
|
'/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0'
|
||||||
if hypervisor_type == 'vmware'
|
if hypervisor == 'vmware'
|
||||||
else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0'
|
else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0'
|
||||||
}}
|
}}
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/etc/fstab
|
path: /mnt/etc/fstab
|
||||||
regexp: "^.*\\/dvd.*$"
|
regexp: '^.*\/dvd.*$'
|
||||||
line: "{{ configuration_fstab_dvd_line }}"
|
line: "{{ configuration_fstab_dvd_line }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Write image from RHEL ISO to the target machine
|
- name: Write image from RHEL ISO to the target machine
|
||||||
when:
|
when: os in ["rhel8", "rhel9", "rhel10"] and hypervisor == 'vmware'
|
||||||
- os == "rhel"
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- system_cfg.features.rhel_repo.source == "iso"
|
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- dd
|
- dd
|
||||||
@@ -71,10 +57,9 @@
|
|||||||
line: "{{ fstab_entry.line }}"
|
line: "{{ fstab_entry.line }}"
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
loop:
|
loop:
|
||||||
- { regexp: "^# TempFS$", line: "# TempFS" }
|
- {regexp: '^# TempFS$', line: '# TempFS'}
|
||||||
- { regexp: "^tmpfs\\s+/tmp\\s+", line: "tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
- {regexp: '^tmpfs\\s+/tmp\\s+', line: 'tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0'}
|
||||||
- { regexp: "^tmpfs\\s+/var/tmp\\s+", line: "tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
- {regexp: '^tmpfs\\s+/var/tmp\\s+', line: 'tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec 0 0'}
|
||||||
- { regexp: "^tmpfs\\s+/dev/shm\\s+", line: "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
- {regexp: '^tmpfs\\s+/dev/shm\\s+', line: 'tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0'}
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: fstab_entry
|
loop_var: fstab_entry
|
||||||
label: "{{ fstab_entry.regexp }}"
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Configure grub defaults
|
- name: Configure grub defaults
|
||||||
when: os_family != 'RedHat'
|
when: not is_rhel | default(false)
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: /mnt/etc/default/grub
|
dest: /mnt/etc/default/grub
|
||||||
regexp: "{{ item.regexp }}"
|
regexp: "{{ item.regexp }}"
|
||||||
@@ -10,58 +10,50 @@
|
|||||||
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
||||||
- regexp: ^GRUB_TIMEOUT=
|
- regexp: ^GRUB_TIMEOUT=
|
||||||
line: GRUB_TIMEOUT=1
|
line: GRUB_TIMEOUT=1
|
||||||
loop_control:
|
|
||||||
label: "{{ item.line }}"
|
|
||||||
|
|
||||||
- name: Ensure grub defaults file exists for RHEL-based systems
|
- name: Ensure grub defaults file exists for RHEL-based systems
|
||||||
when: os_family == 'RedHat'
|
when: is_rhel | default(false)
|
||||||
block:
|
block:
|
||||||
- name: Build RHEL kernel command line defaults
|
- name: Build RHEL kernel command line defaults
|
||||||
vars:
|
vars:
|
||||||
grub_root_uuid: >-
|
configuration_grub_root_uuid_value: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
partitioning_main_uuid.stdout
|
partitioning_main_uuid.stdout
|
||||||
if system_cfg.filesystem == 'btrfs'
|
if (filesystem | lower) == 'btrfs'
|
||||||
else (partitioning_uuid_root | default([]) | first | default(''))
|
else (partitioning_uuid_root | default([]) | first | default(''))
|
||||||
)
|
)
|
||||||
| default('')
|
| default('')
|
||||||
| trim
|
| trim
|
||||||
}}
|
}}
|
||||||
grub_lvm_args: >-
|
configuration_grub_lvm_args_value: >-
|
||||||
{{
|
{{
|
||||||
(
|
['resume=/dev/mapper/sys-swap', 'rd.lvm.lv=sys/root', 'rd.lvm.lv=sys/swap']
|
||||||
['rd.lvm.lv=sys/root']
|
if (filesystem | lower) != 'btrfs'
|
||||||
+ (
|
|
||||||
['rd.lvm.lv=sys/swap', 'resume=/dev/mapper/sys-swap']
|
|
||||||
if system_cfg.features.swap.enabled | bool
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if system_cfg.filesystem != 'btrfs'
|
|
||||||
else []
|
else []
|
||||||
}}
|
}}
|
||||||
grub_root_flags: >-
|
configuration_grub_root_flags_value: >-
|
||||||
{{ ['rootflags=subvol=@'] if system_cfg.filesystem == 'btrfs' else [] }}
|
{{ ['rootflags=subvol=@'] if (filesystem | lower) == 'btrfs' else [] }}
|
||||||
grub_cmdline_linux_base: >-
|
configuration_grub_cmdline_linux_base_value: >-
|
||||||
{{
|
{{
|
||||||
(['crashkernel=auto'] + grub_lvm_args)
|
(['crashkernel=auto'] + configuration_grub_lvm_args_value)
|
||||||
| join(' ')
|
| join(' ')
|
||||||
}}
|
}}
|
||||||
grub_kernel_cmdline_base: >-
|
configuration_grub_kernel_cmdline_base_value: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
(['root=UUID=' + grub_root_uuid]
|
(['root=UUID=' + configuration_grub_root_uuid_value]
|
||||||
if grub_root_uuid | length > 0 else [])
|
if configuration_grub_root_uuid_value | length > 0 else [])
|
||||||
+ ['ro', 'crashkernel=auto']
|
+ ['ro', 'crashkernel=auto']
|
||||||
+ grub_lvm_args
|
+ configuration_grub_lvm_args_value
|
||||||
+ grub_root_flags
|
+ configuration_grub_root_flags_value
|
||||||
)
|
)
|
||||||
| join(' ')
|
| join(' ')
|
||||||
}}
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_grub_cmdline_linux_base: "{{ grub_cmdline_linux_base }}"
|
configuration_grub_cmdline_linux_base: "{{ configuration_grub_cmdline_linux_base_value }}"
|
||||||
configuration_kernel_cmdline_base: "{{ grub_kernel_cmdline_base }}"
|
configuration_kernel_cmdline_base: "{{ configuration_grub_kernel_cmdline_base_value }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Check if grub defaults file exists
|
- name: Check if grub defaults file exists
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
@@ -96,14 +88,27 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: "{{ configuration_kernel_cmdline_base }}\n"
|
content: "{{ configuration_kernel_cmdline_base }}\n"
|
||||||
|
|
||||||
- name: Update BLS entries with kernel cmdline defaults
|
- name: Find BLS entries
|
||||||
vars:
|
ansible.builtin.find:
|
||||||
_bls_cmdline: "{{ configuration_kernel_cmdline_base }}"
|
paths: /mnt/boot/loader/entries
|
||||||
ansible.builtin.include_tasks: _bls_update.yml
|
patterns: "*.conf"
|
||||||
|
register: configuration_grub_bls_entries
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Update BLS options with kernel cmdline defaults
|
||||||
|
when: configuration_grub_bls_entries.files | length > 0
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
regexp: '^options '
|
||||||
|
line: "options {{ configuration_kernel_cmdline_base }}"
|
||||||
|
loop: "{{ configuration_grub_bls_entries.files }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Enable GRUB cryptodisk for encrypted /boot
|
- name: Enable GRUB cryptodisk for encrypted /boot
|
||||||
when: partitioning_grub_enable_cryptodisk | bool
|
when:
|
||||||
|
- partitioning_luks_enabled | default(luks_enabled | default(false)) | bool
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/etc/default/grub
|
path: /mnt/etc/default/grub
|
||||||
regexp: "^GRUB_ENABLE_CRYPTODISK="
|
regexp: '^GRUB_ENABLE_CRYPTODISK='
|
||||||
line: GRUB_ENABLE_CRYPTODISK=y
|
line: GRUB_ENABLE_CRYPTODISK=y
|
||||||
|
|||||||
@@ -1,69 +1,63 @@
|
|||||||
---
|
---
|
||||||
- name: Reload systemd in installer environment
|
|
||||||
when: ansible_service_mgr == 'systemd'
|
|
||||||
ansible.builtin.systemd:
|
|
||||||
daemon_reload: true
|
|
||||||
|
|
||||||
- name: Set local timezone
|
- name: Set local timezone
|
||||||
ansible.builtin.file:
|
ansible.builtin.command: "{{ item }}"
|
||||||
src: /usr/share/zoneinfo/{{ system_cfg.timezone }}
|
loop:
|
||||||
dest: /mnt/etc/localtime
|
- systemctl daemon-reload
|
||||||
state: link
|
- arch-chroot /mnt ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
|
||||||
force: true
|
register: configuration_timezone_result
|
||||||
|
changed_when: configuration_timezone_result.rc == 0
|
||||||
|
|
||||||
- name: Setup locales
|
- name: Setup locales
|
||||||
block:
|
block:
|
||||||
- name: Configure locale.gen
|
- name: Configure locale.gen
|
||||||
when: _configuration_platform.locale_gen
|
when: not is_rhel | default(false)
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
dest: /mnt/etc/locale.gen
|
dest: /mnt/etc/locale.gen
|
||||||
regexp: "{{ item.regex }}"
|
regexp: "{{ item.regex }}"
|
||||||
line: "{{ item.line }}"
|
line: "{{ item.line }}"
|
||||||
loop:
|
loop:
|
||||||
- { regex: "{{ system_cfg.locale }} UTF-8", line: "{{ system_cfg.locale }} UTF-8" }
|
- {regex: en_US\.UTF-8 UTF-8, line: en_US.UTF-8 UTF-8}
|
||||||
loop_control:
|
|
||||||
label: "{{ item.line }}"
|
|
||||||
|
|
||||||
- name: Generate locales
|
- name: Generate locales
|
||||||
when: _configuration_platform.locale_gen
|
when: not is_rhel | default(false)
|
||||||
ansible.builtin.command: "{{ chroot_command }} /usr/sbin/locale-gen"
|
ansible.builtin.command: arch-chroot /mnt /usr/sbin/locale-gen
|
||||||
register: configuration_locale_result
|
register: configuration_locale_result
|
||||||
changed_when: configuration_locale_result.rc == 0
|
changed_when: configuration_locale_result.rc == 0
|
||||||
|
|
||||||
- name: Compute hostname variables
|
- name: Set hostname
|
||||||
ansible.builtin.set_fact:
|
vars:
|
||||||
configuration_dns_domain: >-
|
|
||||||
{{ (system_cfg.network.dns.search | default([]) | first | default('')) | string }}
|
|
||||||
configuration_hostname_fqdn: >-
|
configuration_hostname_fqdn: >-
|
||||||
{{
|
{{
|
||||||
hostname
|
hostname
|
||||||
if '.' in hostname
|
if '.' in hostname
|
||||||
else (
|
else (
|
||||||
hostname + '.' + (system_cfg.network.dns.search | default([]) | first | default('') | string)
|
hostname + '.' + vm_dns_search
|
||||||
if (system_cfg.network.dns.search | default([]) | first | default('') | string) | length > 0
|
if vm_dns_search is defined and vm_dns_search | length
|
||||||
else hostname
|
else hostname
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
- name: Set hostname
|
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: "{{ configuration_hostname_fqdn.split('.')[0] }}"
|
content: "{{ configuration_hostname_fqdn }}"
|
||||||
dest: /mnt/etc/hostname
|
dest: /mnt/etc/hostname
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Add host entry to /etc/hosts
|
- name: Add host entry to /etc/hosts
|
||||||
vars:
|
vars:
|
||||||
|
configuration_hostname_fqdn: >-
|
||||||
|
{{
|
||||||
|
hostname
|
||||||
|
if '.' in hostname
|
||||||
|
else (
|
||||||
|
hostname + '.' + vm_dns_search
|
||||||
|
if vm_dns_search is defined and vm_dns_search | length
|
||||||
|
else hostname
|
||||||
|
)
|
||||||
|
}}
|
||||||
configuration_hostname_short: "{{ hostname.split('.')[0] }}"
|
configuration_hostname_short: "{{ hostname.split('.')[0] }}"
|
||||||
configuration_hostname_entries: >-
|
configuration_hostname_entries: >-
|
||||||
{{ [configuration_hostname_fqdn, configuration_hostname_short] | unique | join(' ') }}
|
{{ [configuration_hostname_fqdn, configuration_hostname_short] | unique | join(' ') }}
|
||||||
configuration_hosts_ip: >-
|
|
||||||
{{
|
|
||||||
system_cfg.network.ip
|
|
||||||
if system_cfg.network.ip is defined and (system_cfg.network.ip | string | length) > 0
|
|
||||||
else inventory_hostname
|
|
||||||
}}
|
|
||||||
configuration_hosts_line: >-
|
configuration_hosts_line: >-
|
||||||
{{ configuration_hosts_ip }} {{ configuration_hostname_entries }}
|
{{ vm_ip | default(inventory_hostname) }} {{ configuration_hostname_entries }}
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/etc/hosts
|
path: /mnt/etc/hosts
|
||||||
line: "{{ configuration_hosts_line }}"
|
line: "{{ configuration_hosts_line }}"
|
||||||
@@ -71,12 +65,24 @@
|
|||||||
|
|
||||||
- name: Create vconsole.conf
|
- name: Create vconsole.conf
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: "KEYMAP={{ system_cfg.keymap }}"
|
content: KEYMAP=us
|
||||||
dest: /mnt/etc/vconsole.conf
|
dest: /mnt/etc/vconsole.conf
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Create locale.conf
|
- name: Create locale.conf
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: "LANG={{ system_cfg.locale }}"
|
content: LANG=en_US.UTF-8
|
||||||
dest: /mnt/etc/locale.conf
|
dest: /mnt/etc/locale.conf
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Ensure SSH password authentication is enabled
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/ssh/sshd_config
|
||||||
|
regexp: "^#?PasswordAuthentication\\s+"
|
||||||
|
line: "PasswordAuthentication yes"
|
||||||
|
|
||||||
|
- name: SSH permit root login
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: /mnt/etc/ssh/sshd_config
|
||||||
|
regexp: "^#?PermitRootLogin.*"
|
||||||
|
replace: "PermitRootLogin yes"
|
||||||
|
|||||||
@@ -1,30 +1,17 @@
|
|||||||
---
|
---
|
||||||
- name: Resolve platform configuration
|
|
||||||
ansible.builtin.import_tasks: _resolve_platform.yml
|
|
||||||
|
|
||||||
- name: Include configuration tasks
|
- name: Include configuration tasks
|
||||||
when: configuration_task.when | default(true)
|
ansible.builtin.include_tasks: "{{ configuration_task }}"
|
||||||
ansible.builtin.include_tasks: "{{ configuration_task.file }}"
|
|
||||||
loop:
|
loop:
|
||||||
- file: repositories.yml
|
- fstab.yml
|
||||||
when: "{{ os_family == 'Debian' }}"
|
- locales.yml
|
||||||
- file: banner.yml
|
- services.yml
|
||||||
- file: fstab.yml
|
- grub.yml
|
||||||
- file: locales.yml
|
- encryption.yml
|
||||||
- file: ssh.yml
|
- bootloader.yml
|
||||||
- file: services.yml
|
- extras.yml
|
||||||
- file: grub.yml
|
- network.yml
|
||||||
- file: encryption.yml
|
- users.yml
|
||||||
when: "{{ system_cfg.luks.enabled | bool }}"
|
- sudo.yml
|
||||||
- file: bootloader.yml
|
- selinux.yml
|
||||||
- file: secure_boot.yml
|
|
||||||
when: "{{ system_cfg.features.secure_boot.enabled | bool }}"
|
|
||||||
- file: extras.yml
|
|
||||||
- file: network.yml
|
|
||||||
- file: users.yml
|
|
||||||
- file: sudo.yml
|
|
||||||
- file: selinux.yml
|
|
||||||
when: "{{ os_family == 'RedHat' }}"
|
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: configuration_task
|
loop_var: configuration_task
|
||||||
label: "{{ configuration_task.file }}"
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
- name: Generate UUID for Network Profile
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_net_uuid: "{{ ('LAN-' ~ hostname) | ansible.builtin.to_uuid }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Read network interfaces
|
- name: Read network interfaces
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
@@ -10,29 +15,82 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Detect available network interface names
|
- name: Resolve network interface and MAC address
|
||||||
vars:
|
vars:
|
||||||
configuration_detected_interfaces: >-
|
configuration_net_inf_from_facts: "{{ (ansible_default_ipv4 | default({})).get('interface', '') }}"
|
||||||
|
configuration_net_inf_from_ip: >-
|
||||||
{{
|
{{
|
||||||
configuration_ip_link.stdout
|
(
|
||||||
|
configuration_ip_link.stdout
|
||||||
|
| default('')
|
||||||
|
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
||||||
|
| reject('equalto', 'lo')
|
||||||
|
| list
|
||||||
|
| first
|
||||||
|
)
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
configuration_net_inf_effective: >-
|
||||||
|
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
|
||||||
|
configuration_net_inf_regex: "{{ configuration_net_inf_effective | ansible.builtin.regex_escape }}"
|
||||||
|
configuration_net_mac_from_virtualization: "{{ virtualization_mac_address | default('') }}"
|
||||||
|
configuration_net_mac_from_facts: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(ansible_facts | default({})).get(configuration_net_inf_effective, {}).get('macaddress', '')
|
||||||
|
)
|
||||||
|
| default(
|
||||||
|
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_effective, {}).get('macaddress', ''),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
configuration_net_mac_from_ip: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
configuration_ip_link.stdout
|
||||||
|
| default('')
|
||||||
|
| regex_findall(
|
||||||
|
'^\\d+: ' ~ configuration_net_inf_regex ~ ':.*?link/ether\\s+([0-9A-Fa-f:]{17})',
|
||||||
|
multiline=True
|
||||||
|
)
|
||||||
|
| first
|
||||||
|
)
|
||||||
| default('')
|
| default('')
|
||||||
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
|
||||||
| reject('equalto', 'lo')
|
|
||||||
| list
|
|
||||||
}}
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_detected_interfaces: "{{ configuration_detected_interfaces }}"
|
configuration_net_inf: "{{ configuration_net_inf_effective }}"
|
||||||
|
configuration_net_mac: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
configuration_net_mac_from_virtualization
|
||||||
|
| default(configuration_net_mac_from_facts, true)
|
||||||
|
| default(configuration_net_mac_from_ip, true)
|
||||||
|
)
|
||||||
|
| upper
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Validate at least one network interface detected
|
- name: Validate Network Interface Name
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- configuration_detected_interfaces | length > 0
|
- configuration_net_inf | length > 0
|
||||||
fail_msg: Failed to detect any network interfaces.
|
fail_msg: Failed to detect an active network interface.
|
||||||
|
|
||||||
- name: Set DNS configuration facts
|
- name: Validate Network Interface MAC Address
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.assert:
|
||||||
configuration_dns_list: "{{ system_cfg.network.dns.servers }}"
|
that:
|
||||||
configuration_dns_search: "{{ system_cfg.network.dns.search }}"
|
- configuration_net_mac | length > 0
|
||||||
|
fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}.
|
||||||
|
|
||||||
- name: Configure networking
|
- name: Copy NetworkManager keyfile
|
||||||
ansible.builtin.include_tasks: "{{ configuration_network_task_map[os] | default('network_nm.yml') }}"
|
ansible.builtin.template:
|
||||||
|
src: network.j2
|
||||||
|
dest: /mnt/etc/NetworkManager/system-connections/LAN.nmconnection
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Fix Ubuntu unmanaged devices
|
||||||
|
when: os | lower in ["ubuntu", "ubuntu-lts"]
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf
|
||||||
|
state: touch
|
||||||
|
mode: "0644"
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Write Alpine network interfaces
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/network/interfaces
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
auto lo
|
|
||||||
iface lo inet loopback
|
|
||||||
{% for iface in system_cfg.network.interfaces %}
|
|
||||||
{% set inv_name = iface.name | default('') | string %}
|
|
||||||
{% set det_name = configuration_detected_interfaces[loop.index0] | default('eth' ~ loop.index0) %}
|
|
||||||
{% set iface_name = inv_name if inv_name | length > 0 else det_name %}
|
|
||||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
|
||||||
|
|
||||||
auto {{ iface_name }}
|
|
||||||
iface {{ iface_name }} inet {{ 'static' if has_static else 'dhcp' }}
|
|
||||||
{% if has_static %}
|
|
||||||
address {{ iface.ip }}/{{ iface.prefix }}
|
|
||||||
{% if iface.gateway | default('') | string | length %}
|
|
||||||
gateway {{ iface.gateway }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
- name: Set Alpine DNS resolvers
|
|
||||||
when: configuration_dns_list | length > 0 or configuration_dns_search | length > 0
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
{% if configuration_dns_search | length > 0 %}
|
|
||||||
search {{ configuration_dns_search | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
{% for resolver in configuration_dns_list %}
|
|
||||||
nameserver {{ resolver }}
|
|
||||||
{% endfor %}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Copy NetworkManager keyfile per interface
|
|
||||||
vars:
|
|
||||||
configuration_iface: "{{ item }}"
|
|
||||||
configuration_iface_name: "{{ item.name | default(configuration_detected_interfaces[idx] | default('')) }}"
|
|
||||||
configuration_net_uuid: "{{ ('LAN-' ~ idx ~ '-' ~ hostname) | ansible.builtin.to_uuid }}"
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: network.j2
|
|
||||||
dest: "/mnt/etc/NetworkManager/system-connections/LAN-{{ idx }}.nmconnection"
|
|
||||||
mode: "0600"
|
|
||||||
loop: "{{ system_cfg.network.interfaces }}"
|
|
||||||
loop_control:
|
|
||||||
index_var: idx
|
|
||||||
label: "LAN-{{ idx }}"
|
|
||||||
|
|
||||||
- name: Fix Ubuntu unmanaged devices
|
|
||||||
when: os in ["ubuntu", "ubuntu-lts"]
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf
|
|
||||||
state: touch
|
|
||||||
mode: "0644"
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Write dhcpcd configuration
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/dhcpcd.conf
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
{% for iface in system_cfg.network.interfaces %}
|
|
||||||
{% set inv_name = iface.name | default('') | string %}
|
|
||||||
{% set det_name = configuration_detected_interfaces[loop.index0] | default('eth' ~ loop.index0) %}
|
|
||||||
{% set iface_name = inv_name if inv_name | length > 0 else det_name %}
|
|
||||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
|
||||||
{% if has_static %}
|
|
||||||
interface {{ iface_name }}
|
|
||||||
static ip_address={{ iface.ip }}/{{ iface.prefix }}
|
|
||||||
{% if iface.gateway | default('') | string | length %}
|
|
||||||
static routers={{ iface.gateway }}
|
|
||||||
{% endif %}
|
|
||||||
{% if loop.index0 == 0 and configuration_dns_list | length > 0 %}
|
|
||||||
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
{% if loop.index0 == 0 and configuration_dns_search | length > 0 %}
|
|
||||||
static domain_search={{ configuration_dns_search | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Write final sources.list
|
|
||||||
vars:
|
|
||||||
_debian_release_map:
|
|
||||||
"10": buster
|
|
||||||
"11": bullseye
|
|
||||||
"12": bookworm
|
|
||||||
"13": trixie
|
|
||||||
unstable: sid
|
|
||||||
_ubuntu_release_map:
|
|
||||||
ubuntu: questing
|
|
||||||
ubuntu-lts: noble
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: "{{ os | replace('-lts', '') }}.sources.list.j2"
|
|
||||||
dest: /mnt/etc/apt/sources.list
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Ensure apt performance configuration persists
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/apt/apt.conf.d/99performance
|
|
||||||
content: |
|
|
||||||
Acquire::Retries "3";
|
|
||||||
Acquire::http::Pipeline-Depth "10";
|
|
||||||
APT::Install-Recommends "false";
|
|
||||||
mode: "0644"
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Configure shim-based Secure Boot
|
|
||||||
when: os != 'archlinux'
|
|
||||||
ansible.builtin.include_tasks: secure_boot/shim.yml
|
|
||||||
|
|
||||||
- name: Configure sbctl Secure Boot
|
|
||||||
when: os == 'archlinux'
|
|
||||||
ansible.builtin.include_tasks: secure_boot/sbctl.yml
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Configure sbctl Secure Boot
|
|
||||||
block:
|
|
||||||
- name: Create Secure Boot signing keys
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} sbctl create-keys"
|
|
||||||
register: _sbctl_create_keys
|
|
||||||
changed_when: _sbctl_create_keys.rc == 0
|
|
||||||
failed_when:
|
|
||||||
- _sbctl_create_keys.rc != 0
|
|
||||||
- "'already exists' not in (_sbctl_create_keys.stderr | default(''))"
|
|
||||||
|
|
||||||
- name: Enroll Secure Boot keys in firmware
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} sbctl enroll-keys --microsoft"
|
|
||||||
register: _sbctl_enroll
|
|
||||||
changed_when: _sbctl_enroll.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Install first-boot enrollment service if chroot enrollment failed
|
|
||||||
when: _sbctl_enroll.rc | default(1) != 0
|
|
||||||
block:
|
|
||||||
- name: Create first-boot sbctl enrollment service
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/systemd/system/sbctl-enroll.service
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
[Unit]
|
|
||||||
Description=Enroll Secure Boot keys via sbctl
|
|
||||||
ConditionPathExists=!/var/lib/sbctl/.enrolled
|
|
||||||
After=local-fs.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/bin/sbctl enroll-keys --microsoft
|
|
||||||
ExecStartPost=/usr/bin/touch /var/lib/sbctl/.enrolled
|
|
||||||
RemainAfterExit=yes
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
||||||
- name: Enable first-boot enrollment service
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} systemctl enable sbctl-enroll.service"
|
|
||||||
register: _sbctl_service_enable
|
|
||||||
changed_when: _sbctl_service_enable.rc == 0
|
|
||||||
|
|
||||||
- name: Find kernel images to sign
|
|
||||||
ansible.builtin.find:
|
|
||||||
paths: /mnt/boot
|
|
||||||
patterns: "vmlinuz-*"
|
|
||||||
file_type: file
|
|
||||||
register: _sbctl_kernel_images
|
|
||||||
|
|
||||||
- name: Sign kernel images
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} sbctl sign -s {{ item.path | regex_replace('^/mnt', '') }}
|
|
||||||
loop: "{{ _sbctl_kernel_images.files }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.path | basename }}"
|
|
||||||
register: _sbctl_sign_kernel
|
|
||||||
changed_when: _sbctl_sign_kernel.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Sign GRUB EFI binary
|
|
||||||
vars:
|
|
||||||
_grub_efi_path: "{{ partitioning_efi_mountpoint }}/EFI/archlinux/grubx64.efi"
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} sbctl sign -s {{ _grub_efi_path }}
|
|
||||||
register: _sbctl_sign_grub
|
|
||||||
changed_when: _sbctl_sign_grub.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Ensure pacman hooks directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/pacman.d/hooks
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Install sbctl auto-signing pacman hook
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/pacman.d/hooks/99-sbctl-sign.hook
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
[Trigger]
|
|
||||||
Operation = Install
|
|
||||||
Operation = Upgrade
|
|
||||||
Type = Path
|
|
||||||
Target = boot/vmlinuz-*
|
|
||||||
Target = usr/lib/modules/*/vmlinuz
|
|
||||||
|
|
||||||
[Action]
|
|
||||||
Description = Signing kernel images for Secure Boot...
|
|
||||||
When = PostTransaction
|
|
||||||
Exec = /usr/bin/sbctl sign-all
|
|
||||||
Depends = sbctl
|
|
||||||
|
|
||||||
- name: Verify sbctl signing status
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} sbctl verify"
|
|
||||||
register: _sbctl_verify
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Report sbctl Secure Boot status
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: >-
|
|
||||||
Secure Boot (sbctl):
|
|
||||||
Enrollment={{ 'done' if (_sbctl_enroll.rc | default(1)) == 0 else 'deferred to first boot' }}.
|
|
||||||
{{ _sbctl_verify.stdout | default('Verify not available') }}
|
|
||||||
|
|
||||||
rescue:
|
|
||||||
- name: Secure Boot setup failed
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: >-
|
|
||||||
sbctl Secure Boot setup failed.
|
|
||||||
On VMs make sure the OVMF firmware is in Setup Mode (fresh NVRAM).
|
|
||||||
On bare metal enter the firmware setup and switch to Setup Mode first.
|
|
||||||
To recover manually: sbctl create-keys && sbctl enroll-keys --microsoft && sbctl sign-all
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Configure shim-based Secure Boot
|
|
||||||
vars:
|
|
||||||
_efi_vendor: >-
|
|
||||||
{{
|
|
||||||
"redhat" if os == "rhel"
|
|
||||||
else ("ubuntu" if os in ["ubuntu", "ubuntu-lts"] else os)
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Find shim binary in target system
|
|
||||||
ansible.builtin.shell:
|
|
||||||
cmd: >-
|
|
||||||
set -o pipefail &&
|
|
||||||
{{ chroot_command }} find /usr/lib/shim /boot/efi/EFI
|
|
||||||
\( -name 'shimx64.efi.signed.latest' -o -name 'shimx64.efi.dualsigned'
|
|
||||||
-o -name 'shimx64.efi.signed' -o -name 'shimx64.efi' \)
|
|
||||||
-type f | sort -r | head -1
|
|
||||||
executable: /bin/bash
|
|
||||||
register: _shim_find_result
|
|
||||||
changed_when: false
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Copy shim to EFI vendor directory
|
|
||||||
when:
|
|
||||||
- _shim_find_result.stdout | default('') | length > 0
|
|
||||||
- _configuration_platform.grub_install | bool
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
cp /mnt{{ _shim_find_result.stdout_lines | first }}
|
|
||||||
/mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/shimx64.efi
|
|
||||||
register: _shim_copy_result
|
|
||||||
changed_when: _shim_copy_result.rc == 0
|
|
||||||
|
|
||||||
- name: Verify shim is present
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "/mnt{{ partitioning_efi_mountpoint }}/EFI/{{ _efi_vendor }}/shimx64.efi"
|
|
||||||
register: _shim_stat
|
|
||||||
|
|
||||||
- name: Report Secure Boot status
|
|
||||||
ansible.builtin.debug:
|
|
||||||
msg: >-
|
|
||||||
Secure Boot (shim): {{
|
|
||||||
'shimx64.efi installed at ' ~ partitioning_efi_mountpoint ~ '/EFI/' ~ _efi_vendor
|
|
||||||
if (_shim_stat.stat.exists | default(false))
|
|
||||||
else 'shimx64.efi not found, shim package may handle placement on first boot'
|
|
||||||
}}
|
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Fix SELinux
|
- name: Fix SELinux
|
||||||
when: os_family == 'RedHat'
|
when: is_rhel | default(false)
|
||||||
block:
|
block:
|
||||||
- name: Fix SELinux by pre-labeling the filesystem before first boot
|
- name: Fix SELinux by pre-labeling the filesystem before first boot
|
||||||
when: os in ['almalinux', 'rocky', 'rhel'] and system_cfg.features.selinux.enabled | bool
|
when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rhel10', 'rocky'] and (selinux | default(true) | bool)
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
{{ chroot_command }} /sbin/setfiles -v -F
|
arch-chroot /mnt /sbin/setfiles -v -F
|
||||||
-e /dev -e /proc -e /sys -e /run
|
-e /dev -e /proc -e /sys -e /run
|
||||||
/etc/selinux/targeted/contexts/files/file_contexts /
|
/etc/selinux/targeted/contexts/files/file_contexts /
|
||||||
register: configuration_setfiles_result
|
register: configuration_setfiles_result
|
||||||
changed_when: configuration_setfiles_result.rc == 0
|
changed_when: configuration_setfiles_result.rc == 0
|
||||||
|
|
||||||
# Fedora: setfiles segfaults during bootstrap chroot relabeling, so SELinux
|
|
||||||
# is left permissive and expected to relabel on first boot.
|
|
||||||
- name: Disable SELinux
|
- name: Disable SELinux
|
||||||
when: os == "fedora" or not system_cfg.features.selinux.enabled | bool
|
when: os | lower == "fedora" or not (selinux | default(true) | bool)
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /mnt/etc/selinux/config
|
path: /mnt/etc/selinux/config
|
||||||
regexp: ^SELINUX=
|
regexp: ^SELINUX=
|
||||||
|
|||||||
@@ -1,105 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: Enable systemd services
|
- name: Enable Systemd Services
|
||||||
when: _configuration_platform.init_system == 'systemd'
|
ansible.builtin.command: >
|
||||||
vars:
|
arch-chroot /mnt systemctl enable NetworkManager
|
||||||
_desktop_dm: >-
|
{{
|
||||||
{{
|
' ssh' if os | lower in ['ubuntu', 'ubuntu-lts'] else
|
||||||
system_cfg.features.desktop.display_manager
|
(' sshd' if os | lower not in ['debian11', 'debian12', 'debian13'] else '')
|
||||||
if (system_cfg.features.desktop.display_manager | length > 0)
|
}}
|
||||||
else (configuration_desktop_dm_map[system_cfg.features.desktop.environment] | default(''))
|
{{
|
||||||
}}
|
'logrotate systemd-resolved systemd-timesyncd systemd-networkd'
|
||||||
configuration_systemd_services: >-
|
if os | lower == 'archlinux' else ''
|
||||||
{{
|
}}
|
||||||
['NetworkManager']
|
register: configuration_enable_services_result
|
||||||
+ (['firewalld'] if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else [])
|
changed_when: configuration_enable_services_result.rc == 0
|
||||||
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
+ ([_configuration_platform.ssh_service] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else [])
|
|
||||||
+ ([_desktop_dm] if system_cfg.features.desktop.enabled | bool and _desktop_dm | length > 0 else [])
|
|
||||||
+ (['bluetooth'] if system_cfg.features.desktop.enabled | bool else [])
|
|
||||||
}}
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
|
|
||||||
loop: "{{ configuration_systemd_services }}"
|
|
||||||
register: configuration_enable_service_result
|
|
||||||
changed_when: configuration_enable_service_result.rc == 0
|
|
||||||
|
|
||||||
- name: Activate UFW firewall
|
|
||||||
when:
|
|
||||||
- system_cfg.features.firewall.backend == 'ufw'
|
|
||||||
- system_cfg.features.firewall.enabled | bool
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} ufw --force enable"
|
|
||||||
register: _ufw_enable_result
|
|
||||||
changed_when: _ufw_enable_result.rc == 0
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Set default systemd target to graphical
|
|
||||||
when:
|
|
||||||
- _configuration_platform.init_system == 'systemd'
|
|
||||||
- system_cfg.features.desktop.enabled | bool
|
|
||||||
ansible.builtin.command: "{{ chroot_command }} systemctl set-default graphical.target"
|
|
||||||
register: _desktop_target_result
|
|
||||||
changed_when: _desktop_target_result.rc == 0
|
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
|
||||||
when: _configuration_platform.init_system == 'openrc'
|
|
||||||
vars:
|
|
||||||
configuration_openrc_services: >-
|
|
||||||
{{
|
|
||||||
['networking']
|
|
||||||
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Ensure OpenRC runlevel directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/runlevels/default
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Check OpenRC init scripts
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "/mnt/etc/init.d/{{ item }}"
|
|
||||||
loop: "{{ configuration_openrc_services }}"
|
|
||||||
register: configuration_openrc_service_stats
|
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: "/mnt/etc/init.d/{{ item.item }}"
|
|
||||||
dest: "/mnt/etc/runlevels/default/{{ item.item }}"
|
|
||||||
state: link
|
|
||||||
loop: "{{ configuration_openrc_service_stats.results }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.item }}"
|
|
||||||
when: item.stat.exists
|
|
||||||
|
|
||||||
- name: Enable runit services
|
|
||||||
when: _configuration_platform.init_system == 'runit'
|
|
||||||
vars:
|
|
||||||
configuration_runit_services: >-
|
|
||||||
{{
|
|
||||||
['dhcpcd']
|
|
||||||
+ (['sshd'] if system_cfg.features.ssh.enabled | bool else [])
|
|
||||||
+ ([system_cfg.features.firewall.backend] if system_cfg.features.firewall.enabled | bool else [])
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Ensure runit service directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/var/service
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Check runit service definitions
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "/mnt/etc/sv/{{ item }}"
|
|
||||||
loop: "{{ configuration_runit_services }}"
|
|
||||||
register: configuration_runit_service_stats
|
|
||||||
|
|
||||||
- name: Enable runit services
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: "/mnt/etc/sv/{{ item.item }}"
|
|
||||||
dest: "/mnt/var/service/{{ item.item }}"
|
|
||||||
state: link
|
|
||||||
loop: "{{ configuration_runit_service_stats.results }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.item }}"
|
|
||||||
when: item.stat.exists
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
# Bootstrap-only: permissive SSH for initial Ansible access.
|
|
||||||
# Post-bootstrap hardening (key-only, no root login) is handled by the linux role.
|
|
||||||
- name: Ensure SSH password authentication is enabled
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
regexp: "^#?PasswordAuthentication\\s+"
|
|
||||||
line: "PasswordAuthentication yes"
|
|
||||||
|
|
||||||
- name: SSH permit root login
|
|
||||||
ansible.builtin.replace:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
regexp: "^#?PermitRootLogin.*"
|
|
||||||
replace: "PermitRootLogin yes"
|
|
||||||
@@ -1,29 +1,7 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure sudoers.d directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/sudoers.d
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
|
|
||||||
- name: Give sudo access to wheel group
|
- name: Give sudo access to wheel group
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: "{{ _configuration_platform.sudo_group }} ALL=(ALL) ALL\n"
|
content: "{{ '%sudo ALL=(ALL) ALL' if is_debian | default(false) else '%wheel ALL=(ALL) ALL' }}"
|
||||||
dest: /mnt/etc/sudoers.d/01-wheel
|
dest: /mnt/etc/sudoers.d/01-wheel
|
||||||
mode: "0440"
|
mode: "0440"
|
||||||
validate: /usr/sbin/visudo --check --file=%s
|
validate: /usr/sbin/visudo --check --file=%s
|
||||||
|
|
||||||
- name: Deploy per-user sudoers rules
|
|
||||||
when: item.value.sudo is defined and (item.value.sudo | string | length > 0)
|
|
||||||
vars:
|
|
||||||
configuration_sudoers_rule: >-
|
|
||||||
{{ item.value.sudo if item.value.sudo is string else 'ALL=(ALL) NOPASSWD: ALL' }}
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: "{{ item.key }} {{ configuration_sudoers_rule }}\n"
|
|
||||||
dest: "/mnt/etc/sudoers.d/{{ item.key }}"
|
|
||||||
mode: "0440"
|
|
||||||
validate: /usr/sbin/visudo --check --file=%s
|
|
||||||
loop: "{{ system_cfg.users | dict2items }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.key }}"
|
|
||||||
|
|||||||
@@ -1,68 +1,37 @@
|
|||||||
---
|
---
|
||||||
- name: Set root password
|
- name: Create user account
|
||||||
when: (system_cfg.root.password | default('') | string | length) > 0
|
|
||||||
ansible.builtin.shell: >-
|
|
||||||
set -o pipefail &&
|
|
||||||
echo 'root:{{ system_cfg.root.password | password_hash("sha512") }}' | {{ chroot_command }} /usr/sbin/chpasswd -e
|
|
||||||
args:
|
|
||||||
executable: /bin/bash
|
|
||||||
register: configuration_root_result
|
|
||||||
changed_when: configuration_root_result.rc == 0
|
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Lock root account when no password is set
|
|
||||||
when: (system_cfg.root.password | default('') | string | length) == 0
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} /usr/bin/passwd -l root
|
|
||||||
register: configuration_root_lock_result
|
|
||||||
changed_when: configuration_root_lock_result.rc == 0
|
|
||||||
|
|
||||||
- name: Set root shell
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
{{ chroot_command }} /usr/sbin/usermod --shell {{ system_cfg.root.shell }} root
|
|
||||||
register: configuration_root_shell_result
|
|
||||||
changed_when: configuration_root_shell_result.rc == 0
|
|
||||||
|
|
||||||
- name: Create user accounts
|
|
||||||
vars:
|
vars:
|
||||||
configuration_user_group: "{{ _configuration_platform.user_group }}"
|
configuration_user_group: >-
|
||||||
|
{{ "sudo" if is_debian | default(false) else "wheel" }}
|
||||||
configuration_useradd_cmd: >-
|
configuration_useradd_cmd: >-
|
||||||
{{ chroot_command }} /usr/sbin/useradd --create-home --user-group
|
arch-chroot /mnt /usr/sbin/useradd --create-home --user-group
|
||||||
--uid {{ 1000 + _idx }}
|
--groups {{ configuration_user_group }} {{ user_name }}
|
||||||
--groups {{ configuration_user_group }} {{ item.key }}
|
--password {{ user_password | password_hash('sha512') }} --shell /bin/bash
|
||||||
{{ ('--password ' ~ (item.value.password | password_hash('sha512'))) if (item.value.password | default('') | string | length > 0) else '' }}
|
configuration_root_cmd: >-
|
||||||
--shell {{ item.value.shell | default('/bin/bash') }}
|
arch-chroot /mnt /usr/sbin/usermod --password
|
||||||
ansible.builtin.command: "{{ configuration_useradd_cmd }}"
|
'{{ root_password | password_hash('sha512') }}' root --shell /bin/bash
|
||||||
loop: "{{ system_cfg.users | dict2items }}"
|
ansible.builtin.command: "{{ item }}"
|
||||||
loop_control:
|
loop:
|
||||||
index_var: _idx
|
- "{{ configuration_useradd_cmd }}"
|
||||||
label: "{{ item.key }}"
|
- "{{ configuration_root_cmd }}"
|
||||||
register: configuration_user_result
|
register: configuration_user_result
|
||||||
changed_when: configuration_user_result.rc == 0
|
changed_when: configuration_user_result.rc == 0
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Ensure .ssh directory exists
|
- name: Ensure .ssh directory exists
|
||||||
when: (item.value['keys'] | default([]) | length) > 0
|
when: user_public_key is defined
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/mnt/home/{{ item.key }}/.ssh"
|
path: /mnt/home/{{ user_name }}/.ssh
|
||||||
state: directory
|
state: directory
|
||||||
owner: "{{ 1000 + _idx }}"
|
owner: 1000
|
||||||
group: "{{ 1000 + _idx }}"
|
group: 1000
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
loop: "{{ system_cfg.users | dict2items }}"
|
|
||||||
loop_control:
|
|
||||||
index_var: _idx
|
|
||||||
label: "{{ item.key }}"
|
|
||||||
|
|
||||||
- name: Deploy SSH authorized_keys
|
- name: Add SSH public key to authorized_keys
|
||||||
when: (item.value['keys'] | default([]) | length) > 0
|
when: user_public_key is defined
|
||||||
ansible.builtin.copy:
|
ansible.builtin.lineinfile:
|
||||||
content: "{{ item.value['keys'] | join('\n') }}\n"
|
path: /mnt/home/{{ user_name }}/.ssh/authorized_keys
|
||||||
dest: "/mnt/home/{{ item.key }}/.ssh/authorized_keys"
|
line: "{{ user_public_key }}"
|
||||||
owner: "{{ 1000 + _idx }}"
|
owner: 1000
|
||||||
group: "{{ 1000 + _idx }}"
|
group: 1000
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
loop: "{{ system_cfg.users | dict2items }}"
|
create: true
|
||||||
loop_control:
|
|
||||||
index_var: _idx
|
|
||||||
label: "{{ item.key }}"
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
# Managed by Ansible.
|
|
||||||
{% set release = _debian_release_map[os_version | string] | default('trixie') %}
|
|
||||||
{% set mirror = system_cfg.mirror %}
|
|
||||||
{% set components = 'main contrib non-free' ~ (' non-free-firmware' if (os_version | string) not in ['10', '11'] else '') %}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }} {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }} {{ components }}
|
|
||||||
{% if release != 'sid' %}
|
|
||||||
|
|
||||||
deb https://security.debian.org/debian-security {{ release }}-security {{ components }}
|
|
||||||
deb-src https://security.debian.org/debian-security {{ release }}-security {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
{% endif %}
|
|
||||||
145
roles/configuration/templates/firstrun.sh.j2
Normal file
145
roles/configuration/templates/firstrun.sh.j2
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[1;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Ask for and set the hostname
|
||||||
|
echo -e "${BLUE}Enter the hostname:${NC}"
|
||||||
|
read -r new_hostname
|
||||||
|
|
||||||
|
# Detect the network interface
|
||||||
|
network_interface=$(nmcli -t -f DEVICE connection show --active | head -n 1)
|
||||||
|
|
||||||
|
# Ask for and set the IP address
|
||||||
|
echo -e "${BLUE}Enter the IP address (eg.: 10.11.x.x/24):${NC}"
|
||||||
|
read -r ip_address
|
||||||
|
|
||||||
|
# Ask for and set the DNS server
|
||||||
|
default_dns1="10.11.23.10"
|
||||||
|
default_dns2="10.11.23.18"
|
||||||
|
echo -e "${BLUE}Enter the DNS server (default: $default_dns1, $default_dns2):${NC}"
|
||||||
|
read -r dns_server
|
||||||
|
dns_server=${dns_server:-"$default_dns1 $default_dns2"}
|
||||||
|
|
||||||
|
# Ask if Btrfs compression should be enabled
|
||||||
|
if [[ $(df -T / | awk 'NR==2 {print $2}') == "btrfs" ]]; then
|
||||||
|
echo -e "${BLUE}Do you want to enable Btrfs compression? (y/n):${NC}"
|
||||||
|
read -r enable_compression
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$enable_compression" == "y" || "$enable_compression" == "Y" ]]; then
|
||||||
|
# Ask for the use case
|
||||||
|
echo -e "${BLUE} the use case:${NC}"
|
||||||
|
echo "1. Databases, File Storage, etc (recommended compression level: 15)"
|
||||||
|
echo "2. Real-time compression (recommended compression level: 3)"
|
||||||
|
echo "3. Custom compression level"
|
||||||
|
read -r use_case
|
||||||
|
|
||||||
|
# Set the recommended compression level based on the use case
|
||||||
|
case "$use_case" in
|
||||||
|
1) compression_level=15 ;;
|
||||||
|
2) compression_level=3 ;;
|
||||||
|
3) echo -e "${BLUE}Enter the custom compression level (1-15):${NC}"
|
||||||
|
read -r compression_level ;;
|
||||||
|
*) echo -e "${RED}Invalid use case. Exiting script.${NC}"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ask if CheckMK Agent should be installed
|
||||||
|
echo -e "${BLUE}Do you want to install the CheckMK Agent? (y/n):${NC}"
|
||||||
|
read -r install_checkmk_agent
|
||||||
|
|
||||||
|
# Ask if ports and services should be opened
|
||||||
|
echo -e "${BLUE}Do you want to open any ports or services? (y/n):${NC}"
|
||||||
|
read -r open_ports_services
|
||||||
|
|
||||||
|
if [[ "$open_ports_services" == "y" || "$open_ports_services" == "Y" ]]; then
|
||||||
|
# Ask for and set the services to open
|
||||||
|
echo -e "${BLUE}Enter the services to open (comma-separated):${NC}"
|
||||||
|
read -r services
|
||||||
|
|
||||||
|
# Ask for and set the ports to open
|
||||||
|
echo -e "${BLUE}Enter the ports to open (comma-separated):${NC}"
|
||||||
|
read -r ports
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply Changes
|
||||||
|
echo -e "${BLUE}Are you sure you want to apply the changes? This may cause a loss of SSH connection. (y/n):${NC}"
|
||||||
|
read -r answer
|
||||||
|
|
||||||
|
# Check the user's response
|
||||||
|
if [[ "$answer" == "y" || "$answer" == "Y" ]]; then
|
||||||
|
# Comment out the script execution line in .bashrc
|
||||||
|
sed -i '/~\/firstrun\.sh/s/^/#/' ~/.bashrc
|
||||||
|
hostnamectl set-hostname "$new_hostname"
|
||||||
|
|
||||||
|
nmcli device modify "$network_interface" ipv4.dns "$dns_server" > /dev/null
|
||||||
|
nmcli device modify "$network_interface" ipv6.method ignore > /dev/null
|
||||||
|
nmcli device modify "$network_interface" ipv4.addresses "$ip_address" ipv4.method manual > /dev/null
|
||||||
|
|
||||||
|
# Modify /etc/hosts file
|
||||||
|
ip_address=$(echo "$ip_address" | sed 's/.\{3\}$//')
|
||||||
|
if grep "$ip_address" /etc/hosts > /dev/null 2>&1; then
|
||||||
|
echo "IP address already exists in /etc/hosts"
|
||||||
|
else
|
||||||
|
# Add IP address and hostname after the "127.0.0.1 localhost" entry
|
||||||
|
sed -i '1a\'"$ip_address\t$new_hostname" /etc/hosts
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "IP address and hostname added to /etc/hosts"
|
||||||
|
else
|
||||||
|
echo "Failed to add IP address and hostname to /etc/hosts"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Modify Btrfs compression settings in /etc/fstab
|
||||||
|
if [[ "$enable_compression" == "y" || "$enable_compression" == "Y" ]]; then
|
||||||
|
if ! grep -q "compress=zstd" /etc/fstab; then
|
||||||
|
sed -i "/btrfs/s/defaults/defaults,compress=zstd:$compression_level/" /etc/fstab
|
||||||
|
else
|
||||||
|
sed -i "/btrfs/s/compress=zstd:[0-9]*/compress=zstd:$compression_level/" /etc/fstab
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if grep -q "compress=zstd" /etc/fstab; then
|
||||||
|
sed -i "/btrfs/s/,compress=zstd:[0-9]*//" /etc/fstab
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$install_checkmk_agent" == "y" || "$install_checkmk_agent" == "Y" ]]; then
|
||||||
|
# Run the CheckMK Agent installation script
|
||||||
|
bash Scripts/install_checkmk_agent.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$open_ports_services" == "y" || "$open_ports_services" == "Y" ]]; then
|
||||||
|
# Open the specified services
|
||||||
|
IFS=',' read -ra service_array <<< "$services"
|
||||||
|
for service in "${service_array[@]}"; do
|
||||||
|
firewall-cmd --add-service="$service" --permanent > /dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
# Open the specified ports
|
||||||
|
IFS=',' read -ra port_array <<< "$ports"
|
||||||
|
for port in "${port_array[@]}"; do
|
||||||
|
firewall-cmd --add-port="$port"/tcp --permanent > /dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
firewall-cmd --reload > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Open port 6556/tcp for CheckMK Agent if it was installed
|
||||||
|
if [[ "$install_checkmk_agent" == "y" || "$install_checkmk_agent" == "Y" ]]; then
|
||||||
|
firewall-cmd --add-port=6556/tcp --permanent > /dev/null 2>&1
|
||||||
|
firewall-cmd --reload > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
firewall-cmd --remove-port=6556/tcp --permanent > /dev/null 2>&1
|
||||||
|
firewall-cmd --reload > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}Changes applied successfully.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Changes not applied. Exiting script.${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
[connection]
|
[connection]
|
||||||
id=LAN-{{ idx }}
|
id=LAN
|
||||||
uuid={{ configuration_net_uuid }}
|
uuid={{ configuration_net_uuid }}
|
||||||
type=ethernet
|
type=ethernet
|
||||||
autoconnect-priority=10
|
|
||||||
{% if configuration_iface_name | length > 0 %}
|
[ethernet]
|
||||||
interface-name={{ configuration_iface_name }}
|
mac-address={{ configuration_net_mac }}
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
[ipv4]
|
[ipv4]
|
||||||
{% set iface = configuration_iface %}
|
{% set dns_value = vm_dns | default('') %}
|
||||||
{% set dns_list = configuration_dns_list %}
|
{% set dns_list_raw = dns_value if dns_value is iterable and dns_value is not string else dns_value.split(',') %}
|
||||||
{% set search_list = configuration_dns_search %}
|
{% set dns_list = dns_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||||
{% if iface.ip | default('') | string | length %}
|
{% set search_value = vm_dns_search | default('') %}
|
||||||
address1={{ iface.ip }}/{{ iface.prefix }}{{ (',' ~ iface.gateway) if (iface.gateway | default('') | string | length) else '' }}
|
{% set search_list_raw = search_value if search_value is iterable and search_value is not string else search_value.split(',') %}
|
||||||
|
{% set search_list = search_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||||
|
{% if vm_ip is defined and vm_ip | length %}
|
||||||
|
address1={{ vm_ip }}/{{ vm_nms | default(24) }}{{ (',' ~ vm_gw) if (vm_gw is defined and vm_gw | length) else '' }}
|
||||||
method=manual
|
method=manual
|
||||||
{% else %}
|
{% else %}
|
||||||
method=auto
|
method=auto
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if idx | int == 0 and dns_list %}
|
{% if dns_list %}
|
||||||
dns={{ dns_list | join(';') }};
|
dns={{ dns_list | join(';') }}
|
||||||
|
{% endif %}
|
||||||
|
{% if dns_list or search_list %}
|
||||||
ignore-auto-dns=true
|
ignore-auto-dns=true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if idx | int == 0 and search_list %}
|
{% if search_list %}
|
||||||
dns-search={{ search_list | join(';') }};
|
dns-search={{ search_list | join(';') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
[ipv6]
|
[ipv6]
|
||||||
|
|||||||
11
roles/configuration/templates/sudo_lecture.txt.j2
Normal file
11
roles/configuration/templates/sudo_lecture.txt.j2
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
\^V//
|
||||||
|
|. .| I AM (G)ROOT!
|
||||||
|
- \ - / _
|
||||||
|
\_| |_/
|
||||||
|
\ \
|
||||||
|
__/_/__
|
||||||
|
|_______| With great power comes great responsibility.
|
||||||
|
\ / Use sudo wisely.
|
||||||
|
\___/
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Managed by Ansible.
|
|
||||||
{% set release = _ubuntu_release_map[os] | default('noble') %}
|
|
||||||
{% set mirror = system_cfg.mirror %}
|
|
||||||
{% set components = 'main restricted universe multiverse' %}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }} {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }} {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-updates {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-security {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-security {{ components }}
|
|
||||||
|
|
||||||
deb {{ mirror }} {{ release }}-backports {{ components }}
|
|
||||||
deb-src {{ mirror }} {{ release }}-backports {{ components }}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
---
|
|
||||||
# Platform-specific configuration values keyed by os_family.
|
|
||||||
# Consumed as _configuration_platform in tasks via:
|
|
||||||
# configuration_platform_config[os_family]
|
|
||||||
configuration_platform_config:
|
|
||||||
RedHat:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: shimx64.efi
|
|
||||||
grub_install: false
|
|
||||||
initramfs_cmd: "/usr/bin/dracut --regenerate-all --force"
|
|
||||||
grub_mkconfig_prefix: grub2-mkconfig
|
|
||||||
locale_gen: false
|
|
||||||
init_system: systemd
|
|
||||||
Debian:
|
|
||||||
user_group: sudo
|
|
||||||
sudo_group: "%sudo"
|
|
||||||
ssh_service: ssh
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: >-
|
|
||||||
/usr/bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
||||||
/usr/sbin/update-initramfs -u -k all
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: true
|
|
||||||
init_system: systemd
|
|
||||||
Archlinux:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: "/usr/sbin/mkinitcpio -P"
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: true
|
|
||||||
init_system: systemd
|
|
||||||
Suse:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: "/usr/bin/dracut --regenerate-all --force"
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: true
|
|
||||||
init_system: systemd
|
|
||||||
Alpine:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: ""
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: false
|
|
||||||
init_system: openrc
|
|
||||||
Void:
|
|
||||||
user_group: wheel
|
|
||||||
sudo_group: "%wheel"
|
|
||||||
ssh_service: sshd
|
|
||||||
efi_loader: grubx64.efi
|
|
||||||
grub_install: true
|
|
||||||
initramfs_cmd: ""
|
|
||||||
grub_mkconfig_prefix: grub-mkconfig
|
|
||||||
locale_gen: false
|
|
||||||
init_system: runit
|
|
||||||
|
|
||||||
# Display manager auto-detection from desktop environment name.
|
|
||||||
configuration_desktop_dm_map:
|
|
||||||
gnome: gdm
|
|
||||||
kde: sddm
|
|
||||||
xfce: lightdm
|
|
||||||
sway: greetd
|
|
||||||
hyprland: ly@tty2
|
|
||||||
cinnamon: lightdm
|
|
||||||
mate: lightdm
|
|
||||||
lxqt: sddm
|
|
||||||
budgie: gdm
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
# Connection and timing
|
|
||||||
environment_wait_timeout: 180
|
|
||||||
environment_wait_delay: 5
|
|
||||||
|
|
||||||
# Pacman installer settings
|
|
||||||
environment_parallel_downloads: 20
|
|
||||||
environment_pacman_lock_timeout: 120
|
|
||||||
environment_pacman_retries: 4
|
|
||||||
environment_pacman_retry_delay: 15
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Select primary Network Interface
|
|
||||||
when: hypervisor_type == "vmware"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
environment_interface_name: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
(ansible_facts.interfaces | default(ansible_facts['ansible_interfaces'] | default([])))
|
|
||||||
| reject('equalto', 'lo')
|
|
||||||
| list
|
|
||||||
| first
|
|
||||||
)
|
|
||||||
| default('')
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Bring up network interface
|
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- environment_interface_name | default('') | length > 0
|
|
||||||
ansible.builtin.command: "ip link set {{ environment_interface_name }} up"
|
|
||||||
register: environment_link_result
|
|
||||||
changed_when: environment_link_result.rc == 0
|
|
||||||
|
|
||||||
- name: Set IP-Address
|
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- system_cfg.network.ip is defined and system_cfg.network.ip | string | length > 0
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
ip addr replace {{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
|
||||||
dev {{ environment_interface_name }}
|
|
||||||
register: environment_ip_result
|
|
||||||
changed_when: environment_ip_result.rc == 0
|
|
||||||
|
|
||||||
- name: Set Default Gateway
|
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length > 0
|
|
||||||
- system_cfg.network.ip is defined and system_cfg.network.ip | string | length > 0
|
|
||||||
ansible.builtin.command: "ip route replace default via {{ system_cfg.network.gateway }}"
|
|
||||||
register: environment_gateway_result
|
|
||||||
changed_when: environment_gateway_result.rc == 0
|
|
||||||
|
|
||||||
- name: Configure DNS resolvers
|
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- system_cfg.network.dns.servers | default([]) | length > 0
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /etc/resolv.conf
|
|
||||||
content: |
|
|
||||||
{% for server in system_cfg.network.dns.servers %}
|
|
||||||
nameserver {{ server }}
|
|
||||||
{% endfor %}
|
|
||||||
{% if system_cfg.network.dns.search | default([]) | length > 0 %}
|
|
||||||
search {{ system_cfg.network.dns.search | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Synchronize clock via NTP
|
|
||||||
ansible.builtin.command: timedatectl set-ntp true
|
|
||||||
register: environment_ntp_result
|
|
||||||
changed_when: environment_ntp_result.rc == 0
|
|
||||||
|
|
||||||
- name: Configure SSH for root login
|
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- hypervisor_cfg.ssh | default(false) | bool
|
|
||||||
- system_cfg.network.ip is defined and system_cfg.network.ip | string | length > 0
|
|
||||||
block:
|
|
||||||
- name: Allow login
|
|
||||||
ansible.builtin.replace:
|
|
||||||
path: /etc/ssh/sshd_config
|
|
||||||
regexp: "{{ item.regexp }}"
|
|
||||||
replace: "{{ item.replace }}"
|
|
||||||
loop:
|
|
||||||
- regexp: "^#?PermitEmptyPasswords.*"
|
|
||||||
replace: "PermitEmptyPasswords yes"
|
|
||||||
- regexp: "^#?PermitRootLogin.*"
|
|
||||||
replace: "PermitRootLogin yes"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.replace }}"
|
|
||||||
|
|
||||||
- name: Reload SSH service to apply changes
|
|
||||||
ansible.builtin.service:
|
|
||||||
name: sshd
|
|
||||||
state: reloaded
|
|
||||||
|
|
||||||
- name: Switch to SSH connection
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
ansible_connection: ssh
|
|
||||||
ansible_host: "{{ system_cfg.network.ip }}"
|
|
||||||
ansible_port: 22
|
|
||||||
ansible_user: root
|
|
||||||
ansible_password: ""
|
|
||||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
|
||||||
|
|
||||||
- name: Reset connection for SSH switchover
|
|
||||||
ansible.builtin.meta: reset_connection
|
|
||||||
|
|
||||||
- name: Verify SSH connectivity
|
|
||||||
ansible.builtin.wait_for_connection:
|
|
||||||
timeout: 30
|
|
||||||
delay: 2
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Wait for connection
|
|
||||||
ansible.builtin.wait_for_connection:
|
|
||||||
timeout: "{{ environment_wait_timeout }}"
|
|
||||||
delay: "{{ environment_wait_delay }}"
|
|
||||||
|
|
||||||
- name: Gather facts
|
|
||||||
ansible.builtin.setup:
|
|
||||||
|
|
||||||
- name: Check for live environment markers
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "{{ item }}"
|
|
||||||
loop:
|
|
||||||
- /run/archiso
|
|
||||||
- /run/live
|
|
||||||
- /run/initramfs
|
|
||||||
- /run/initramfs/live
|
|
||||||
register: environment_live_marker_stat
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Determine root filesystem type
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
environment_root_fstype: >-
|
|
||||||
{{
|
|
||||||
ansible_mounts
|
|
||||||
| selectattr('mount', 'equalto', '/')
|
|
||||||
| map(attribute='fstype')
|
|
||||||
| list
|
|
||||||
| first
|
|
||||||
| default('')
|
|
||||||
| lower
|
|
||||||
}}
|
|
||||||
environment_archiso_present: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
environment_live_marker_stat.results
|
|
||||||
| selectattr('item', 'equalto', '/run/archiso')
|
|
||||||
| selectattr('stat.exists')
|
|
||||||
| list
|
|
||||||
| length
|
|
||||||
) > 0
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Identify live environment indicators
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
environment_is_live_environment: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
environment_live_marker_stat.results
|
|
||||||
| selectattr('stat.exists')
|
|
||||||
| list
|
|
||||||
| length
|
|
||||||
) > 0
|
|
||||||
or environment_root_fstype in ['overlay', 'overlayfs', 'squashfs', 'aufs']
|
|
||||||
or (ansible_hostname | default('') | lower is search('live'))
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Abort if target is not a live environment
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- environment_is_live_environment | bool
|
|
||||||
fail_msg: |
|
|
||||||
PRODUCTION SYSTEM DETECTED - ABORTING
|
|
||||||
|
|
||||||
The target system does not appear to be a live installer environment.
|
|
||||||
This playbook must run from a live ISO to avoid wiping production data.
|
|
||||||
|
|
||||||
Boot from a live installer (Arch, Debian, Ubuntu, etc.) and retry.
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Harden sshd for Ansible automation
|
|
||||||
ansible.builtin.blockinfile:
|
|
||||||
path: /etc/ssh/sshd_config
|
|
||||||
marker: "# {mark} BOOTSTRAP ANSIBLE SETTINGS"
|
|
||||||
block: |
|
|
||||||
PerSourcePenalties no
|
|
||||||
MaxStartups 50:30:100
|
|
||||||
ClientAliveInterval 30
|
|
||||||
ClientAliveCountMax 10
|
|
||||||
register: _sshd_config_result
|
|
||||||
|
|
||||||
- name: Restart sshd immediately if config was changed
|
|
||||||
when: _sshd_config_result is changed
|
|
||||||
ansible.builtin.service:
|
|
||||||
name: sshd
|
|
||||||
state: restarted
|
|
||||||
|
|
||||||
- name: Abort if the host is not booted from the Arch install media
|
|
||||||
when:
|
|
||||||
- not (custom_iso | bool)
|
|
||||||
- not environment_archiso_present | bool
|
|
||||||
ansible.builtin.fail:
|
|
||||||
msg: This host is not booted from the Arch install media!
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Speed-up Bootstrap process
|
|
||||||
when: not (custom_iso | bool)
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /etc/pacman.conf
|
|
||||||
regexp: ^#ParallelDownloads =
|
|
||||||
line: "ParallelDownloads = {{ environment_parallel_downloads }}"
|
|
||||||
|
|
||||||
- name: Wait for pacman lock to be released
|
|
||||||
when: not (custom_iso | bool)
|
|
||||||
ansible.builtin.wait_for:
|
|
||||||
path: /var/lib/pacman/db.lck
|
|
||||||
state: absent
|
|
||||||
timeout: "{{ environment_pacman_lock_timeout }}"
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Setup Pacman
|
|
||||||
when:
|
|
||||||
- not (custom_iso | bool)
|
|
||||||
- item.os is not defined or os in item.os
|
|
||||||
community.general.pacman:
|
|
||||||
update_cache: true
|
|
||||||
force: true
|
|
||||||
name: "{{ item.name }}"
|
|
||||||
state: latest
|
|
||||||
loop:
|
|
||||||
- { name: glibc }
|
|
||||||
- { name: lua, os: [almalinux, fedora, rhel, rocky] }
|
|
||||||
- { name: dnf, os: [almalinux, fedora, rhel, rocky] }
|
|
||||||
- { name: debootstrap, os: [debian, ubuntu, ubuntu-lts] }
|
|
||||||
- { name: debian-archive-keyring, os: [debian] }
|
|
||||||
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.name }}"
|
|
||||||
retries: "{{ environment_pacman_retries }}"
|
|
||||||
delay: "{{ environment_pacman_retry_delay }}"
|
|
||||||
|
|
||||||
- name: Prepare /iso mount and repository for RHEL-based systems
|
|
||||||
when: os == "rhel"
|
|
||||||
block:
|
|
||||||
- name: Create /iso directory
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /usr/local/install/redhat/dvd
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Detect RHEL ISO device
|
|
||||||
ansible.builtin.command: lsblk -rno NAME,TYPE
|
|
||||||
register: environment_lsblk_result
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Select RHEL ISO device
|
|
||||||
vars:
|
|
||||||
_rom_devices: >-
|
|
||||||
{{
|
|
||||||
environment_lsblk_result.stdout_lines
|
|
||||||
| map('split', ' ')
|
|
||||||
| selectattr('1', 'equalto', 'rom')
|
|
||||||
| map('first')
|
|
||||||
| map('regex_replace', '^', '/dev/')
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
environment_rhel_iso_device: >-
|
|
||||||
{{
|
|
||||||
_rom_devices[-1]
|
|
||||||
if _rom_devices | length > 1
|
|
||||||
else (_rom_devices[0] | default('/dev/sr1'))
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Mount RHEL ISO
|
|
||||||
ansible.posix.mount:
|
|
||||||
src: "{{ environment_rhel_iso_device }}"
|
|
||||||
path: /usr/local/install/redhat/dvd
|
|
||||||
fstype: iso9660
|
|
||||||
opts: "ro,loop"
|
|
||||||
state: mounted
|
|
||||||
|
|
||||||
# Security note: RPM Sequoia signature policy is relaxed to allow
|
|
||||||
# bootstrapping RHEL-family distros from the Arch ISO, where the
|
|
||||||
# host rpm/dnf does not trust target distro GPG keys. Package
|
|
||||||
# integrity is verified by the target system's own rpm after reboot.
|
|
||||||
- name: Create RPM macros directory
|
|
||||||
when: is_rhel | bool
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /etc/rpm
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Relax RPM Sequoia signature policy for RHEL bootstrap
|
|
||||||
when: is_rhel | bool
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /etc/rpm/macros
|
|
||||||
content: "%_pkgverify_level none\n"
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Configure RHEL Repos for installation
|
|
||||||
when: is_rhel | bool
|
|
||||||
block:
|
|
||||||
- name: Create directories for repository files and RPM GPG keys
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /etc/yum.repos.d
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Create RHEL repository file
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: "{{ os }}.repo.j2"
|
|
||||||
dest: /etc/yum.repos.d/{{ os }}.repo
|
|
||||||
mode: "0644"
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Check for third-party preparation tasks
|
|
||||||
run_once: true
|
|
||||||
become: false
|
|
||||||
delegate_to: localhost
|
|
||||||
vars:
|
|
||||||
ansible_connection: local
|
|
||||||
block:
|
|
||||||
- name: Resolve third-party preparation task path
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
environment_thirdparty_tasks_path: >-
|
|
||||||
{{
|
|
||||||
thirdparty_tasks
|
|
||||||
if thirdparty_tasks | regex_search('^/')
|
|
||||||
else playbook_dir + '/' + thirdparty_tasks
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Stat third-party preparation tasks
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "{{ environment_thirdparty_tasks_path }}"
|
|
||||||
register: environment_thirdparty_tasks_stat
|
|
||||||
|
|
||||||
- name: Run third-party preparation tasks
|
|
||||||
when:
|
|
||||||
- thirdparty_tasks | length > 0
|
|
||||||
- environment_thirdparty_tasks_stat.stat.exists
|
|
||||||
ansible.builtin.include_tasks: "{{ environment_thirdparty_tasks_path }}"
|
|
||||||
@@ -1,15 +1,156 @@
|
|||||||
---
|
---
|
||||||
- name: Configure work environment
|
- name: Configure work environment
|
||||||
become: "{{ (hypervisor_type | default('none')) != 'vmware' }}"
|
become: "{{ hypervisor != 'vmware' }}"
|
||||||
block:
|
block:
|
||||||
- name: Detect and validate live environment
|
- name: Wait for connection
|
||||||
ansible.builtin.include_tasks: _detect_live.yml
|
ansible.builtin.wait_for_connection:
|
||||||
|
timeout: 180
|
||||||
|
delay: 5
|
||||||
|
|
||||||
- name: Configure network and connectivity
|
- name: Gather facts
|
||||||
ansible.builtin.include_tasks: _configure_network.yml
|
ansible.builtin.setup:
|
||||||
|
|
||||||
|
- name: Check if host is booted from the Arch install media
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /run/archiso
|
||||||
|
register: environment_archiso_stat
|
||||||
|
|
||||||
|
- name: Abort if the host is not booted from the Arch install media
|
||||||
|
when:
|
||||||
|
- not (custom_iso | default(false) | bool)
|
||||||
|
- not environment_archiso_stat.stat.exists
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: This host is not booted from the Arch install media!
|
||||||
|
|
||||||
|
- name: Select primary Network Interface
|
||||||
|
when: hypervisor == "vmware"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
environment_interface_name: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(ansible_facts.interfaces | default(ansible_facts['ansible_interfaces'] | default([])))
|
||||||
|
| reject('equalto', 'lo')
|
||||||
|
| list
|
||||||
|
| first
|
||||||
|
)
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set IP-Address
|
||||||
|
when:
|
||||||
|
- hypervisor == "vmware"
|
||||||
|
- vm_ip is defined
|
||||||
|
- vm_ip | length
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
ip addr replace {{ vm_ip }}/{{ vm_nms | default(24) }}
|
||||||
|
dev {{ environment_interface_name }}
|
||||||
|
register: environment_ip_result
|
||||||
|
changed_when: environment_ip_result.rc == 0
|
||||||
|
|
||||||
|
- name: Set Default Gateway
|
||||||
|
when:
|
||||||
|
- hypervisor == "vmware"
|
||||||
|
- vm_gw is defined
|
||||||
|
- vm_gw | length
|
||||||
|
- vm_ip is defined
|
||||||
|
- vm_ip | length
|
||||||
|
ansible.builtin.command: "ip route replace default via {{ vm_gw }}"
|
||||||
|
register: environment_gateway_result
|
||||||
|
changed_when: environment_gateway_result.rc == 0
|
||||||
|
|
||||||
|
- name: Synchronize clock via NTP
|
||||||
|
ansible.builtin.command: timedatectl set-ntp true
|
||||||
|
register: environment_ntp_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Configure SSH for root login
|
||||||
|
when: hypervisor == "vmware" and (vmware_ssh is defined and vmware_ssh | bool)
|
||||||
|
block:
|
||||||
|
- name: Allow login
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: "{{ item.regexp }}"
|
||||||
|
replace: "{{ item.replace }}"
|
||||||
|
loop:
|
||||||
|
- regexp: "^#?PermitEmptyPasswords.*"
|
||||||
|
replace: "PermitEmptyPasswords yes"
|
||||||
|
- regexp: "^#?PermitRootLogin.*"
|
||||||
|
replace: "PermitRootLogin yes"
|
||||||
|
|
||||||
|
- name: Reload SSH service to apply changes
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: sshd
|
||||||
|
state: reloaded
|
||||||
|
|
||||||
|
- name: Set SSH connection for VMware
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ansible_connection: ssh
|
||||||
|
ansible_user: root
|
||||||
|
|
||||||
- name: Prepare installer environment
|
- name: Prepare installer environment
|
||||||
ansible.builtin.include_tasks: _prepare_installer.yml
|
block:
|
||||||
|
- name: Speed-up Bootstrap process
|
||||||
|
when: not (custom_iso | default(false) | bool)
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/pacman.conf
|
||||||
|
regexp: ^#ParallelDownloads =
|
||||||
|
line: ParallelDownloads = 20
|
||||||
|
|
||||||
- name: Run third-party preparation tasks
|
- name: Wait for pacman lock to be released
|
||||||
ansible.builtin.include_tasks: _thirdparty.yml
|
when: not (custom_iso | default(false) | bool)
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
path: /var/lib/pacman/db.lck
|
||||||
|
state: absent
|
||||||
|
timeout: 120
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Setup Pacman
|
||||||
|
when:
|
||||||
|
- not (custom_iso | default(false) | bool)
|
||||||
|
- "'os' not in item or os in item.os"
|
||||||
|
community.general.pacman:
|
||||||
|
update_cache: true
|
||||||
|
force: true
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: latest
|
||||||
|
loop:
|
||||||
|
- {name: glibc}
|
||||||
|
- {name: dnf, os: [almalinux, fedora, rhel8, rhel9, rhel10, rocky]}
|
||||||
|
- {name: debootstrap, os: [debian11, debian12, debian13, ubuntu, ubuntu-lts]}
|
||||||
|
- {name: debian-archive-keyring, os: [debian11, debian12, debian13]}
|
||||||
|
- {name: ubuntu-keyring, os: [ubuntu, ubuntu-lts]}
|
||||||
|
retries: 4
|
||||||
|
delay: 15
|
||||||
|
|
||||||
|
- name: Prepare /iso mount and repository for RHEL-based systems
|
||||||
|
when: os | lower in ["rhel8", "rhel9", "rhel10"]
|
||||||
|
block:
|
||||||
|
- name: Create /iso directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /usr/local/install/redhat/dvd
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Mount RHEL ISO
|
||||||
|
ansible.posix.mount:
|
||||||
|
src: "{{ '/dev/sr1' if hypervisor == 'vmware' else '/dev/sr2' }}"
|
||||||
|
path: /usr/local/install/redhat/dvd
|
||||||
|
fstype: iso9660
|
||||||
|
opts: "ro,loop"
|
||||||
|
state: mounted
|
||||||
|
|
||||||
|
- name: Configure RHEL Repos for installation
|
||||||
|
when: is_rhel | default(false)
|
||||||
|
block:
|
||||||
|
- name: Create directories for repository files and RPM GPG keys
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/yum.repos.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Create RHEL repository file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ os | lower }}.repo.j2"
|
||||||
|
dest: /etc/yum.repos.d/{{ os | lower }}.repo
|
||||||
|
mode: "0644"
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
[baseos]
|
|
||||||
name=Rocky Linux $releasever - BaseOS
|
|
||||||
mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=BaseOS-$releasever
|
|
||||||
#baseurl=http://dl.rockylinux.org/$contentdir/$releasever/BaseOS/$basearch/os/
|
|
||||||
gpgcheck=1
|
|
||||||
enabled=1
|
|
||||||
countme=1
|
|
||||||
gpgkey=https://dl.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-$releasever
|
|
||||||
metadata_expire=86400
|
|
||||||
enabled_metadata=1
|
|
||||||
|
|
||||||
[appstream]
|
|
||||||
name=Rocky Linux $releasever - AppStream
|
|
||||||
mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=AppStream-$releasever
|
|
||||||
#baseurl=http://dl.rockylinux.org/$contentdir/$releasever/AppStream/$basearch/os/
|
|
||||||
gpgcheck=1
|
|
||||||
enabled=1
|
|
||||||
countme=1
|
|
||||||
gpgkey=https://dl.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-$releasever
|
|
||||||
metadata_expire=86400
|
|
||||||
enabled_metadata=1
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
---
|
|
||||||
# OS family lists — single source of truth for platform detection and validation
|
|
||||||
os_family_rhel:
|
|
||||||
- almalinux
|
|
||||||
- fedora
|
|
||||||
- rhel
|
|
||||||
- rocky
|
|
||||||
os_family_debian:
|
|
||||||
- debian
|
|
||||||
- ubuntu
|
|
||||||
- ubuntu-lts
|
|
||||||
|
|
||||||
# OS → family mapping — aligns with the main project's ansible_os_family pattern.
|
|
||||||
# Enables platform_config dict lookups per role instead of inline when: is_rhel chains.
|
|
||||||
os_family_map:
|
|
||||||
almalinux: RedHat
|
|
||||||
alpine: Alpine
|
|
||||||
archlinux: Archlinux
|
|
||||||
debian: Debian
|
|
||||||
fedora: RedHat
|
|
||||||
opensuse: Suse
|
|
||||||
rhel: RedHat
|
|
||||||
rocky: RedHat
|
|
||||||
ubuntu: Debian
|
|
||||||
ubuntu-lts: Debian
|
|
||||||
void: Void
|
|
||||||
|
|
||||||
os_supported:
|
|
||||||
- almalinux
|
|
||||||
- alpine
|
|
||||||
- archlinux
|
|
||||||
- debian
|
|
||||||
- fedora
|
|
||||||
- opensuse
|
|
||||||
- rhel
|
|
||||||
- rocky
|
|
||||||
- ubuntu
|
|
||||||
- ubuntu-lts
|
|
||||||
- void
|
|
||||||
|
|
||||||
# User input. Normalized into hypervisor_cfg + hypervisor_type.
|
|
||||||
hypervisor:
|
|
||||||
type: "none"
|
|
||||||
hypervisor_defaults:
|
|
||||||
type: "none"
|
|
||||||
url: ""
|
|
||||||
username: ""
|
|
||||||
password: ""
|
|
||||||
node: ""
|
|
||||||
storage: ""
|
|
||||||
datacenter: ""
|
|
||||||
cluster: ""
|
|
||||||
folder: ""
|
|
||||||
certs: false
|
|
||||||
ssh: false
|
|
||||||
|
|
||||||
physical_default_os: "archlinux"
|
|
||||||
custom_iso: false
|
|
||||||
thirdparty_tasks: "dropins/preparation.yml"
|
|
||||||
|
|
||||||
system_defaults:
|
|
||||||
type: "virtual" # virtual|physical
|
|
||||||
os: ""
|
|
||||||
version: ""
|
|
||||||
filesystem: "ext4"
|
|
||||||
name: ""
|
|
||||||
id: ""
|
|
||||||
cpus: 0
|
|
||||||
memory: 0 # MiB
|
|
||||||
balloon: 0 # MiB
|
|
||||||
network:
|
|
||||||
bridge: ""
|
|
||||||
vlan: ""
|
|
||||||
ip: ""
|
|
||||||
prefix: ""
|
|
||||||
gateway: ""
|
|
||||||
dns:
|
|
||||||
servers: []
|
|
||||||
search: []
|
|
||||||
interfaces: []
|
|
||||||
path: ""
|
|
||||||
timezone: "Europe/Vienna"
|
|
||||||
locale: "en_US.UTF-8"
|
|
||||||
keymap: "us"
|
|
||||||
mirror: ""
|
|
||||||
packages: []
|
|
||||||
disks: []
|
|
||||||
users: {}
|
|
||||||
root:
|
|
||||||
password: ""
|
|
||||||
shell: "/bin/bash"
|
|
||||||
luks:
|
|
||||||
enabled: false
|
|
||||||
passphrase: ""
|
|
||||||
mapper: "SYSTEM_DECRYPTED"
|
|
||||||
auto: true
|
|
||||||
method: "tpm2"
|
|
||||||
tpm2:
|
|
||||||
device: "auto"
|
|
||||||
pcrs: ""
|
|
||||||
keysize: 64
|
|
||||||
options: "discard,tries=3"
|
|
||||||
type: "luks2"
|
|
||||||
cipher: "aes-xts-plain64"
|
|
||||||
hash: "sha512"
|
|
||||||
iter: 4000
|
|
||||||
bits: 512
|
|
||||||
pbkdf: "argon2id"
|
|
||||||
urandom: true
|
|
||||||
verify: true
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: false
|
|
||||||
selinux:
|
|
||||||
enabled: true
|
|
||||||
firewall:
|
|
||||||
enabled: true
|
|
||||||
backend: "firewalld" # firewalld|ufw
|
|
||||||
toolkit: "nftables" # nftables|iptables
|
|
||||||
ssh:
|
|
||||||
enabled: true
|
|
||||||
zstd:
|
|
||||||
enabled: true
|
|
||||||
swap:
|
|
||||||
enabled: true
|
|
||||||
banner:
|
|
||||||
motd: false
|
|
||||||
sudo: true
|
|
||||||
rhel_repo:
|
|
||||||
source: "iso" # iso|satellite|none — how RHEL systems get packages post-install
|
|
||||||
url: "" # Satellite/custom repo URL when source=satellite
|
|
||||||
aur:
|
|
||||||
enabled: false
|
|
||||||
helper: "yay" # yay|paru
|
|
||||||
user: "_aur_builder"
|
|
||||||
chroot:
|
|
||||||
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
|
|
||||||
initramfs:
|
|
||||||
generator: "" # auto-detected; override: dracut|mkinitcpio|initramfs-tools
|
|
||||||
desktop:
|
|
||||||
enabled: false
|
|
||||||
environment: "" # gnome|kde|xfce|sway|hyprland|cinnamon|mate|lxqt|budgie
|
|
||||||
display_manager: "" # auto from environment when empty; override: gdm|sddm|lightdm|greetd
|
|
||||||
secure_boot:
|
|
||||||
enabled: false
|
|
||||||
method: "" # arch only: sbctl (default) or uki; ignored for other distros
|
|
||||||
|
|
||||||
# Per-hypervisor required fields — drives data-driven validation.
|
|
||||||
# All virtual types additionally require network bridge or interfaces.
|
|
||||||
hypervisor_required_fields:
|
|
||||||
proxmox:
|
|
||||||
hypervisor: [url, username, password, node, storage]
|
|
||||||
system: [id]
|
|
||||||
vmware:
|
|
||||||
hypervisor: [url, username, password, datacenter, storage]
|
|
||||||
system: []
|
|
||||||
xen:
|
|
||||||
hypervisor: []
|
|
||||||
system: []
|
|
||||||
libvirt:
|
|
||||||
hypervisor: []
|
|
||||||
system: []
|
|
||||||
|
|
||||||
# Hypervisor-to-disk device prefix mapping for virtual machines.
|
|
||||||
# Physical installs must set system.disks[].device explicitly.
|
|
||||||
hypervisor_disk_device_map:
|
|
||||||
libvirt: "/dev/vd"
|
|
||||||
xen: "/dev/xvd"
|
|
||||||
proxmox: "/dev/sd"
|
|
||||||
vmware: "/dev/sd"
|
|
||||||
|
|
||||||
# Mountpoints managed by the partitioning role — forbidden for extra disks.
|
|
||||||
reserved_mounts:
|
|
||||||
- /boot
|
|
||||||
- /boot/efi
|
|
||||||
- /home
|
|
||||||
- /var
|
|
||||||
- /var/log
|
|
||||||
- /var/log/audit
|
|
||||||
|
|
||||||
# Drive letter sequence for disk device naming (max 26 disks).
|
|
||||||
disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
system_disk_defaults:
|
|
||||||
size: 0
|
|
||||||
device: ""
|
|
||||||
mount:
|
|
||||||
path: ""
|
|
||||||
fstype: ""
|
|
||||||
label: ""
|
|
||||||
opts: "defaults"
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Normalize system disks input
|
|
||||||
vars:
|
|
||||||
system_disks: "{{ system_cfg.disks | default([]) }}"
|
|
||||||
system_disk_letter_map: "{{ disk_letter_map }}"
|
|
||||||
system_disk_device_prefix: >-
|
|
||||||
{{
|
|
||||||
hypervisor_disk_device_map.get(hypervisor_type, '')
|
|
||||||
if system_cfg.type == 'virtual'
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Validate system disks structure
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system_disks is sequence
|
|
||||||
- (system_disks | length) <= 26
|
|
||||||
fail_msg: "system.disks must be a list with at most 26 entries."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system disk entries
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- item is mapping
|
|
||||||
- item.mount is not defined or item.mount is mapping
|
|
||||||
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ system_disks }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item | to_json }}"
|
|
||||||
|
|
||||||
- name: Initialize normalized disk list
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_disks_cfg: []
|
|
||||||
|
|
||||||
- name: Build normalized system disk configuration
|
|
||||||
vars:
|
|
||||||
disk_idx: "{{ ansible_loop.index0 }}"
|
|
||||||
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
|
|
||||||
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
|
|
||||||
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
|
|
||||||
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
|
|
||||||
disk_mount_fstype: >-
|
|
||||||
{{
|
|
||||||
disk_mount.fstype
|
|
||||||
if (disk_mount.fstype | default('') | string | length) > 0
|
|
||||||
else ('ext4' if disk_mount_path | length > 0 else '')
|
|
||||||
}}
|
|
||||||
disk_device: >-
|
|
||||||
{{
|
|
||||||
disk_cfg_base.device
|
|
||||||
if (disk_cfg_base.device | string | length) > 0
|
|
||||||
else (
|
|
||||||
(system_disk_device_prefix ~ disk_letter)
|
|
||||||
if system_cfg.type == 'virtual'
|
|
||||||
else ''
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
disk_partition: >-
|
|
||||||
{{
|
|
||||||
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
|
|
||||||
if disk_device | length > 0
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_disks_cfg: >-
|
|
||||||
{{
|
|
||||||
system_disks_cfg + [
|
|
||||||
disk_cfg_base
|
|
||||||
| combine(
|
|
||||||
{
|
|
||||||
'device': disk_device,
|
|
||||||
'mount': {
|
|
||||||
'path': disk_mount_path,
|
|
||||||
'fstype': disk_mount_fstype,
|
|
||||||
'label': disk_mount.label | default('') | string,
|
|
||||||
'opts': disk_mount.opts | default('defaults') | string
|
|
||||||
},
|
|
||||||
'partition': disk_partition
|
|
||||||
},
|
|
||||||
recursive=True
|
|
||||||
)
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
loop: "{{ system_disks }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: item
|
|
||||||
extended: true
|
|
||||||
label: "{{ item | to_json }}"
|
|
||||||
|
|
||||||
- name: Update system configuration with normalized disks
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg}, recursive=True) }}"
|
|
||||||
|
|
||||||
- name: Set install_drive from primary disk
|
|
||||||
when:
|
|
||||||
- system_disks_cfg | length > 0
|
|
||||||
- system_disks_cfg[0].device | string | length > 0
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
install_drive: "{{ system_disks_cfg[0].device }}"
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Build normalized system configuration
|
|
||||||
vars:
|
|
||||||
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
|
||||||
system_type: "{{ 'virtual' if (system_raw.type | string | lower) in ['vm', 'virtual'] else (system_raw.type | string | lower) }}"
|
|
||||||
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
|
|
||||||
system_name: >-
|
|
||||||
{{
|
|
||||||
system_raw.name | string | trim
|
|
||||||
if (system_raw.name | default('') | string | trim | length) > 0
|
|
||||||
else inventory_hostname
|
|
||||||
}}
|
|
||||||
_mirror_defaults:
|
|
||||||
debian: "https://deb.debian.org/debian/"
|
|
||||||
ubuntu: "http://archive.ubuntu.com/ubuntu/"
|
|
||||||
ubuntu-lts: "http://archive.ubuntu.com/ubuntu/"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_cfg:
|
|
||||||
# --- Identity & platform ---
|
|
||||||
type: "{{ system_type }}"
|
|
||||||
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}"
|
|
||||||
version: "{{ system_raw.version | default('') | string }}"
|
|
||||||
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
|
|
||||||
name: "{{ system_name }}"
|
|
||||||
id: "{{ system_raw.id | default('') | string }}"
|
|
||||||
# --- VM sizing (ignored for physical) ---
|
|
||||||
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
|
|
||||||
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
|
||||||
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
|
||||||
# --- Network ---
|
|
||||||
# Flat fields (bridge, ip, etc.) and interfaces[] are mutually exclusive.
|
|
||||||
# When interfaces[] is set, flat fields are populated from the first
|
|
||||||
# interface in the "Populate primary network fields" task below.
|
|
||||||
# When only flat fields are set, a synthetic interfaces[] entry is built.
|
|
||||||
network:
|
|
||||||
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
|
||||||
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
|
||||||
ip: "{{ system_raw.network.ip | default('') | string }}"
|
|
||||||
prefix: >-
|
|
||||||
{{
|
|
||||||
(system_raw.network.prefix | int | string)
|
|
||||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
|
||||||
dns:
|
|
||||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
|
||||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
|
||||||
interfaces: >-
|
|
||||||
{{
|
|
||||||
system_raw.network.interfaces
|
|
||||||
if (system_raw.network.interfaces | default([]) | length > 0)
|
|
||||||
else (
|
|
||||||
[{
|
|
||||||
'name': '',
|
|
||||||
'bridge': system_raw.network.bridge | default('') | string,
|
|
||||||
'vlan': system_raw.network.vlan | default('') | string,
|
|
||||||
'ip': system_raw.network.ip | default('') | string,
|
|
||||||
'prefix': (
|
|
||||||
(system_raw.network.prefix | int | string)
|
|
||||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
|
||||||
else ''
|
|
||||||
),
|
|
||||||
'gateway': system_raw.network.gateway | default('') | string
|
|
||||||
}]
|
|
||||||
if (system_raw.network.bridge | default('') | string | length > 0)
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
# --- Locale & environment ---
|
|
||||||
timezone: "{{ system_raw.timezone | string }}"
|
|
||||||
locale: "{{ system_raw.locale | string }}"
|
|
||||||
keymap: "{{ system_raw.keymap | string }}"
|
|
||||||
mirror: >-
|
|
||||||
{{
|
|
||||||
system_raw.mirror | string | trim
|
|
||||||
if (system_raw.mirror | default('') | string | trim | length) > 0
|
|
||||||
else _mirror_defaults[system_raw.os | default('') | string | lower] | default('')
|
|
||||||
}}
|
|
||||||
path: >-
|
|
||||||
{{
|
|
||||||
(system_raw.path | default('') | string)
|
|
||||||
if (system_raw.path | default('') | string | length > 0)
|
|
||||||
else (hypervisor_cfg.folder | default('') | string)
|
|
||||||
}}
|
|
||||||
packages: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
system_raw.packages
|
|
||||||
if system_raw.packages is iterable and system_raw.packages is not string
|
|
||||||
else (system_raw.packages | string).split(',')
|
|
||||||
)
|
|
||||||
| map('trim')
|
|
||||||
| reject('equalto', '')
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
# --- Storage & accounts ---
|
|
||||||
disks: "{{ system_raw.disks | default([]) }}"
|
|
||||||
users: "{{ system_raw.users | default({}) }}"
|
|
||||||
root:
|
|
||||||
password: "{{ system_raw.root.password | string }}"
|
|
||||||
shell: "{{ system_raw.root.shell | default('/bin/bash') | string }}"
|
|
||||||
# --- LUKS disk encryption ---
|
|
||||||
luks:
|
|
||||||
enabled: "{{ system_raw.luks.enabled | bool }}"
|
|
||||||
passphrase: "{{ system_raw.luks.passphrase | string }}"
|
|
||||||
mapper: "{{ system_raw.luks.mapper | string }}"
|
|
||||||
auto: "{{ system_raw.luks.auto | bool }}"
|
|
||||||
method: "{{ system_raw.luks.method | string | lower }}"
|
|
||||||
tpm2:
|
|
||||||
device: "{{ system_raw.luks.tpm2.device | string }}"
|
|
||||||
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
|
|
||||||
keysize: "{{ system_raw.luks.keysize | int }}"
|
|
||||||
options: "{{ system_raw.luks.options | string }}"
|
|
||||||
type: "{{ system_raw.luks.type | string }}"
|
|
||||||
cipher: "{{ system_raw.luks.cipher | string }}"
|
|
||||||
hash: "{{ system_raw.luks.hash | string }}"
|
|
||||||
iter: "{{ system_raw.luks.iter | int }}"
|
|
||||||
bits: "{{ system_raw.luks.bits | int }}"
|
|
||||||
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
|
|
||||||
urandom: "{{ system_raw.luks.urandom | bool }}"
|
|
||||||
verify: "{{ system_raw.luks.verify | bool }}"
|
|
||||||
# --- Feature flags ---
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: "{{ system_raw.features.cis.enabled | bool }}"
|
|
||||||
selinux:
|
|
||||||
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
|
|
||||||
firewall:
|
|
||||||
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
|
|
||||||
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
|
|
||||||
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
|
|
||||||
ssh:
|
|
||||||
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
|
|
||||||
zstd:
|
|
||||||
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
|
|
||||||
swap:
|
|
||||||
enabled: "{{ system_raw.features.swap.enabled | bool }}"
|
|
||||||
banner:
|
|
||||||
motd: "{{ system_raw.features.banner.motd | bool }}"
|
|
||||||
sudo: "{{ system_raw.features.banner.sudo | bool }}"
|
|
||||||
rhel_repo:
|
|
||||||
source: "{{ system_raw.features.rhel_repo.source | default('iso') | string | lower }}"
|
|
||||||
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
|
|
||||||
chroot:
|
|
||||||
tool: "{{ system_raw.features.chroot.tool | string }}"
|
|
||||||
initramfs:
|
|
||||||
generator: "{{ system_raw.features.initramfs.generator | default('') | string | lower }}"
|
|
||||||
desktop:
|
|
||||||
enabled: "{{ system_raw.features.desktop.enabled | bool }}"
|
|
||||||
environment: "{{ system_raw.features.desktop.environment | default('') | string | lower }}"
|
|
||||||
display_manager: "{{ system_raw.features.desktop.display_manager | default('') | string | lower }}"
|
|
||||||
secure_boot:
|
|
||||||
enabled: "{{ system_raw.features.secure_boot.enabled | bool }}"
|
|
||||||
method: "{{ system_raw.features.secure_boot.method | default('') | string | lower }}"
|
|
||||||
hostname: "{{ system_name }}"
|
|
||||||
os: "{{ system_os_input if system_os_input | length > 0 else (physical_default_os if system_type == 'physical' else '') }}"
|
|
||||||
os_version: "{{ system_raw.version | default('') | string }}"
|
|
||||||
no_log: true
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Ensure system input is a dictionary
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system: "{{ system | default({}) }}"
|
|
||||||
|
|
||||||
- name: Validate system input types
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system is mapping
|
|
||||||
- system.network is not defined or system.network is mapping
|
|
||||||
- system.users is not defined or system.users is mapping
|
|
||||||
- system.root is not defined or system.root is mapping
|
|
||||||
- system.luks is not defined or system.luks is mapping
|
|
||||||
- system.features is not defined or system.features is mapping
|
|
||||||
fail_msg: "system and its nested keys (network, root, luks, features, users) must be dictionaries."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate DNS lists (not strings)
|
|
||||||
when: system.network is defined and system.network.dns is defined
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
|
||||||
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
|
||||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system.users entries
|
|
||||||
when: system.users is defined and system.users is mapping and system.users | length > 0
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- item.value is mapping
|
|
||||||
- item.key | string | length > 0
|
|
||||||
- item.value['keys'] is not defined or (item.value['keys'] is iterable and item.value['keys'] is not string)
|
|
||||||
fail_msg: "Each system.users entry must be a dict keyed by username; 'keys' must be a list."
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ system.users | dict2items }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.key }}"
|
|
||||||
|
|
||||||
- name: Validate system features input types
|
|
||||||
when: system.features is defined
|
|
||||||
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
|
||||||
loop_control:
|
|
||||||
label: "system.features.{{ item }}"
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- (system.features[item] | default({})) is mapping
|
|
||||||
fail_msg: "system.features.{{ item }} must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system LUKS TPM2 input type
|
|
||||||
when: system.luks is defined and system.luks is mapping
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
|
||||||
fail_msg: "system.luks.tpm2 must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Normalize hypervisor configuration
|
|
||||||
when: hypervisor_cfg is not defined
|
|
||||||
block:
|
|
||||||
- name: Ensure hypervisor input is a dictionary
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
hypervisor: "{{ hypervisor | default({}) }}"
|
|
||||||
|
|
||||||
- name: Validate hypervisor input
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- hypervisor is mapping
|
|
||||||
- hypervisor.type is defined
|
|
||||||
- hypervisor.type | string | length > 0
|
|
||||||
fail_msg: "hypervisor must be a dictionary and hypervisor.type must be set (e.g. libvirt|proxmox|vmware|xen|none)."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Merge hypervisor defaults with input
|
|
||||||
vars:
|
|
||||||
merged: "{{ hypervisor_defaults | combine(hypervisor, recursive=True) }}"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
hypervisor_cfg: "{{ merged }}"
|
|
||||||
hypervisor_type: "{{ merged.type | string | lower }}"
|
|
||||||
no_log: true
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user