Compare commits
160 Commits
a78c9ab8e8
...
f8eaa41fc2
| Author | SHA1 | Date | |
|---|---|---|---|
| f8eaa41fc2 | |||
| ed8da6e4e2 | |||
| a60e6fd0d3 | |||
| 45c002c2dd | |||
| 7a76f58384 | |||
| 8c0716508e | |||
| e5d2720bfe | |||
| cd34b41862 | |||
| 37130da17b | |||
| 4be9e2bfe1 | |||
| d8fcc6033d | |||
| fc8f43a25a | |||
| 70475f4082 | |||
| 865d96c18e | |||
| 920e7d3f21 | |||
| 9f6fff313b | |||
| e7323258fd | |||
| 3d026407e5 | |||
| 469d89641e | |||
| 5326907ae9 | |||
| aac2bd0b06 | |||
| 636656214b | |||
| e2a42771ab | |||
| 8894da2ea1 | |||
| b8c672507f | |||
| 04727033f1 | |||
| a9db85d45e | |||
| fcc7c6aeb6 | |||
| db08609acf | |||
| 961c8f259c | |||
| 9101e12126 | |||
| fc05708466 | |||
| c4c96dbfb5 | |||
| 5ff0bac9d8 | |||
| 3d8b623f66 | |||
| a093bf3e28 | |||
| a90e08cd4f | |||
| a5a58710a2 | |||
| 51c0f58b16 | |||
| 21318b8c8a | |||
| c3f26f2c92 | |||
| c62de8bf4a | |||
| c5e01c3652 | |||
| 49372309d2 | |||
| a669e3ddfb | |||
| 9b070c6e8d | |||
| cc07a896d9 | |||
| 88a8737115 | |||
| 76bbff43c0 | |||
| 53b4390ebf | |||
| 8a369de5d7 | |||
| 74fbfbccb5 | |||
| 41cccbb547 | |||
| ba9654b7bd | |||
| 14d774ffac | |||
| 40df28f59b | |||
| 20e10c3627 | |||
| 2672da4187 | |||
| 0143517787 | |||
| e9bf0c8242 | |||
| a6d71125e8 | |||
| df90672237 | |||
| 65494a6977 | |||
| 52c67c5a39 | |||
| 1c23055dd2 | |||
| 8395ad9e90 | |||
| e59f056904 | |||
| 0bd85319f6 | |||
| 8eed5c04c4 | |||
| e7c5166128 | |||
| cf179b0d16 | |||
| f4b4a669ba | |||
| 5288167825 | |||
| 7cf0dabc3c | |||
| 34d70c0edc | |||
| 73b42f29cb | |||
| b0d9adcf13 | |||
| cbc88c8d03 | |||
| 4705db7fe2 | |||
| 1d8d4cc4fd | |||
| 3f0408e271 | |||
| 6e30bbb4ff | |||
| 5914d216ce | |||
| cc8b95463a | |||
| 7323781046 | |||
| eba93f90b7 | |||
| 2873c8f81a | |||
| c353be967a | |||
| 259604470f | |||
| b2f812823a | |||
| 774f9529b1 | |||
| 5d7778c13e | |||
| caab1a8690 | |||
| 0989849163 | |||
| 698ffc61f1 | |||
| d106111f15 | |||
| 69422a6f64 | |||
| 93dae69781 | |||
| f17bdfa528 | |||
| e036761c9a | |||
| da7f22edbe | |||
| bafab61a37 | |||
| 9ba38c9d74 | |||
| 9f4f147b1c | |||
| b5adfb271f | |||
| 1eaa192eaa | |||
| e1556caccd | |||
| cb2f7b3e93 | |||
| b23eb9db28 | |||
| cc8f5c6675 | |||
| 8001fe2874 | |||
| 4c4a075560 | |||
| 8882160fc4 | |||
| cfcf1d6107 | |||
| 52af252662 | |||
| 4ac6cf540e | |||
| f4ca2ca34f | |||
| 893f5995ab | |||
| 96929a260c | |||
| c802d9b30e | |||
| 17f2a1a93e | |||
| 229395211c | |||
| c84ddd70db | |||
| 97f91f5d11 | |||
| febd87919f | |||
| bf818304ef | |||
| 758213e1ec | |||
| 5e8d9ff29c | |||
| e13db88768 | |||
| 39fdefc324 | |||
| b5ea94bdf3 | |||
| f7f88226a9 | |||
| 48949cc9e3 | |||
| bfa1be86d1 | |||
| fe5c182f76 | |||
| 6328d40d70 | |||
| 83fff50d89 | |||
| dd6aff8aa1 | |||
| 530d224fd0 | |||
| c81a7f1e96 | |||
| a03f00f28b | |||
| f7c6c9198f | |||
| f5c09571c0 | |||
| 88d77cf9a6 | |||
| bd3f3b0478 | |||
| a6da314d3b | |||
| 70bd67f7c4 | |||
| 0f729b4e8a | |||
| 99499a2f45 | |||
| fe08896ed4 | |||
| 1d3c305688 | |||
| 26cfbb9ce3 | |||
| e5d4886246 | |||
| 658287c159 | |||
| b5f46bc812 | |||
| 64abe4daa5 | |||
| ed0be16f61 | |||
| d47296a918 | |||
| 842a68ab36 | |||
| da8480a0c9 |
4
.ansible-lint
Normal file
4
.ansible-lint
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
skip_list:
|
||||||
|
- run-once
|
||||||
|
exclude_paths:
|
||||||
|
- roles/global_defaults/
|
||||||
471
README.md
471
README.md
@@ -1,111 +1,406 @@
|
|||||||
# Ansible-Bootstrap
|
# Ansible Bootstrap
|
||||||
|
|
||||||
An Ansible playbook for automating system bootstrap processes in an Infrastructure-as-Code manner, utilizing ArchISO as the foundational tool.
|
An Ansible playbook for automating Linux system bootstrap in an Infrastructure-as-Code manner. It uses the Arch Linux ISO as a foundational tool to provide an efficient and systematic method for the automatic deployment of a variety of Linux distributions on designated target systems, ensuring a standardized setup across different platforms.
|
||||||
|
|
||||||
# Info
|
Most roles are adaptable for use with systems beyond Arch Linux, requiring only that the target system can install the necessary package manager (e.g. `dnf` for RHEL-based systems). A replacement for the `arch-chroot` command may also be required; set `system.features.chroot.tool` accordingly.
|
||||||
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 and RHEL 9, 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.
|
|
||||||
|
|
||||||
# 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) |
|
|
||||||
| fedora | Fedora 41 |
|
|
||||||
| rhel8 | Red Hat Enterprise Linux 8 |
|
|
||||||
| rhel9 | Red Hat Enterprise Linux 9 |
|
|
||||||
| rocky | Rocky Linux 9.x |
|
|
||||||
| ubuntu | Ubuntu 24.10 (Oracular Oriole) |
|
|
||||||
| ubuntu-lts | Ubuntu 24.04 LTS (Noble Numbat) |
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [Overview](#1-overview)
|
1. [Supported Platforms](#1-supported-platforms)
|
||||||
2. [Global Variables](#2-global-variables)
|
2. [Compatibility Notes](#2-compatibility-notes)
|
||||||
3. [Inventory Variables](#3-inventory-variables)
|
3. [Configuration Model](#3-configuration-model)
|
||||||
4. [How to Use the Playbook](#4-how-to-use-the-playbook)
|
4. [Variable Reference](#4-variable-reference)
|
||||||
- 4.1 [Prerequisites](#41-prerequisites)
|
- 4.1 [Core Variables](#41-core-variables)
|
||||||
- 4.2 [Running the Playbook](#42-running-the-playbook)
|
- 4.2 [`system` Dictionary](#42-system-dictionary)
|
||||||
- 4.3 [Example Usage](#43-example-usage)
|
- 4.3 [`hypervisor` Dictionary](#43-hypervisor-dictionary)
|
||||||
|
- 4.4 [VMware Guest Operations](#44-vmware-guest-operations)
|
||||||
|
- 4.5 [Multi-Disk Schema](#45-multi-disk-schema)
|
||||||
|
- 4.6 [Advanced Partitioning Overrides](#46-advanced-partitioning-overrides)
|
||||||
|
5. [How to Use the Playbook](#5-how-to-use-the-playbook)
|
||||||
|
- 5.1 [Prerequisites](#51-prerequisites)
|
||||||
|
- 5.2 [Running the Playbook](#52-running-the-playbook)
|
||||||
|
- 5.3 [Example Usage](#53-example-usage)
|
||||||
|
6. [Security](#6-security)
|
||||||
|
7. [Operational Notes](#7-operational-notes)
|
||||||
|
8. [Safety](#8-safety)
|
||||||
|
|
||||||
## 1. Overview
|
## 1. Supported Platforms
|
||||||
|
|
||||||
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.
|
### Distributions
|
||||||
|
|
||||||
## 2. Global Variables
|
| `system.os` | Distribution | `system.version` |
|
||||||
|
| ------------ | ------------------------ | ------------------------------- |
|
||||||
|
| `almalinux` | AlmaLinux | `8`, `9`, `10` |
|
||||||
|
| `alpine` | Alpine Linux | latest (rolling) |
|
||||||
|
| `archlinux` | Arch Linux | latest (rolling) |
|
||||||
|
| `debian` | Debian | `10`, `11`, `12`, `13`, `unstable` |
|
||||||
|
| `fedora` | Fedora | `40`, `41`, `42`, `43` |
|
||||||
|
| `opensuse` | openSUSE Tumbleweed | latest (rolling) |
|
||||||
|
| `rhel` | Red Hat Enterprise Linux | `8`, `9`, `10` |
|
||||||
|
| `rocky` | Rocky Linux | `8`, `9`, `10` |
|
||||||
|
| `ubuntu` | Ubuntu | latest |
|
||||||
|
| `ubuntu-lts` | Ubuntu LTS | latest |
|
||||||
|
| `void` | Void Linux | latest (rolling) |
|
||||||
|
|
||||||
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.
|
### Hypervisors
|
||||||
|
|
||||||
| Variable | Description | Example Value |
|
| Hypervisor | `hypervisor.type` |
|
||||||
|-----------------------|--------------------------------------------------------------------|-----------------------------------------|
|
| ----------- | ----------------- |
|
||||||
| `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` |
|
| libvirt | `libvirt` |
|
||||||
| `rhel_iso` | Path to the RHEL ISO file, required for RHEL 8 and RHEL 9. |`local-btrfs:iso/rhel-9.4-x86_64-dvd.iso`|
|
| Proxmox VE | `proxmox` |
|
||||||
| `hypervisor` | Type of hypervisor. | `libvirt`, `proxmox`, `vmware`, `none` |
|
| VMware | `vmware` |
|
||||||
| `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` |
|
| Xen | `xen` |
|
||||||
| `hypervisor_node` | Hypervisor node name. | `node01` |
|
| Bare metal | `none` |
|
||||||
| `hypervisor_password` | Password for hypervisor authentication. | `123456` |
|
|
||||||
| `hypervisor_storage` | Storage identifier for VM disks. | `local-btrfs` |
|
|
||||||
| `hypervisor_url` | URL/IP address for the hypervisor interface. | `192.168.0.2` |
|
|
||||||
| `hypervisor_username` | Username for hypervisor authentication. | `root@pam` |
|
|
||||||
| `install_drive` | Drive where the system will be installed. | `/dev/sda` |
|
|
||||||
| `install_type` | Type of installation. | `virtual`, `physical` |
|
|
||||||
| `vlan_name` (optional)| VLAN for the VM's network interface. | `vlan100` |
|
|
||||||
|
|
||||||
To protect sensitive information, such as passwords, API keys, and other confidential variables (e.g., `hypervisor_password`), **it is recommended to use Ansible Vault**.
|
## 2. Compatibility Notes
|
||||||
|
|
||||||
## 3. Inventory Variables
|
- `rhel_iso` is required for `system.os: rhel`.
|
||||||
|
- RHEL installs should use `system.filesystem: ext4` or `system.filesystem: xfs` (not `btrfs`).
|
||||||
|
- For RHEL 8 specifically, prefer `ext4` over `xfs` if you hit installer/filesystem edge cases.
|
||||||
|
- `custom_iso: true` skips ArchISO validation and pacman preparation; your installer image must already provide required tooling.
|
||||||
|
- On non-Arch installers, set `system.features.chroot.tool` (`arch-chroot`, `chroot`, or `systemd-nspawn`) explicitly as needed.
|
||||||
|
|
||||||
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.
|
## 3. Configuration Model
|
||||||
|
|
||||||
| Variable | Description | Example Value |
|
The project uses two dict-based variables:
|
||||||
|-------------------------|-----------------------------------------------------------------------------------|----------------------------------------------------|
|
|
||||||
| `cis` (optional) | Adjusts the installation to be CIS level 3 conformant. | `true`, `false` |
|
|
||||||
| `filesystem` | Filesystem type for the VM's primary storage. | `btrfs`, `ext4`, `xfs` |
|
|
||||||
| `hostname` | The hostname assigned to the virtual machine or system. | `vm01` |
|
|
||||||
| `os` | Operating system to be installed on the VM. | `archlinux`, `almalinux`, `debian11`, `debian12`, `fedora`, `rhel8`, `rhel9`, `rocky`, `ubuntu`, `ubuntu-lts` |
|
|
||||||
| `root_password` | Root password for the VM or system, used for initial setup or secure access. | `SecurePass123` |
|
|
||||||
| `user_name` | Username for a user account within the VM, often used with cloud-init. | `adminuser` |
|
|
||||||
| `user_password` | Password for the user account within the VM. | `UserPass123` |
|
|
||||||
| `vm_ballo` (optional) | Ballooning memory size for the VM, used to adjust memory allocation dynamically. | `2048` |
|
|
||||||
| `vm_cpus` | Number of CPU cores assigned to the virtual machine. | `4` |
|
|
||||||
| `vm_dns` | DNS server IP address(es) for the virtual machine's network configuration. | `1.0.0.1`, `1.1.1.1` |
|
|
||||||
| `vm_gw` | Default gateway IP address for the virtual machine's network configuration. | `192.168.0.1` |
|
|
||||||
| `vm_id` | Unique identifier for the virtual machine. | `101` |
|
|
||||||
| `vm_ip` | IP address assigned to the virtual machine. | `192.168.0.10` |
|
|
||||||
| `vm_nm` (optional) | IP address netmask assigned to the virtual machine. | `255.255.255.0` |
|
|
||||||
| `vm_nms` (optional) | IP address netmask assigned to the virtual machine. | `24` |
|
|
||||||
| `vm_memory` | Amount of memory (in MB) allocated to the virtual machine. | `2048` |
|
|
||||||
| `vm_nif` | Network interface type or identifier for the VM's network connection. | `vmbr0` |
|
|
||||||
| `vm_path (optional)` | Path or folder where the VM configuration or related files will be stored. | `/var/lib/libvirt/images/` |
|
|
||||||
| `vm_size` | Disk size allocated for the VM's primary storage (in GB). | `20` |
|
|
||||||
|
|
||||||
## 4. How to Use the Playbook
|
- `system` for host/runtime/install configuration
|
||||||
|
- `hypervisor` for virtualization backend configuration
|
||||||
|
|
||||||
### 4.1 Prerequisites
|
These are normal Ansible variables and belong in host/group vars. You can define them in inventory host entries, `group_vars/*`, or `host_vars/*`. Dictionary variables are merged across scopes (`group_vars` -> `host_vars`) by project config (`hash_behaviour = merge`), so you can set shared values like `system.filesystem` once in group vars and override only host-specific keys per host.
|
||||||
|
|
||||||
Before running the playbook, ensure you have Ansible installed and configured correctly, and your inventory file is set up with the target systems defined.
|
### Variable Placement
|
||||||
|
|
||||||
### 4.2 Running the Playbook
|
| Location | Scope | Typical use |
|
||||||
|
| -------------------------- | ----------- | ------------------------------------------------------------ |
|
||||||
|
| `group_vars/all.yml` | All hosts | Shared defaults like `hypervisor`, `system.filesystem`, `boot_iso` |
|
||||||
|
| `group_vars/<group>.yml` | Group | Environment or role-specific defaults |
|
||||||
|
| `host_vars/<host>.yml` | Single host | Host-specific overrides |
|
||||||
|
| Inventory inline host vars | Single host | Inline definitions for quick setup |
|
||||||
|
|
||||||
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.
|
### Example Inventory
|
||||||
|
|
||||||
### 4.3 Example Usage
|
```yaml
|
||||||
|
all:
|
||||||
|
vars:
|
||||||
|
system:
|
||||||
|
filesystem: btrfs
|
||||||
|
boot_iso: "local:iso/archlinux-x86_64.iso"
|
||||||
|
hypervisor:
|
||||||
|
type: proxmox
|
||||||
|
url: pve01.example.com
|
||||||
|
username: root@pam
|
||||||
|
password: CHANGE_ME
|
||||||
|
host: pve01
|
||||||
|
storage: local-lvm
|
||||||
|
|
||||||
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:
|
children:
|
||||||
|
bootstrap:
|
||||||
```bash
|
hosts:
|
||||||
ansible-playbook -i inventory.yml -e @vars.yml main.yml
|
app01.example.com:
|
||||||
|
ansible_host: 10.0.0.10
|
||||||
|
system:
|
||||||
|
type: virtual
|
||||||
|
os: debian
|
||||||
|
version: "12"
|
||||||
|
name: app01.example.com
|
||||||
|
id: 101
|
||||||
|
cpus: 2
|
||||||
|
memory: 4096
|
||||||
|
balloon: 0
|
||||||
|
network:
|
||||||
|
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: 120
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: xfs
|
||||||
|
user:
|
||||||
|
name: ops
|
||||||
|
password: CHANGE_ME
|
||||||
|
key: "ssh-ed25519 AAAA..."
|
||||||
|
root:
|
||||||
|
password: CHANGE_ME
|
||||||
|
luks:
|
||||||
|
enabled: true
|
||||||
|
passphrase: CHANGE_ME
|
||||||
|
auto: true
|
||||||
|
method: tpm2
|
||||||
|
tpm2:
|
||||||
|
pcrs: "7"
|
||||||
|
features:
|
||||||
|
firewall:
|
||||||
|
enabled: true
|
||||||
|
backend: firewalld
|
||||||
|
toolkit: nftables
|
||||||
```
|
```
|
||||||
|
|
||||||
This command prompts Ansible to execute the `main.yml` playbook, applying configurations defined in both `vars.yml` and the inventory file.
|
## 4. Variable Reference
|
||||||
|
|
||||||
|
### 4.1 Core Variables
|
||||||
|
|
||||||
|
These top-level variables sit outside the `system`/`hypervisor` dictionaries.
|
||||||
|
|
||||||
|
| Variable | Type | Description |
|
||||||
|
| ------------ | ------ | ------------------------------------------------ |
|
||||||
|
| `boot_iso` | string | Path to the boot ISO image (required for virtual installs). |
|
||||||
|
| `rhel_iso` | string | Path to the RHEL ISO (required when `system.os: rhel`). |
|
||||||
|
| `custom_iso` | bool | Skip ArchISO validation and pacman setup. Default `false`. |
|
||||||
|
|
||||||
|
### 4.2 `system` Dictionary
|
||||||
|
|
||||||
|
Top-level host install/runtime settings. Use these keys under `system`.
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
| ------------ | ---------- | -------------------- | ---------------------------------------- |
|
||||||
|
| `type` | string | `virtual` | `virtual` or `physical` |
|
||||||
|
| `os` | string | empty | Target distribution (see [table](#distributions)) |
|
||||||
|
| `version` | string | empty | Version selector for distro families |
|
||||||
|
| `filesystem` | string | empty | `btrfs`, `ext4`, or `xfs` |
|
||||||
|
| `name` | string | inventory hostname | Final hostname |
|
||||||
|
| `id` | int/string | empty | VMID (required for Proxmox) |
|
||||||
|
| `cpus` | int | `0` | vCPU count |
|
||||||
|
| `memory` | int | `0` | Memory in MiB |
|
||||||
|
| `balloon` | int | `0` | Balloon memory in MiB |
|
||||||
|
| `path` | string | empty | Hypervisor folder/path (libvirt/vmware) |
|
||||||
|
| `packages` | list | `[]` | Additional packages installed post-reboot |
|
||||||
|
| `network` | dict | see below | Network configuration |
|
||||||
|
| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#45-multi-disk-schema)) |
|
||||||
|
| `user` | dict | see below | User account settings |
|
||||||
|
| `root` | dict | see below | Root account settings |
|
||||||
|
| `luks` | dict | see below | Encryption settings |
|
||||||
|
| `features` | dict | see below | Feature toggles |
|
||||||
|
|
||||||
|
#### `system.network`
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
| -------------- | ----------- | ------- | --------------------------------------------------- |
|
||||||
|
| `bridge` | string | empty | Hypervisor network/bridge name |
|
||||||
|
| `vlan` | string/int | empty | VLAN tag |
|
||||||
|
| `ip` | string | empty | Static IP (omit for DHCP) |
|
||||||
|
| `prefix` | int | empty | CIDR prefix for static IP |
|
||||||
|
| `gateway` | string | empty | Default gateway (static only) |
|
||||||
|
| `dns.servers` | list/string | `[]` | DNS resolvers; comma-separated string is normalized |
|
||||||
|
| `dns.search` | list/string | `[]` | Search domains; comma-separated string is normalized |
|
||||||
|
|
||||||
|
#### `system.user`
|
||||||
|
|
||||||
|
Credentials are prompted interactively by default via `vars_prompt` in `main.yml`, but can be supplied via inventory, vars files, or `-e` for non-interactive runs.
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
| ---------- | ------ | ------- | ------------------------------------- |
|
||||||
|
| `name` | string | empty | Username created on target |
|
||||||
|
| `password` | string | empty | User password (also used for sudo) |
|
||||||
|
| `key` | string | empty | SSH public key for `authorized_keys` |
|
||||||
|
|
||||||
|
#### `system.root`
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
| ---------- | ------ | ------- | -------------- |
|
||||||
|
| `password` | string | empty | Root password |
|
||||||
|
|
||||||
|
#### `system.luks`
|
||||||
|
|
||||||
|
LUKS container, unlock, and initramfs-related settings.
|
||||||
|
|
||||||
|
| Key | Type | Default | Allowed | Description |
|
||||||
|
| ------------ | ------ | ------------------ | -------------------------- | ------------------------------------------ |
|
||||||
|
| `enabled` | bool | `false` | `true`/`false` | Enable encrypted root workflow |
|
||||||
|
| `passphrase` | string | empty | any string | Passphrase used for format/open/enroll |
|
||||||
|
| `mapper` | string | `SYSTEM_DECRYPTED` | mapper name | Mapper name under `/dev/mapper` |
|
||||||
|
| `auto` | bool | `true` | `true`/`false` | Auto-unlock behavior toggle |
|
||||||
|
| `method` | string | `tpm2` | `tpm2`, `keyfile` | Auto-unlock backend when `auto=true` |
|
||||||
|
| `keysize` | int | `64` | positive int | Keyfile size (bytes) for keyfile mode |
|
||||||
|
| `options` | string | `discard,tries=3` | crypttab opts | Additional crypttab/kernel options |
|
||||||
|
| `type` | string | `luks2` | cryptsetup type | LUKS format type |
|
||||||
|
| `cipher` | string | `aes-xts-plain64` | cipher name | Cryptsetup cipher |
|
||||||
|
| `hash` | string | `sha512` | hash name | Cryptsetup hash |
|
||||||
|
| `iter` | int | `4000` | positive int | PBKDF iteration time (ms) |
|
||||||
|
| `bits` | int | `512` | positive int | Key size (bits) |
|
||||||
|
| `pbkdf` | string | `argon2id` | pbkdf name | PBKDF algorithm |
|
||||||
|
| `urandom` | bool | `true` | `true`/`false` | Use urandom during key generation |
|
||||||
|
| `verify` | bool | `true` | `true`/`false` | Verify passphrase during format |
|
||||||
|
|
||||||
|
#### `system.luks.tpm2`
|
||||||
|
|
||||||
|
TPM2-specific policy settings used when `system.luks.method: tpm2`.
|
||||||
|
|
||||||
|
| Key | Type | Default | Allowed | Description |
|
||||||
|
| ------ | ----------- | ------- | --------------------- | --------------------------------------------------- |
|
||||||
|
| `device` | string | `auto` | `auto` or device path | TPM2 device selector |
|
||||||
|
| `pcrs` | string/list | empty | PCR expression | PCR binding policy (e.g. `"7"` or `"0+7"`) |
|
||||||
|
|
||||||
|
#### `system.features`
|
||||||
|
|
||||||
|
Feature toggles for optional system configuration.
|
||||||
|
|
||||||
|
| Key | Type | Default | Allowed | Description |
|
||||||
|
| ------------------ | ------ | -------------- | ------------------------------------------ | ---------------------------------- |
|
||||||
|
| `cis.enabled` | bool | `false` | `true`/`false` | Enable CIS hardening role |
|
||||||
|
| `selinux.enabled` | bool | `true` | `true`/`false` | SELinux management |
|
||||||
|
| `firewall.enabled` | bool | `true` | `true`/`false` | Enable firewall role actions |
|
||||||
|
| `firewall.backend` | string | `firewalld` | `firewalld`, `ufw` | Firewall service backend |
|
||||||
|
| `firewall.toolkit` | string | `nftables` | `nftables`, `iptables` | Packet filtering toolkit |
|
||||||
|
| `ssh.enabled` | bool | `true` | `true`/`false` | SSH service/package management |
|
||||||
|
| `zstd.enabled` | bool | `true` | `true`/`false` | zstd related tuning |
|
||||||
|
| `swap.enabled` | bool | `true` | `true`/`false` | Swap setup toggle |
|
||||||
|
| `banner.motd` | bool | `false` | `true`/`false` | MOTD banner management |
|
||||||
|
| `banner.sudo` | bool | `true` | `true`/`false` | Sudo banner management |
|
||||||
|
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, `systemd-nspawn` | Chroot wrapper command |
|
||||||
|
|
||||||
|
### 4.3 `hypervisor` Dictionary
|
||||||
|
|
||||||
|
| Key | Type | Description |
|
||||||
|
| ------------ | ------ | -------------------------------------------------------- |
|
||||||
|
| `type` | string | `libvirt`, `proxmox`, `vmware`, `xen`, or `none` |
|
||||||
|
| `url` | string | Proxmox/VMware API host |
|
||||||
|
| `username` | string | API username |
|
||||||
|
| `password` | string | API password |
|
||||||
|
| `host` | string | Proxmox node name |
|
||||||
|
| `storage` | string | Proxmox/VMware storage identifier |
|
||||||
|
| `datacenter` | string | VMware datacenter |
|
||||||
|
| `cluster` | string | VMware cluster |
|
||||||
|
| `certs` | bool | TLS certificate validation for VMware |
|
||||||
|
| `ssh` | bool | VMware: enable SSH on guest and switch connection to SSH |
|
||||||
|
|
||||||
|
### 4.4 VMware Guest Operations
|
||||||
|
|
||||||
|
When `hypervisor.type: vmware` uses the `vmware_tools` connection, these Ansible connection variables are required.
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| ------------------------------- | -------------------------------------------------- |
|
||||||
|
| `ansible_vmware_tools_user` | Guest OS username for guest operations |
|
||||||
|
| `ansible_vmware_tools_password` | Guest OS password for guest operations |
|
||||||
|
| `ansible_vmware_guest_path` | VM inventory path (`/datacenter/vm/folder/name`) |
|
||||||
|
| `ansible_vmware_host` | vCenter/ESXi hostname |
|
||||||
|
| `ansible_vmware_user` | vCenter/ESXi API username |
|
||||||
|
| `ansible_vmware_password` | vCenter/ESXi API password |
|
||||||
|
| `ansible_vmware_validate_certs` | Enable/disable TLS certificate validation |
|
||||||
|
|
||||||
|
### 4.5 Multi-Disk Schema
|
||||||
|
|
||||||
|
`system.disks[0]` is always the OS disk. Additional entries define data disks.
|
||||||
|
|
||||||
|
| Key | Type | Description |
|
||||||
|
| ------------- | ------ | ---------------------------------------------------- |
|
||||||
|
| `size` | number | Disk size in GB (required for virtual installs) |
|
||||||
|
| `device` | string | Explicit block device (required for physical data disks) |
|
||||||
|
| `mount.path` | string | Mount point (for additional disks) |
|
||||||
|
| `mount.fstype`| string | `btrfs`, `ext4`, or `xfs` |
|
||||||
|
| `mount.label` | string | Optional filesystem label |
|
||||||
|
| `mount.opts` | string | Mount options (default: `defaults`) |
|
||||||
|
|
||||||
|
Virtual install example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
system:
|
||||||
|
disks:
|
||||||
|
- size: 80
|
||||||
|
- size: 200
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: xfs
|
||||||
|
label: DATA
|
||||||
|
opts: defaults,noatime
|
||||||
|
- size: 300
|
||||||
|
mount:
|
||||||
|
path: /backup
|
||||||
|
fstype: ext4
|
||||||
|
```
|
||||||
|
|
||||||
|
Physical install example (device paths required):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
system:
|
||||||
|
type: physical
|
||||||
|
disks:
|
||||||
|
- device: /dev/sda
|
||||||
|
size: 120
|
||||||
|
- device: /dev/sdb
|
||||||
|
size: 500
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: ext4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.6 Advanced Partitioning Overrides
|
||||||
|
|
||||||
|
Use these only when you need to override the default partition layout logic.
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
| ------------------------------ | ------------------------------------------------- | ------------ |
|
||||||
|
| `partitioning_efi_size_mib` | EFI system partition size in MiB | `512` |
|
||||||
|
| `partitioning_boot_size_mib` | Separate `/boot` size in MiB (when used) | `1024` |
|
||||||
|
| `partitioning_separate_boot` | Force a separate `/boot` partition | auto-derived |
|
||||||
|
| `partitioning_boot_fs_fstype` | Filesystem for `/boot` when separate | auto-derived |
|
||||||
|
| `partitioning_use_full_disk` | Consume remaining VG space for root LV | `true` |
|
||||||
|
|
||||||
|
## 5. How to Use the Playbook
|
||||||
|
|
||||||
|
### 5.1 Prerequisites
|
||||||
|
|
||||||
|
- Ansible installed on the control machine.
|
||||||
|
- Inventory file with target systems defined and variables configured.
|
||||||
|
- Disposable/non-production targets (the playbook enforces production-safety checks).
|
||||||
|
|
||||||
|
### 5.2 Running the Playbook
|
||||||
|
|
||||||
|
Execute the playbook using `ansible-playbook`, ensuring that all necessary variables are defined either in the inventory, in a vars file, or passed via `-e`. Credentials (`root_password`, `user_name`, `user_password`, `user_public_key`) are prompted interactively unless supplied through inventory or extra vars.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventory.yml main.yml
|
||||||
|
ansible-playbook -i inventory.yml main.yml -e @vars_example.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 Example Usage
|
||||||
|
|
||||||
|
Use the bundled example files as starting points for new inventories:
|
||||||
|
|
||||||
|
- `inventory_example.yml` -- Proxmox virtual setup
|
||||||
|
- `inventory_libvirt_example.yml` -- libvirt virtual setup
|
||||||
|
- `inventory_baremetal_example.yml` -- bare-metal physical setup
|
||||||
|
- `vars_example.yml` -- shared variable overrides
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Proxmox example
|
||||||
|
ansible-playbook -i inventory_example.yml main.yml
|
||||||
|
|
||||||
|
# libvirt example
|
||||||
|
ansible-playbook -i inventory_libvirt_example.yml main.yml
|
||||||
|
|
||||||
|
# Custom inventory with separate vars file
|
||||||
|
ansible-playbook -i inventory.yml main.yml -e @vars_example.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Security
|
||||||
|
|
||||||
|
To protect sensitive information such as passwords, API keys, and other confidential variables (e.g. `hypervisor.password`, `system.luks.passphrase`), **use Ansible Vault** instead of plaintext inventory files.
|
||||||
|
|
||||||
|
## 7. Operational Notes
|
||||||
|
|
||||||
|
- For virtual installs, `system.cpus`, `system.memory`, and `system.disks[0].size` are required and validated.
|
||||||
|
- For physical installs, sizing is derived from the detected install drive; set installer access (`ansible_user`/`ansible_password`) when the installer environment differs from the prompted user credentials.
|
||||||
|
- `system.network.dns.servers` and `system.network.dns.search` accept either YAML lists or comma-separated strings.
|
||||||
|
- `hypervisor.type` selects backend-specific provisioning and cleanup behavior.
|
||||||
|
- Guest tools are selected automatically by hypervisor: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`.
|
||||||
|
- With `system.luks.method: tpm2` on virtual installs, the virtualization role enables a TPM2 device where supported (libvirt/proxmox/vmware).
|
||||||
|
- With LUKS enabled on non-Arch targets, provisioning uses an ESP (512 MiB), a separate `/boot` (1 GiB), and the encrypted root; adjust sizes via `partitioning_efi_size_mib` and `partitioning_boot_size_mib` if needed.
|
||||||
|
- For VMware, `hypervisor.ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks.
|
||||||
|
- Molecule is scaffolded with a delegated driver and a no-op converge for lint-only validation.
|
||||||
|
|
||||||
|
## 8. Safety
|
||||||
|
|
||||||
|
This playbook intentionally aborts if it detects a non-live/production target. It also refuses to touch pre-existing VMs and only cleans up VMs created in the current run.
|
||||||
|
|
||||||
|
Always run lint after changes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-lint
|
||||||
|
```
|
||||||
|
|||||||
2
ansible.cfg
Normal file
2
ansible.cfg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[defaults]
|
||||||
|
hash_behaviour = merge
|
||||||
9
collections/requirements.yml
Normal file
9
collections/requirements.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
collections:
|
||||||
|
- name: ansible.posix
|
||||||
|
- name: community.general
|
||||||
|
- name: community.libvirt
|
||||||
|
- name: community.crypto
|
||||||
|
- name: community.proxmox
|
||||||
|
- name: community.vmware
|
||||||
|
- name: vmware.vmware
|
||||||
20
inventory_baremetal_example.yml
Normal file
20
inventory_baremetal_example.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
all:
|
||||||
|
vars:
|
||||||
|
hypervisor:
|
||||||
|
type: "none"
|
||||||
|
system:
|
||||||
|
filesystem: "ext4"
|
||||||
|
hosts:
|
||||||
|
baremetal01.example.com:
|
||||||
|
ansible_host: 10.0.0.162
|
||||||
|
ansible_user: root
|
||||||
|
ansible_password: "1234"
|
||||||
|
ansible_become_password: "1234"
|
||||||
|
system:
|
||||||
|
type: "physical"
|
||||||
|
os: "archlinux"
|
||||||
|
name: "baremetal01.example.com"
|
||||||
|
disks:
|
||||||
|
- device: "/dev/sda"
|
||||||
|
size: 120
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
[promox-kvm]
|
|
||||||
192.168.122.10
|
|
||||||
192.168.122.11
|
|
||||||
|
|
||||||
[promox-kvm:vars]
|
|
||||||
vm_gw=192.168.122.1
|
|
||||||
vm_dns=1.1.1.1
|
|
||||||
|
|
||||||
[192.168.122.10]
|
|
||||||
hostname=proxy
|
|
||||||
vm_id=300
|
|
||||||
os=archlinux
|
|
||||||
filesystem=btrfs
|
|
||||||
vm_memory=2048
|
|
||||||
vm_ballo=1024
|
|
||||||
vm_cpus=2
|
|
||||||
vm_size=5
|
|
||||||
vm_nif=vmbr1
|
|
||||||
|
|
||||||
[192.168.122.11]
|
|
||||||
hostname=database
|
|
||||||
vm_id=101
|
|
||||||
os=archlinux
|
|
||||||
filesystem=btrfs
|
|
||||||
vm_memory=6144
|
|
||||||
vm_ballo=3072
|
|
||||||
vm_cpus=4
|
|
||||||
vm_size=40
|
|
||||||
vm_nif=vmbr1
|
|
||||||
@@ -1,33 +1,129 @@
|
|||||||
|
---
|
||||||
all:
|
all:
|
||||||
vars:
|
vars:
|
||||||
hypervisor: 'proxmox'
|
hypervisor:
|
||||||
install_drive: '/dev/sda'
|
type: "proxmox"
|
||||||
cis: true
|
url: "pve01.example.com"
|
||||||
|
username: "root@pam"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
host: "pve01"
|
||||||
|
storage: "local-lvm"
|
||||||
|
boot_iso: "local:iso/archlinux-x86_64.iso"
|
||||||
children:
|
children:
|
||||||
promox-kvm:
|
proxmox:
|
||||||
hosts:
|
hosts:
|
||||||
192.168.122.10:
|
app01.example.com:
|
||||||
hostname: proxy
|
ansible_host: 10.0.0.10
|
||||||
vm_id: 100
|
system:
|
||||||
os: archlinux
|
filesystem: "btrfs"
|
||||||
filesystem: btrfs
|
type: "virtual"
|
||||||
vm_memory: "2048"
|
os: "archlinux"
|
||||||
vm_ballo: "1024"
|
name: "app01.example.com"
|
||||||
vm_cpus: "2"
|
id: 100
|
||||||
vm_size: "5"
|
cpus: 2
|
||||||
vm_nif: vmbr1
|
memory: 4096
|
||||||
vm_gw: 192.168.122.1
|
balloon: 0
|
||||||
vm_dns: 1.1.1.1
|
network:
|
||||||
192.168.122.11:
|
bridge: "vmbr0"
|
||||||
hostname: database
|
ip: 10.0.0.10
|
||||||
vm_id: 101
|
prefix: 24
|
||||||
os: rhel9
|
gateway: 10.0.0.1
|
||||||
filesystem: xfs
|
dns:
|
||||||
vm_memory: "6144"
|
servers:
|
||||||
vm_ballo: "3072"
|
- 1.1.1.1
|
||||||
vm_cpus: "4"
|
- 1.0.0.1
|
||||||
vm_size: "40"
|
search:
|
||||||
vm_nif: vmbr1
|
- example.com
|
||||||
vm_gw: 192.168.122.1
|
disks:
|
||||||
vm_dns: 1.1.1.1
|
- size: 40
|
||||||
rhel_iso: "local-btrfs:iso/rhel-9.4-x86_64-dvd.iso"
|
- size: 80
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: xfs
|
||||||
|
label: DATA
|
||||||
|
opts: defaults
|
||||||
|
user:
|
||||||
|
name: "ops"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "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
|
||||||
|
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
|
||||||
|
user:
|
||||||
|
name: "dbadmin"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "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"
|
||||||
|
|||||||
129
inventory_libvirt_example.yml
Normal file
129
inventory_libvirt_example.yml
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
user:
|
||||||
|
name: "web"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "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
|
||||||
|
user:
|
||||||
|
name: "db"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "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
|
||||||
|
user:
|
||||||
|
name: "compute"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "ssh-ed25519 AAAA..."
|
||||||
|
root:
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
features:
|
||||||
|
cis:
|
||||||
|
enabled: true
|
||||||
127
main.yml
127
main.yml
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
- name: Create and configure VMs
|
- name: Create and configure VMs
|
||||||
hosts: all
|
hosts: all
|
||||||
strategy: free
|
strategy: free # noqa: run-once[play]
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
become: true
|
become: true
|
||||||
vars_prompt:
|
vars_prompt:
|
||||||
@@ -10,6 +10,11 @@
|
|||||||
What is your username?
|
What is your username?
|
||||||
private: false
|
private: false
|
||||||
|
|
||||||
|
- name: user_public_key
|
||||||
|
prompt: |
|
||||||
|
What is your ssh key?
|
||||||
|
private: false
|
||||||
|
|
||||||
- name: user_password
|
- name: user_password
|
||||||
prompt: |
|
prompt: |
|
||||||
What is your password?
|
What is your password?
|
||||||
@@ -19,69 +24,107 @@
|
|||||||
prompt: |
|
prompt: |
|
||||||
What is your root password?
|
What is your root password?
|
||||||
confirm: true
|
confirm: true
|
||||||
vars_files: vars.yml
|
|
||||||
pre_tasks:
|
pre_tasks:
|
||||||
- name: Set ansible_python_interpreter
|
- name: Apply prompted authentication values to system input
|
||||||
when: os | lower in ["almalinux", "rhel9", "rhel8", "rocky"]
|
vars:
|
||||||
|
system_input: "{{ system | default({}) }}"
|
||||||
|
system_user_input: "{{ (system_input.user | default({})) if (system_input.user is mapping) else {} }}"
|
||||||
|
system_root_input: "{{ (system_input.root | default({})) if (system_input.root is mapping) else {} }}"
|
||||||
|
prompt_user_name: "{{ user_name | default(system_user_name | default(''), true) | string }}"
|
||||||
|
prompt_user_key: "{{ user_public_key | default(user_key | default(system_user_key | default(''), true), true) | string }}"
|
||||||
|
prompt_user_password: "{{ user_password | default(system_user_password | default(''), true) | string }}"
|
||||||
|
prompt_root_password: "{{ root_password | default(system_root_password | default(''), true) | string }}"
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
ansible_python_interpreter: /usr/bin/python3
|
system: >-
|
||||||
|
{{
|
||||||
|
system_input
|
||||||
|
| combine(
|
||||||
|
{
|
||||||
|
'user': {
|
||||||
|
'name': (
|
||||||
|
(system_user_input.name | default('') | string | length) > 0
|
||||||
|
) | ternary(system_user_input.name | string, prompt_user_name),
|
||||||
|
'key': (
|
||||||
|
(system_user_input.key | default('') | string | length) > 0
|
||||||
|
) | ternary(system_user_input.key | string, prompt_user_key),
|
||||||
|
'password': (
|
||||||
|
(system_user_input.password | default('') | string | length) > 0
|
||||||
|
) | ternary(system_user_input.password | string, prompt_user_password)
|
||||||
|
},
|
||||||
|
'root': {
|
||||||
|
'password': (
|
||||||
|
(system_root_input.password | default('') | string | length) > 0
|
||||||
|
) | ternary(system_root_input.password | string, prompt_root_password)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
recursive=True
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Set SSH Access
|
- name: Load global defaults
|
||||||
when: hypervisor != "vmware"
|
ansible.builtin.import_role:
|
||||||
ansible.builtin.set_fact:
|
name: global_defaults
|
||||||
ansible_user: "{{ user_name }}"
|
|
||||||
ansible_password: "{{ user_password }}"
|
|
||||||
ansible_become_password: "{{ user_password }}"
|
|
||||||
ansible_ssh_extra_args: '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
|
|
||||||
|
|
||||||
- name: Validate variables
|
- name: Perform safety checks
|
||||||
ansible.builtin.assert:
|
ansible.builtin.import_role:
|
||||||
that:
|
name: system_check
|
||||||
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
|
|
||||||
- filesystem in ["btrfs", "ext4", "xfs"]
|
|
||||||
- install_drive is defined
|
|
||||||
- os in ["archlinux", "almalinux", "debian11", "debian12", "fedora", "rhel8", "rhel9", "rocky", "ubuntu", "ubuntu-lts"]
|
|
||||||
- os not in ["rhel8", "rhel9"] or rhel_iso is defined
|
|
||||||
- (filesystem == "btrfs" and (vm_size | int) >= 10) or (filesystem != "btrfs" and (vm_size | int) >= 20)
|
|
||||||
fail_msg: Invalid input specified, please try again.
|
|
||||||
|
|
||||||
- name: Set connection
|
|
||||||
when: hypervisor == "vmware"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
ansible_connection: vmware_tools
|
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- role: virtualization
|
- role: virtualization
|
||||||
when: install_type == "virtual"
|
when: system_cfg.type == "virtual"
|
||||||
become: false
|
become: false
|
||||||
vars:
|
vars:
|
||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
- role: environment
|
- role: environment
|
||||||
vars:
|
vars:
|
||||||
ansible_connection: "{{ 'vmware_tools' if hypervisor == 'vmware' else 'ssh' }}"
|
ansible_connection: "{{ 'vmware_tools' if hypervisor_type == 'vmware' else 'ssh' }}"
|
||||||
|
|
||||||
- role: partitioning
|
- role: partitioning
|
||||||
vars:
|
vars:
|
||||||
boot_partition_suffix: 1
|
partitioning_boot_partition_suffix: 1
|
||||||
main_partition_suffix: 2
|
partitioning_main_partition_suffix: 2
|
||||||
|
|
||||||
- role: bootstrap
|
- role: bootstrap
|
||||||
|
|
||||||
- role: configuration
|
- role: configuration
|
||||||
|
|
||||||
- role: cis
|
- role: cis
|
||||||
when: cis | bool
|
when: system_cfg.features.cis.enabled | bool
|
||||||
|
|
||||||
- role: cleanup
|
- role: cleanup
|
||||||
when: install_type == "virtual"
|
when: system_cfg.type in ["virtual", "physical"]
|
||||||
vars:
|
become: false
|
||||||
ansible_connection: local
|
|
||||||
|
|
||||||
tasks:
|
post_tasks:
|
||||||
- name: Reboot system
|
- name: Set post-reboot connection flags
|
||||||
when: hypervisor != "libvirt"
|
ansible.builtin.set_fact:
|
||||||
ansible.builtin.command: reboot
|
post_reboot_can_connect: >-
|
||||||
failed_when: false
|
{{
|
||||||
changed_when: result.rc == 0
|
(ansible_connection | default('ssh')) != 'ssh'
|
||||||
register: result
|
or ((system_cfg.network.ip | default('') | string | length) > 0)
|
||||||
|
or (
|
||||||
|
system_cfg.type == 'physical'
|
||||||
|
and (ansible_host | default('') | string | length) > 0
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set final SSH credentials for post-reboot tasks
|
||||||
|
when:
|
||||||
|
- post_reboot_can_connect | bool
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ansible_user: "{{ system_cfg.user.name }}"
|
||||||
|
ansible_password: "{{ system_cfg.user.password }}"
|
||||||
|
ansible_become_password: "{{ system_cfg.user.password }}"
|
||||||
|
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||||
|
|
||||||
|
- name: Install post-reboot packages
|
||||||
|
when:
|
||||||
|
- post_reboot_can_connect | bool
|
||||||
|
- system_cfg.packages is defined
|
||||||
|
- system_cfg.packages | length > 0
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ system_cfg.packages }}"
|
||||||
|
state: present
|
||||||
|
|||||||
8
molecule/default/converge.yml
Normal file
8
molecule/default/converge.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
- 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."
|
||||||
19
molecule/default/molecule.yml
Normal file
19
molecule/default/molecule.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
9
molecule/default/verify.yml
Normal file
9
molecule/default/verify.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
- name: Molecule verify placeholder
|
||||||
|
hosts: all
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Verify placeholder
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- true
|
||||||
35
roles/bootstrap/tasks/almalinux.yml
Normal file
35
roles/bootstrap/tasks/almalinux.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap AlmaLinux
|
||||||
|
vars:
|
||||||
|
bootstrap_almalinux_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Install AlmaLinux base system
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
|
||||||
|
--installroot=/mnt --setopt=install_weak_deps=False
|
||||||
|
groupinstall -y core
|
||||||
|
register: bootstrap_almalinux_base_result
|
||||||
|
changed_when: bootstrap_almalinux_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_almalinux_extra }}
|
||||||
|
register: bootstrap_almalinux_extra_result
|
||||||
|
changed_when: bootstrap_almalinux_extra_result.rc == 0
|
||||||
|
|
||||||
|
- name: Reinstall kernel core
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
||||||
|
register: bootstrap_almalinux_kernel_result
|
||||||
|
changed_when: bootstrap_almalinux_kernel_result.rc == 0
|
||||||
33
roles/bootstrap/tasks/alpine.yml
Normal file
33
roles/bootstrap/tasks/alpine.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Alpine Linux
|
||||||
|
vars:
|
||||||
|
bootstrap_alpine_packages: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', 'bootstrap_alpine') | reject('equalto', '') | join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Install Alpine Linux packages
|
||||||
|
ansible.builtin.command: >
|
||||||
|
apk --root /mnt --no-cache add alpine-base
|
||||||
|
register: bootstrap_alpine_bootstrap_result
|
||||||
|
changed_when: bootstrap_alpine_bootstrap_result.rc == 0
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
when: bootstrap_alpine_packages | length > 0
|
||||||
|
ansible.builtin.command: >
|
||||||
|
apk --root /mnt add {{ bootstrap_alpine_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
|
||||||
11
roles/bootstrap/tasks/archlinux.yml
Normal file
11
roles/bootstrap/tasks/archlinux.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap ArchLinux
|
||||||
|
vars:
|
||||||
|
bootstrap_archlinux_packages: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
pacstrap /mnt {{ bootstrap_archlinux_packages | reject('equalto', '') | join(' ') }} --asexplicit
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
67
roles/bootstrap/tasks/debian.yml
Normal file
67
roles/bootstrap/tasks/debian.yml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Debian System
|
||||||
|
vars:
|
||||||
|
bootstrap_debian_release: >-
|
||||||
|
{{
|
||||||
|
'buster' if (os_version | string) == '10'
|
||||||
|
else 'bullseye' if (os_version | string) == '11'
|
||||||
|
else 'bookworm' if (os_version | string) == '12'
|
||||||
|
else 'trixie' if (os_version | string) == '13'
|
||||||
|
else 'sid' if (os_version | string) == 'unstable'
|
||||||
|
else 'trixie'
|
||||||
|
}}
|
||||||
|
bootstrap_debian_package_config: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
}}
|
||||||
|
bootstrap_debian_base_packages: >-
|
||||||
|
{{
|
||||||
|
bootstrap_debian_package_config.base
|
||||||
|
| default([])
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
bootstrap_debian_extra_packages: >-
|
||||||
|
{{
|
||||||
|
bootstrap_debian_package_config.extra
|
||||||
|
| default([])
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
bootstrap_debian_base_csv: "{{ bootstrap_debian_base_packages | join(',') }}"
|
||||||
|
bootstrap_debian_extra_args: >-
|
||||||
|
{{
|
||||||
|
bootstrap_debian_extra_packages
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Validate Debian package configuration
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- bootstrap_debian_package_config is mapping
|
||||||
|
- bootstrap_debian_package_config.base is defined
|
||||||
|
- bootstrap_debian_package_config.base is sequence
|
||||||
|
- bootstrap_debian_package_config.base is not string
|
||||||
|
- bootstrap_debian_package_config.extra is defined
|
||||||
|
- bootstrap_debian_package_config.extra is sequence
|
||||||
|
- bootstrap_debian_package_config.extra is not string
|
||||||
|
fail_msg: "bootstrap package definition for {{ bootstrap_var_key }} must be a mapping with base/extra lists."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Install Debian base system
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
debootstrap --include={{ bootstrap_debian_base_csv }}
|
||||||
|
{{ bootstrap_debian_release }} /mnt http://deb.debian.org/debian/
|
||||||
|
register: bootstrap_debian_base_result
|
||||||
|
changed_when: bootstrap_debian_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
when: bootstrap_debian_extra_packages | 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
|
||||||
35
roles/bootstrap/tasks/fedora.yml
Normal file
35
roles/bootstrap/tasks/fedora.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Fedora
|
||||||
|
vars:
|
||||||
|
bootstrap_fedora_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Install Fedora base system
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dnf --releasever={{ os_version }} --best --repo=fedora --repo=fedora-updates
|
||||||
|
--installroot=/mnt --setopt=install_weak_deps=False
|
||||||
|
groupinstall -y critical-path-base core
|
||||||
|
register: bootstrap_fedora_base_result
|
||||||
|
changed_when: bootstrap_fedora_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_fedora_extra }}
|
||||||
|
register: bootstrap_fedora_extra_result
|
||||||
|
changed_when: bootstrap_fedora_extra_result.rc == 0
|
||||||
|
|
||||||
|
- name: Reinstall kernel core
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
||||||
|
register: bootstrap_fedora_kernel_result
|
||||||
|
changed_when: bootstrap_fedora_kernel_result.rc == 0
|
||||||
@@ -1,106 +1,45 @@
|
|||||||
---
|
---
|
||||||
- name: Include Packages
|
|
||||||
ansible.builtin.include_vars:
|
|
||||||
file: packages.yml
|
|
||||||
name: role_packages
|
|
||||||
|
|
||||||
- name: Run OS-specific bootstrap process
|
- name: Run OS-specific bootstrap process
|
||||||
|
vars:
|
||||||
|
bootstrap_os_key: "{{ (os_resolved | default(os)) | lower }}"
|
||||||
|
bootstrap_var_key: "{{ 'bootstrap_' + ((os_resolved | default(os)) | lower | replace('-', '_')) }}"
|
||||||
block:
|
block:
|
||||||
- name: Bootstrap ArchLinux
|
- name: Include AlmaLinux bootstrap tasks
|
||||||
when: os | lower == 'archlinux'
|
when: bootstrap_os_key in ['almalinux', 'almalinux8', 'almalinux9', 'almalinux10']
|
||||||
ansible.builtin.command: pacstrap /mnt {{ role_packages.archlinux | join(' ') }} --asexplicit
|
ansible.builtin.include_tasks: almalinux.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Bootstrap Debian System
|
- name: Include Alpine bootstrap tasks
|
||||||
when: os | lower in ['debian11', 'debian12']
|
when: bootstrap_os_key == 'alpine'
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.include_tasks: alpine.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- debootstrap --include={{ role_packages[os].base | join(',') }} {{ 'bullseye' if os == 'debian11' else 'bookworm' }}
|
|
||||||
/mnt http://deb.debian.org/debian/
|
|
||||||
- arch-chroot /mnt apt install -y {{ role_packages[os].extra | join(' ') }}
|
|
||||||
- arch-chroot /mnt apt remove -y libcups2 libavahi-common3 libavahi-common-data
|
|
||||||
|
|
||||||
- name: Bootstrap Ubuntu System
|
- name: Include ArchLinux bootstrap tasks
|
||||||
when: os | lower in ['ubuntu', 'ubuntu-lts']
|
when: bootstrap_os_key == 'archlinux'
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.include_tasks: archlinux.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- debootstrap --include={{ role_packages[os].base | join(',') }} {{ 'oracular' if os == 'ubuntu' else 'noble' }}
|
|
||||||
/mnt http://archive.ubuntu.com/ubuntu/
|
|
||||||
- ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
- arch-chroot /mnt sed -i '1s|$| universe|' /etc/apt/sources.list
|
|
||||||
- arch-chroot /mnt apt update -y
|
|
||||||
- arch-chroot /mnt apt install -y {{ role_packages[os].extra | join(' ') }}
|
|
||||||
|
|
||||||
- name: Bootstrap AlmaLinux 9
|
- name: Include Debian bootstrap tasks
|
||||||
when: os | lower == 'almalinux'
|
when: bootstrap_os_key in ['debian10', 'debian11', 'debian12', 'debian13', 'debianunstable']
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.include_tasks: debian.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- dnf --releasever=9 --best --repo=alma-baseos --installroot=/mnt --setopt=install_weak_deps=False groupinstall -y base core
|
|
||||||
- ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
- arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False install -y {{ role_packages.almalinux | join(' ') }}
|
|
||||||
|
|
||||||
- name: Bootstrap Fedora 41
|
- name: Include Fedora bootstrap tasks
|
||||||
when: os | lower == 'fedora'
|
when: bootstrap_os_key in ['fedora', 'fedora40', 'fedora41', 'fedora42', 'fedora43']
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.include_tasks: fedora.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- dnf --releasever=41 --best --repo=fedora --repo=fedora-updates
|
|
||||||
--installroot=/mnt --setopt=install_weak_deps=False groupinstall -y critical-path-base core
|
|
||||||
- ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
- arch-chroot /mnt dnf --releasever=41 --setopt=install_weak_deps=False install -y {{ role_packages.fedora | join(' ') }}
|
|
||||||
- arch-chroot /mnt dnf reinstall -y kernel-core
|
|
||||||
|
|
||||||
- name: Bootstrap RockyLinux 9
|
- name: Include openSUSE bootstrap tasks
|
||||||
when: os | lower == 'rocky'
|
when: bootstrap_os_key == 'opensuse'
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.include_tasks: opensuse.yml
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- 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/systemd/resolve/resolv.conf /mnt/etc/resolv.conf
|
|
||||||
- arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False install -y {{ role_packages.rocky | join(' ') }}
|
|
||||||
|
|
||||||
- name: Bootstrap RHEL System
|
- name: Include Rocky bootstrap tasks
|
||||||
when: os | lower in ['rhel8', 'rhel9']
|
when: bootstrap_os_key in ['rocky', 'rocky8', 'rocky9', 'rocky10']
|
||||||
block:
|
ansible.builtin.include_tasks: rocky.yml
|
||||||
- name: Install base packages in chroot environment
|
|
||||||
ansible.builtin.command: >-
|
|
||||||
dnf --releasever={{ '8' if os == 'rhel8' else '9' }} --repo={{ os | lower }}-baseos
|
|
||||||
--installroot=/mnt
|
|
||||||
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
|
||||||
groupinstall -y base core
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Prepare chroot environment
|
- name: Include RHEL bootstrap tasks
|
||||||
ansible.builtin.shell: |
|
when: bootstrap_os_key in ['rhel8', 'rhel9', 'rhel10']
|
||||||
ln -sf /run/systemd/resolve/resolv.conf /mnt/etc/resolv.conf
|
ansible.builtin.include_tasks: rhel.yml
|
||||||
mkdir -p /mnt/usr/local/install/redhat/dvd
|
|
||||||
mount --bind /usr/local/install/redhat/dvd /mnt/usr/local/install/redhat/dvd
|
|
||||||
arch-chroot /mnt rpm --rebuilddb
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Copy RHEL repo file into chroot environment
|
- name: Include Ubuntu bootstrap tasks
|
||||||
ansible.builtin.copy:
|
when: bootstrap_os_key in ['ubuntu', 'ubuntu-lts']
|
||||||
src: /etc/yum.repos.d/{{ os | lower }}.repo
|
ansible.builtin.include_tasks: ubuntu.yml
|
||||||
dest: /mnt/etc/yum.repos.d/{{ os | lower }}.repo
|
|
||||||
mode: '0644'
|
|
||||||
remote_src: true
|
|
||||||
|
|
||||||
- name: Install additional packages in chroot
|
- name: Include Void bootstrap tasks
|
||||||
ansible.builtin.command: >-
|
when: bootstrap_os_key == 'void'
|
||||||
arch-chroot /mnt dnf --releasever={{ '8' if os == 'rhel8' else '9' }}
|
ansible.builtin.include_tasks: void.yml
|
||||||
--setopt=install_weak_deps=False install -y {{ role_packages[os] | join(' ') }}
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|||||||
33
roles/bootstrap/tasks/opensuse.yml
Normal file
33
roles/bootstrap/tasks/opensuse.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap openSUSE
|
||||||
|
vars:
|
||||||
|
bootstrap_opensuse_packages: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', 'bootstrap_opensuse') | reject('equalto', '') | join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Install openSUSE base packages
|
||||||
|
ansible.builtin.command: >
|
||||||
|
zypper --root /mnt --non-interactive install -t pattern patterns-base-base
|
||||||
|
register: bootstrap_opensuse_base_result
|
||||||
|
changed_when: bootstrap_opensuse_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Install openSUSE extra packages
|
||||||
|
when: bootstrap_opensuse_packages | length > 0
|
||||||
|
ansible.builtin.command: >
|
||||||
|
zypper --root /mnt --non-interactive install {{ bootstrap_opensuse_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
|
||||||
60
roles/bootstrap/tasks/rhel.yml
Normal file
60
roles/bootstrap/tasks/rhel.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap RHEL System
|
||||||
|
block:
|
||||||
|
- name: Install base packages in chroot environment
|
||||||
|
vars:
|
||||||
|
bootstrap_rhel_release: "{{ bootstrap_os_key | replace('rhel', '') }}"
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dnf --releasever={{ bootstrap_rhel_release }} --repo={{ bootstrap_os_key }}-baseos
|
||||||
|
--installroot=/mnt
|
||||||
|
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
||||||
|
groupinstall -y core base standard
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Ensure chroot RHEL DVD directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/usr/local/install/redhat/dvd
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Bind mount RHEL DVD into chroot
|
||||||
|
ansible.posix.mount:
|
||||||
|
src: /usr/local/install/redhat/dvd
|
||||||
|
path: /mnt/usr/local/install/redhat/dvd
|
||||||
|
fstype: none
|
||||||
|
opts: bind
|
||||||
|
state: mounted
|
||||||
|
|
||||||
|
- name: Rebuild RPM database inside chroot
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} rpm --rebuilddb"
|
||||||
|
register: bootstrap_rpm_rebuild_result
|
||||||
|
changed_when: bootstrap_rpm_rebuild_result.rc == 0
|
||||||
|
|
||||||
|
- name: Copy RHEL repo file into chroot environment
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: /etc/yum.repos.d/{{ bootstrap_os_key }}.repo
|
||||||
|
dest: /mnt/etc/yum.repos.d/redhat.repo
|
||||||
|
mode: "0644"
|
||||||
|
remote_src: true
|
||||||
|
|
||||||
|
- 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: >-
|
||||||
|
{{ chroot_command }} dnf --releasever={{ bootstrap_rhel_release }}
|
||||||
|
--setopt=install_weak_deps=False install -y {{ bootstrap_rhel_extra }}
|
||||||
|
register: bootstrap_result
|
||||||
|
changed_when: bootstrap_result.rc == 0
|
||||||
35
roles/bootstrap/tasks/rocky.yml
Normal file
35
roles/bootstrap/tasks/rocky.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Rocky Linux
|
||||||
|
vars:
|
||||||
|
bootstrap_rocky_extra: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Install Rocky Linux base system
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dnf --releasever={{ os_version }} --best --repo=baseos --repo=appstream
|
||||||
|
--installroot=/mnt --setopt=install_weak_deps=False
|
||||||
|
groupinstall -y core
|
||||||
|
register: bootstrap_rocky_base_result
|
||||||
|
changed_when: bootstrap_rocky_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
install -y {{ bootstrap_rocky_extra }}
|
||||||
|
register: bootstrap_rocky_extra_result
|
||||||
|
changed_when: bootstrap_rocky_extra_result.rc == 0
|
||||||
|
|
||||||
|
- name: Reinstall kernel core
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} dnf reinstall -y kernel-core"
|
||||||
|
register: bootstrap_rocky_kernel_result
|
||||||
|
changed_when: bootstrap_rocky_kernel_result.rc == 0
|
||||||
68
roles/bootstrap/tasks/ubuntu.yml
Normal file
68
roles/bootstrap/tasks/ubuntu.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Ubuntu System
|
||||||
|
vars:
|
||||||
|
bootstrap_ubuntu_release: >-
|
||||||
|
{{ 'plucky' if bootstrap_os_key == 'ubuntu' else 'noble' }}
|
||||||
|
bootstrap_ubuntu_package_config: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', bootstrap_var_key)
|
||||||
|
}}
|
||||||
|
bootstrap_ubuntu_base_packages: >-
|
||||||
|
{{
|
||||||
|
bootstrap_ubuntu_package_config.base
|
||||||
|
| default([])
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
bootstrap_ubuntu_extra_packages: >-
|
||||||
|
{{
|
||||||
|
bootstrap_ubuntu_package_config.extra
|
||||||
|
| default([])
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
bootstrap_ubuntu_base_csv: "{{ bootstrap_ubuntu_base_packages | join(',') }}"
|
||||||
|
bootstrap_ubuntu_extra: "{{ bootstrap_ubuntu_extra_packages | join(' ') }}"
|
||||||
|
block:
|
||||||
|
- name: Validate Ubuntu package configuration
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- bootstrap_ubuntu_package_config is mapping
|
||||||
|
- bootstrap_ubuntu_package_config.base is defined
|
||||||
|
- bootstrap_ubuntu_package_config.base is sequence
|
||||||
|
- bootstrap_ubuntu_package_config.base is not string
|
||||||
|
- bootstrap_ubuntu_package_config.extra is defined
|
||||||
|
- bootstrap_ubuntu_package_config.extra is sequence
|
||||||
|
- bootstrap_ubuntu_package_config.extra is not string
|
||||||
|
fail_msg: "bootstrap package definition for {{ bootstrap_var_key }} must be a mapping with base/extra lists."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Install Ubuntu base system
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
debootstrap --include={{ bootstrap_ubuntu_base_csv }}
|
||||||
|
{{ bootstrap_ubuntu_release }} /mnt
|
||||||
|
http://archive.ubuntu.com/ubuntu/
|
||||||
|
register: bootstrap_ubuntu_base_result
|
||||||
|
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Enable universe repository
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} sed -i '1s|$| universe|' /etc/apt/sources.list"
|
||||||
|
register: bootstrap_ubuntu_repo_result
|
||||||
|
changed_when: bootstrap_ubuntu_repo_result.rc == 0
|
||||||
|
|
||||||
|
- 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: Install extra packages
|
||||||
|
when: bootstrap_ubuntu_extra_packages | length > 0
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} apt install -y {{ bootstrap_ubuntu_extra }}"
|
||||||
|
register: bootstrap_ubuntu_extra_result
|
||||||
|
changed_when: bootstrap_ubuntu_extra_result.rc == 0
|
||||||
33
roles/bootstrap/tasks/void.yml
Normal file
33
roles/bootstrap/tasks/void.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
- name: Bootstrap Void Linux
|
||||||
|
vars:
|
||||||
|
bootstrap_void_packages: >-
|
||||||
|
{{
|
||||||
|
lookup('vars', 'bootstrap_void') | reject('equalto', '') | join(' ')
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Ensure chroot has resolv.conf
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Install Void Linux base packages
|
||||||
|
ansible.builtin.command: >
|
||||||
|
xbps-install -Sy -r /mnt -R https://repo-default.voidlinux.org/current void-repo-nonfree base-system
|
||||||
|
register: bootstrap_void_base_result
|
||||||
|
changed_when: bootstrap_void_base_result.rc == 0
|
||||||
|
|
||||||
|
- name: Install extra packages
|
||||||
|
when: bootstrap_void_packages | length > 0
|
||||||
|
ansible.builtin.command: >
|
||||||
|
xbps-install -Su -r /mnt {{ bootstrap_void_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
|
||||||
267
roles/bootstrap/vars/main.yml
Normal file
267
roles/bootstrap/vars/main.yml
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
---
|
||||||
|
bootstrap_rhel_base:
|
||||||
|
- bind-utils
|
||||||
|
- dhcp-client
|
||||||
|
- efibootmgr
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- glibc-langpack-de
|
||||||
|
- glibc-langpack-en
|
||||||
|
- lrzsz
|
||||||
|
- lvm2
|
||||||
|
- mtr
|
||||||
|
- ncurses-term
|
||||||
|
- nfs-utils
|
||||||
|
- policycoreutils-python-utils
|
||||||
|
- shim
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_rhel_versioned:
|
||||||
|
- grub2
|
||||||
|
- "{{ 'grub2-efi-x64' if os_version_major | default('') == '8' else 'grub2-efi' }}"
|
||||||
|
- "{{ 'grub2-tools-extra' if os_version_major | default('') in ['8', '9'] 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_rhel_common: "{{ bootstrap_rhel_base + bootstrap_rhel_versioned }}"
|
||||||
|
|
||||||
|
bootstrap_rhel8: "{{ bootstrap_rhel_common }}"
|
||||||
|
bootstrap_rhel9: "{{ bootstrap_rhel_common }}"
|
||||||
|
bootstrap_rhel10: "{{ bootstrap_rhel_common }}"
|
||||||
|
|
||||||
|
bootstrap_almalinux:
|
||||||
|
"{{ bootstrap_rhel_base + ['grub2', 'grub2-efi', 'dbus-daemon', 'lrzsz', 'nfsv4-client-utils', 'nc', 'ppp', 'zram-generator'] }}"
|
||||||
|
|
||||||
|
bootstrap_rocky:
|
||||||
|
"{{ bootstrap_rhel_base + ['grub2', 'grub2-efi', 'nfsv4-client-utils', 'nc', 'ppp', 'telnet', 'util-linux-core', 'wget', 'zram-generator'] }}"
|
||||||
|
|
||||||
|
bootstrap_almalinux8: "{{ bootstrap_almalinux }}"
|
||||||
|
bootstrap_almalinux9: "{{ bootstrap_almalinux }}"
|
||||||
|
bootstrap_almalinux10: "{{ bootstrap_almalinux }}"
|
||||||
|
|
||||||
|
bootstrap_rocky8: "{{ bootstrap_rocky }}"
|
||||||
|
bootstrap_rocky9: "{{ bootstrap_rocky }}"
|
||||||
|
bootstrap_rocky10: "{{ bootstrap_rocky }}"
|
||||||
|
|
||||||
|
bootstrap_fedora:
|
||||||
|
- bat
|
||||||
|
- bind-utils
|
||||||
|
- btrfs-progs
|
||||||
|
- cronie
|
||||||
|
- dhcp-client
|
||||||
|
- duf
|
||||||
|
- efibootmgr
|
||||||
|
- entr
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- 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 system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- vim-default-editor
|
||||||
|
- wget
|
||||||
|
- zoxide
|
||||||
|
- zram-generator
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_fedora40: "{{ bootstrap_fedora }}"
|
||||||
|
bootstrap_fedora41: "{{ bootstrap_fedora }}"
|
||||||
|
bootstrap_fedora42: "{{ bootstrap_fedora }}"
|
||||||
|
bootstrap_fedora43: "{{ bootstrap_fedora }}"
|
||||||
|
|
||||||
|
bootstrap_debian_base_common:
|
||||||
|
- btrfs-progs
|
||||||
|
- cron
|
||||||
|
- gnupg
|
||||||
|
- grub-efi
|
||||||
|
- grub-efi-amd64-signed
|
||||||
|
- grub2-common
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'cryptsetup-initramfs' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- locales
|
||||||
|
- logrotate
|
||||||
|
- lvm2
|
||||||
|
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- "{{ 'openssh-server' if system_cfg.features.ssh.enabled | bool else '' }}"
|
||||||
|
- python3
|
||||||
|
- xfsprogs
|
||||||
|
|
||||||
|
bootstrap_debian_extra_common:
|
||||||
|
- apparmor-utils
|
||||||
|
- bat
|
||||||
|
- chrony
|
||||||
|
- curl
|
||||||
|
- entr
|
||||||
|
- "{{ '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 '' }}"
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- htop
|
||||||
|
- jq
|
||||||
|
- libpam-pwquality
|
||||||
|
- lrzsz
|
||||||
|
- mtr
|
||||||
|
- ncdu
|
||||||
|
- net-tools
|
||||||
|
- network-manager
|
||||||
|
- python-is-python3
|
||||||
|
- ripgrep
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- sudo
|
||||||
|
- syslog-ng
|
||||||
|
- tcpd
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- wget
|
||||||
|
- zstd
|
||||||
|
|
||||||
|
bootstrap_debian_extra_versioned:
|
||||||
|
- linux-image-amd64
|
||||||
|
- "{{ 'duf' if (os_version | string) not in ['10', '11'] else '' }}"
|
||||||
|
- "{{ 'fastfetch' if (os_version | string) in ['12', '13', 'unstable'] else '' }}"
|
||||||
|
- "{{ 'neofetch' if (os_version | string) == '12' else '' }}"
|
||||||
|
- "{{ 'software-properties-common' if (os_version | string) not in ['13', 'unstable'] else '' }}"
|
||||||
|
- "{{ 'systemd-zram-generator' if (os_version | string) not in ['10', '11'] else '' }}"
|
||||||
|
- "{{ 'tldr' if (os_version | string) not in ['13', 'unstable'] else '' }}"
|
||||||
|
|
||||||
|
bootstrap_debian:
|
||||||
|
base: "{{ bootstrap_debian_base_common }}"
|
||||||
|
extra: "{{ bootstrap_debian_extra_common + bootstrap_debian_extra_versioned }}"
|
||||||
|
|
||||||
|
bootstrap_debian10: "{{ bootstrap_debian }}"
|
||||||
|
bootstrap_debian11: "{{ bootstrap_debian }}"
|
||||||
|
bootstrap_debian12: "{{ bootstrap_debian }}"
|
||||||
|
bootstrap_debian13: "{{ bootstrap_debian }}"
|
||||||
|
bootstrap_debianunstable: "{{ bootstrap_debian }}"
|
||||||
|
|
||||||
|
bootstrap_ubuntu:
|
||||||
|
base:
|
||||||
|
- linux-image-generic
|
||||||
|
extra: >-
|
||||||
|
{{
|
||||||
|
bootstrap_debian_base_common
|
||||||
|
+ bootstrap_debian_extra_common
|
||||||
|
+ ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio', 'ncurses-term', 'software-properties-common', 'systemd-zram-generator', 'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
|
||||||
|
}}
|
||||||
|
|
||||||
|
bootstrap_ubuntu_lts:
|
||||||
|
base:
|
||||||
|
- linux-image-generic
|
||||||
|
extra: >-
|
||||||
|
{{
|
||||||
|
bootstrap_debian_base_common
|
||||||
|
+ bootstrap_debian_extra_common
|
||||||
|
+ ['bash-completion', 'dnsutils', 'duf', 'eza', 'fdupes', 'fio', 'ncurses-term', 'software-properties-common', 'systemd-zram-generator', 'tldr', 'traceroute', 'util-linux-extra', 'yq', 'zoxide']
|
||||||
|
}}
|
||||||
|
|
||||||
|
bootstrap_archlinux:
|
||||||
|
- base
|
||||||
|
- btrfs-progs
|
||||||
|
- cronie
|
||||||
|
- dhcpcd
|
||||||
|
- efibootmgr
|
||||||
|
- fastfetch
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'iptables-nft' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- fish
|
||||||
|
- fzf
|
||||||
|
- grub
|
||||||
|
- htop
|
||||||
|
- libpwquality
|
||||||
|
- linux
|
||||||
|
- logrotate
|
||||||
|
- lrzsz
|
||||||
|
- lsof
|
||||||
|
- lvm2
|
||||||
|
- ncdu
|
||||||
|
- networkmanager
|
||||||
|
- nfs-utils
|
||||||
|
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
|
||||||
|
- ppp
|
||||||
|
- prometheus-node-exporter
|
||||||
|
- python-psycopg2
|
||||||
|
- reflector
|
||||||
|
- rsync
|
||||||
|
- sudo
|
||||||
|
- tldr
|
||||||
|
- tmux
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- vim
|
||||||
|
- wireguard-tools
|
||||||
|
- zram-generator
|
||||||
|
|
||||||
|
bootstrap_alpine:
|
||||||
|
- alpine-base
|
||||||
|
- vim
|
||||||
|
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
|
||||||
|
bootstrap_opensuse:
|
||||||
|
- vim
|
||||||
|
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
|
|
||||||
|
bootstrap_void:
|
||||||
|
- vim
|
||||||
|
- "{{ 'openssh' if system_cfg.features.ssh.enabled | bool else '' }}"
|
||||||
|
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
||||||
|
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
||||||
|
- "{{ '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' else '' }}"
|
||||||
|
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
||||||
|
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
||||||
|
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
---
|
|
||||||
almalinux:
|
|
||||||
- bind-utils
|
|
||||||
- cloud-init
|
|
||||||
- dbus-daemon
|
|
||||||
- dhcp-client
|
|
||||||
- efibootmgr
|
|
||||||
- glibc-langpack-de
|
|
||||||
- glibc-langpack-en
|
|
||||||
- grub2
|
|
||||||
- grub2-efi
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nc
|
|
||||||
- nfs-utils
|
|
||||||
- nfsv4-client-utils
|
|
||||||
- open-vm-tools
|
|
||||||
- ppp
|
|
||||||
- shim
|
|
||||||
- telnet
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
archlinux:
|
|
||||||
- base
|
|
||||||
- btrfs-progs
|
|
||||||
- cloud-init
|
|
||||||
- cronie
|
|
||||||
- dhcpcd
|
|
||||||
- efibootmgr
|
|
||||||
- firewalld
|
|
||||||
- fish
|
|
||||||
- grub
|
|
||||||
- htop
|
|
||||||
- libpwquality
|
|
||||||
- linux
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- lsof
|
|
||||||
- lvm2
|
|
||||||
- ncdu
|
|
||||||
- neofetch
|
|
||||||
- networkmanager
|
|
||||||
- nfs-utils
|
|
||||||
- openssh
|
|
||||||
- open-vm-tools
|
|
||||||
- ppp
|
|
||||||
- prometheus-node-exporter
|
|
||||||
- python-psycopg2
|
|
||||||
- qemu-guest-agent
|
|
||||||
- reflector
|
|
||||||
- rsync
|
|
||||||
- screen
|
|
||||||
- sudo
|
|
||||||
- vim
|
|
||||||
- wireguard-tools
|
|
||||||
|
|
||||||
debian11:
|
|
||||||
base:
|
|
||||||
- apparmor-utils
|
|
||||||
- btrfs-progs
|
|
||||||
- chrony
|
|
||||||
- cron
|
|
||||||
- gnupg
|
|
||||||
- grub-efi
|
|
||||||
- grub-efi-amd64-signed
|
|
||||||
- grub2-common
|
|
||||||
- linux-image-amd64
|
|
||||||
- locales
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- net-tools
|
|
||||||
- openssh-server
|
|
||||||
- python3
|
|
||||||
- sudo
|
|
||||||
- xfsprogs
|
|
||||||
|
|
||||||
extra:
|
|
||||||
- cloud-init
|
|
||||||
- curl
|
|
||||||
- firewalld
|
|
||||||
- fish
|
|
||||||
- htop
|
|
||||||
- libpam-pwquality
|
|
||||||
- lrzsz
|
|
||||||
- ncdu
|
|
||||||
- neofetch
|
|
||||||
- network-manager
|
|
||||||
- open-vm-tools
|
|
||||||
- python-is-python3
|
|
||||||
- rsync
|
|
||||||
- screen
|
|
||||||
- software-properties-common
|
|
||||||
- syslog-ng
|
|
||||||
- tcpd
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
debian12:
|
|
||||||
base:
|
|
||||||
- btrfs-progs
|
|
||||||
- cron
|
|
||||||
- gnupg
|
|
||||||
- grub-efi
|
|
||||||
- grub-efi-amd64-signed
|
|
||||||
- grub2-common
|
|
||||||
- linux-image-amd64
|
|
||||||
- locales
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- xfsprogs
|
|
||||||
|
|
||||||
extra:
|
|
||||||
- apparmor-utils
|
|
||||||
- chrony
|
|
||||||
- cloud-init
|
|
||||||
- curl
|
|
||||||
- firewalld
|
|
||||||
- fish
|
|
||||||
- htop
|
|
||||||
- libpam-pwquality
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- ncdu
|
|
||||||
- neofetch
|
|
||||||
- net-tools
|
|
||||||
- network-manager
|
|
||||||
- open-vm-tools
|
|
||||||
- openssh-server
|
|
||||||
- python-is-python3
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- screen
|
|
||||||
- software-properties-common
|
|
||||||
- sudo
|
|
||||||
- syslog-ng
|
|
||||||
- tcpd
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
fedora:
|
|
||||||
- bind-utils
|
|
||||||
- btrfs-progs
|
|
||||||
- cloud-init
|
|
||||||
- cronie
|
|
||||||
- dhcp-client
|
|
||||||
- efibootmgr
|
|
||||||
- glibc-langpack-de
|
|
||||||
- glibc-langpack-en
|
|
||||||
- grub2
|
|
||||||
- grub2-efi
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nc
|
|
||||||
- nfs-utils
|
|
||||||
- nfsv4-client-utils
|
|
||||||
- open-vm-tools
|
|
||||||
- polkit
|
|
||||||
- ppp
|
|
||||||
- shim
|
|
||||||
- telnet
|
|
||||||
- vim-default-editor
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
rhel8:
|
|
||||||
- cloud-init
|
|
||||||
- dhcp-client
|
|
||||||
- efibootmgr
|
|
||||||
- grub2
|
|
||||||
- grub2-efi-x64
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nfs-utils
|
|
||||||
- open-vm-tools
|
|
||||||
- shim
|
|
||||||
- telnet
|
|
||||||
- vim
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
rhel9:
|
|
||||||
- cloud-init
|
|
||||||
- dhcp-client
|
|
||||||
- efibootmgr
|
|
||||||
- grub2
|
|
||||||
- grub2-efi
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nfs-utils
|
|
||||||
- open-vm-tools
|
|
||||||
- shim
|
|
||||||
- telnet
|
|
||||||
- vim
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
rocky:
|
|
||||||
- bind-utils
|
|
||||||
- cloud-init
|
|
||||||
- dbus-daemon
|
|
||||||
- dhcp-client
|
|
||||||
- efibootmgr
|
|
||||||
- glibc-langpack-de
|
|
||||||
- glibc-langpack-en
|
|
||||||
- grub2
|
|
||||||
- grub2-efi
|
|
||||||
- lrzsz
|
|
||||||
- lvm2
|
|
||||||
- nc
|
|
||||||
- nfs-utils
|
|
||||||
- nfsv4-client-utils
|
|
||||||
- open-vm-tools
|
|
||||||
- ppp
|
|
||||||
- shim
|
|
||||||
- telnet
|
|
||||||
- util-linux-core
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
ubuntu:
|
|
||||||
base:
|
|
||||||
- btrfs-progs
|
|
||||||
- cron
|
|
||||||
- gnupg
|
|
||||||
- grub-efi
|
|
||||||
- grub-efi-amd64-signed
|
|
||||||
- grub2-common
|
|
||||||
- initramfs-tools
|
|
||||||
- linux-image-generic
|
|
||||||
- locales
|
|
||||||
- lvm2
|
|
||||||
- xfsprogs
|
|
||||||
|
|
||||||
extra:
|
|
||||||
- apparmor-utils
|
|
||||||
- bash-completion
|
|
||||||
- chrony
|
|
||||||
- cloud-init
|
|
||||||
- curl
|
|
||||||
- dnsutils
|
|
||||||
- firewalld
|
|
||||||
- fish
|
|
||||||
- htop
|
|
||||||
- libpam-pwquality
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- ncdu
|
|
||||||
- net-tools
|
|
||||||
- network-manager
|
|
||||||
- open-vm-tools
|
|
||||||
- openssh-server
|
|
||||||
- python-is-python3
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- screen
|
|
||||||
- software-properties-common
|
|
||||||
- sudo
|
|
||||||
- syslog-ng
|
|
||||||
- tcpd
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
|
|
||||||
ubuntu-lts:
|
|
||||||
base:
|
|
||||||
- btrfs-progs
|
|
||||||
- cron
|
|
||||||
- gnupg
|
|
||||||
- grub-efi
|
|
||||||
- grub-efi-amd64-signed
|
|
||||||
- grub2-common
|
|
||||||
- initramfs-tools
|
|
||||||
- linux-image-generic
|
|
||||||
- locales
|
|
||||||
- lvm2
|
|
||||||
- xfsprogs
|
|
||||||
|
|
||||||
extra:
|
|
||||||
- apparmor-utils
|
|
||||||
- bash-completion
|
|
||||||
- chrony
|
|
||||||
- cloud-init
|
|
||||||
- curl
|
|
||||||
- dnsutils
|
|
||||||
- firewalld
|
|
||||||
- fish
|
|
||||||
- htop
|
|
||||||
- libpam-pwquality
|
|
||||||
- logrotate
|
|
||||||
- lrzsz
|
|
||||||
- ncdu
|
|
||||||
- net-tools
|
|
||||||
- network-manager
|
|
||||||
- open-vm-tools
|
|
||||||
- openssh-server
|
|
||||||
- python-is-python3
|
|
||||||
- python3
|
|
||||||
- rsync
|
|
||||||
- screen
|
|
||||||
- software-properties-common
|
|
||||||
- sudo
|
|
||||||
- syslog-ng
|
|
||||||
- tcpd
|
|
||||||
- vim
|
|
||||||
- wget
|
|
||||||
- zstd
|
|
||||||
21
roles/cis/defaults/main.yml
Normal file
21
roles/cis/defaults/main.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
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 != "rhel" else None,
|
||||||
|
{
|
||||||
|
"path": "/mnt/usr/bin/"
|
||||||
|
+ ("fusermount3" if os in ["archlinux", "fedora", "rocky"] or os == "rhel" or (os == "debian" and (os_version | string) == "12") else "fusermount"),
|
||||||
|
"mode": "755"
|
||||||
|
},
|
||||||
|
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian" and (os_version | string) == "11" else "write"), "mode": "755" }
|
||||||
|
] | reject("none")
|
||||||
|
}}
|
||||||
15
roles/cis/tasks/auth.yml
Normal file
15
roles/cis/tasks/auth.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure the Default UMASK is Set Correctly
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "/mnt/etc/profile"
|
||||||
|
regexp: "^(\\s*)umask\\s+\\d+"
|
||||||
|
line: "umask 027"
|
||||||
|
|
||||||
|
- name: Prevent Login to Accounts With Empty Password
|
||||||
|
ansible.builtin.replace:
|
||||||
|
dest: "{{ item }}"
|
||||||
|
regexp: "\\s*nullok"
|
||||||
|
replace: ""
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/pam.d/system-auth
|
||||||
|
- /mnt/etc/pam.d/password-auth
|
||||||
12
roles/cis/tasks/crypto.yml
Normal file
12
roles/cis/tasks/crypto.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
- name: Configure System Cryptography Policy
|
||||||
|
when: os == "rhel" or os in ["almalinux", "rocky"]
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
||||||
|
register: cis_crypto_policy_result
|
||||||
|
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
|
||||||
|
|
||||||
|
- name: Mask Systemd Services
|
||||||
|
ansible.builtin.command: >
|
||||||
|
{{ chroot_command }} systemctl mask {{ 'nftables' if system_cfg.features.firewall.toolkit == 'iptables' else 'iptables' }} bluetooth rpcbind
|
||||||
|
register: cis_mask_services_result
|
||||||
|
changed_when: cis_mask_services_result.rc == 0
|
||||||
19
roles/cis/tasks/files.yml
Normal file
19
roles/cis/tasks/files.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure files exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: touch
|
||||||
|
mode: "0600"
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/at.allow
|
||||||
|
- /mnt/etc/cron.allow
|
||||||
|
- /mnt/etc/hosts.allow
|
||||||
|
- /mnt/etc/hosts.deny
|
||||||
|
|
||||||
|
- name: Ensure files do not exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/at.deny
|
||||||
|
- /mnt/etc/cron.deny
|
||||||
@@ -1,183 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: Configurationg System for CIS conformity
|
- name: Include CIS hardening tasks
|
||||||
block:
|
ansible.builtin.include_tasks: "{{ cis_task }}"
|
||||||
- name: Disable Kernel Modules
|
loop:
|
||||||
ansible.builtin.copy:
|
- modules.yml
|
||||||
dest: /mnt/etc/modprobe.d/cis.conf
|
- sysctl.yml
|
||||||
mode: '0644'
|
- auth.yml
|
||||||
content: |
|
- crypto.yml
|
||||||
CIS LVL 3 Restrictions
|
- files.yml
|
||||||
install freevxfs /bin/true
|
- security_lines.yml
|
||||||
install jffs2 /bin/true
|
- permissions.yml
|
||||||
install hfs /bin/true
|
- sshd.yml
|
||||||
install hfsplus /bin/true
|
loop_control:
|
||||||
install squashfs /bin/true
|
loop_var: cis_task
|
||||||
install udf /bin/true
|
|
||||||
install usb-storage /bin/true
|
|
||||||
|
|
||||||
install dccp /bin/true
|
|
||||||
install sctp /bin/true
|
|
||||||
install rds /bin/true
|
|
||||||
install tipc /bin/true
|
|
||||||
|
|
||||||
- name: Create USB Rules
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh
|
|
||||||
mode: '0644'
|
|
||||||
content: |
|
|
||||||
By default, disable all.
|
|
||||||
ACTION=="add", SUBSYSTEMS=="usb", TEST=="authorized_default", ATTR{authorized_default}="0"
|
|
||||||
|
|
||||||
Enable hub devices.
|
|
||||||
ACTION=="add", ATTR{bDeviceClass}=="09", TEST=="authorized", ATTR{authorized}="1"
|
|
||||||
|
|
||||||
Enables keyboard devices
|
|
||||||
ACTION=="add", ATTR{product}=="*[Kk]eyboard*", TEST=="authorized", ATTR{authorized}="1"
|
|
||||||
|
|
||||||
PS2-USB converter
|
|
||||||
ACTION=="add", ATTR{product}=="*Thinnet TM*", TEST=="authorized", ATTR{authorized}="1"
|
|
||||||
|
|
||||||
- name: Create a consolidated sysctl configuration file
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/sysctl.d/10-cis.conf
|
|
||||||
mode: '0644'
|
|
||||||
content: |
|
|
||||||
## CIS Sysctl configurations
|
|
||||||
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.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
|
|
||||||
|
|
||||||
# - name: Adjust login.defs
|
|
||||||
# replace:
|
|
||||||
# path: /mnt/etc/login.defs
|
|
||||||
# regexp: "{{ item.regexp }}"
|
|
||||||
# replace: "{{ item.replace }}"
|
|
||||||
# loop:
|
|
||||||
# - { regexp: '^PASS_MAX_DAYS.*', replace: 'PASS_MAX_DAYS 90' }
|
|
||||||
# - { regexp: '^PASS_MIN_DAYS.*', replace: 'PASS_MIN_DAYS 7' }
|
|
||||||
# - { regexp: '^UMASK.*', replace: 'UMASK 027' }
|
|
||||||
|
|
||||||
- name: Ensure files exist
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: touch
|
|
||||||
mode: "0600"
|
|
||||||
loop:
|
|
||||||
- /mnt/etc/at.allow
|
|
||||||
- /mnt/etc/cron.allow
|
|
||||||
- /mnt/etc/hosts.allow
|
|
||||||
- /mnt/etc/hosts.deny
|
|
||||||
|
|
||||||
- name: Add Security related lines into config files
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
line: "{{ item.content }}"
|
|
||||||
loop:
|
|
||||||
- { path: /mnt/etc/security/limits.conf, content: "* hard core 0" }
|
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: minlen = 14 }
|
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: dcredit = -1 }
|
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: ucredit = -1 }
|
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 }
|
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 }
|
|
||||||
- { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"] else "bash.bashrc" }}', content: umask 077 }
|
|
||||||
- { path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"] else "bash.bashrc" }}', content: export TMOUT=3000 }
|
|
||||||
- { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent }
|
|
||||||
- { path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log" }
|
|
||||||
- { path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so }
|
|
||||||
- { path: '/mnt/etc/{{ "pam.d/common-auth" if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"]
|
|
||||||
else "authselect/system-auth" if os == "fedora" else "pam.d/system-auth" }}',
|
|
||||||
content: auth required pam_faillock.so onerr=fail audit silent deny=5 unlock_time=900 }
|
|
||||||
- { path: '/mnt/etc/{{ "pam.d/common-account" if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] else "authselect/system-auth"
|
|
||||||
if os == "fedora" else "pam.d/system-auth" }}', content: account required pam_faillock.so }
|
|
||||||
- { path: '/mnt/etc/pam.d/{{ "common-password" if os in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] else "passwd" }}',
|
|
||||||
content: "password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5" }
|
|
||||||
- { path: /mnt/etc/hosts.deny, content: "ALL: ALL" }
|
|
||||||
- { path: /mnt/etc/hosts.allow, content: "sshd: ALL" }
|
|
||||||
|
|
||||||
- name: Set permissions for various files and directories
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item.path }}"
|
|
||||||
owner: "{{ item.owner | default(omit) }}"
|
|
||||||
group: "{{ item.group | default(omit) }}"
|
|
||||||
mode: "{{ item.mode }}"
|
|
||||||
loop: >
|
|
||||||
{{ [
|
|
||||||
{ "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"] else None,
|
|
||||||
{ "path": "/mnt/usr/bin/" + ("fusermount3" if os in ["almalinux", "archlinux", "debian12", "fedora", "rhel9", "rocky"]
|
|
||||||
else "fusermount"), "mode": "755" },
|
|
||||||
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" }
|
|
||||||
] | reject("none") }}
|
|
||||||
|
|
||||||
- name: Adjust SSHD config
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
regexp: ^\s*#?{{ item.option }}\s+.*$
|
|
||||||
line: "{{ item.option }} {{ item.value }}"
|
|
||||||
with_items:
|
|
||||||
- { 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: ChallengeResponseAuthentication, value: "no" }
|
|
||||||
- { option: GatewayPorts, value: "no" }
|
|
||||||
- { option: X11Forwarding, value: "no" }
|
|
||||||
- { option: PermitUserEnvironment, value: "no" }
|
|
||||||
- { option: ClientAliveInterval, value: "300" }
|
|
||||||
- { option: ClientAliveCountMax, value: "0" }
|
|
||||||
- { option: PermitTunnel, value: "no" }
|
|
||||||
- { option: Banner, value: /etc/issue.net }
|
|
||||||
|
|
||||||
- name: Append CIS Specific configurations to sshd_config
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
line: |2-
|
|
||||||
|
|
||||||
## CIS Specific
|
|
||||||
Protocol 2
|
|
||||||
|
|
||||||
### Ciphers and keying ###
|
|
||||||
RekeyLimit 512M 6h
|
|
||||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
|
|
||||||
Ciphers chacha20-poly1305@openssh.com,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,hmac-sha2-512,hmac-sha2-256
|
|
||||||
###########################
|
|
||||||
|
|
||||||
AllowStreamLocalForwarding no
|
|
||||||
PermitUserRC no
|
|
||||||
|
|
||||||
AllowUsers *
|
|
||||||
AllowGroups *
|
|
||||||
DenyUsers nobody
|
|
||||||
DenyGroups nobody
|
|
||||||
|
|||||||
38
roles/cis/tasks/modules.yml
Normal file
38
roles/cis/tasks/modules.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
- name: Disable Kernel Modules
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/modprobe.d/cis.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
# CIS LVL 3 Restrictions
|
||||||
|
install freevxfs /bin/false
|
||||||
|
install jffs2 /bin/false
|
||||||
|
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
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/udev/rules.d/10-cis_usb_devices.sh
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Create USB rules
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/udev/rules.d/10-cis_usb_devices.rules
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
# By default, disable all.
|
||||||
|
ACTION=="add", SUBSYSTEMS=="usb", TEST=="authorized_default", ATTR{authorized_default}="0"
|
||||||
|
# Enable hub devices.
|
||||||
|
ACTION=="add", ATTR{bDeviceClass}=="09", TEST=="authorized", ATTR{authorized}="1"
|
||||||
|
# Enable keyboard devices.
|
||||||
|
ACTION=="add", ATTR{product}=="*[Kk]eyboard*", TEST=="authorized", ATTR{authorized}="1"
|
||||||
|
# PS2-USB converter.
|
||||||
|
ACTION=="add", ATTR{product}=="*Thinnet TM*", TEST=="authorized", ATTR{authorized}="1"
|
||||||
16
roles/cis/tasks/permissions.yml
Normal file
16
roles/cis/tasks/permissions.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: Check CIS permission targets
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
loop: "{{ cis_permission_targets }}"
|
||||||
|
register: cis_permission_stats
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set permissions for existing targets
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.item.path }}"
|
||||||
|
owner: "{{ item.item.owner | default(omit) }}"
|
||||||
|
group: "{{ item.item.group | default(omit) }}"
|
||||||
|
mode: "{{ item.item.mode }}"
|
||||||
|
loop: "{{ cis_permission_stats.results }}"
|
||||||
|
when: item.stat.exists
|
||||||
46
roles/cis/tasks/security_lines.yml
Normal file
46
roles/cis/tasks/security_lines.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
- name: Add Security related lines into config files
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
line: "{{ item.content }}"
|
||||||
|
loop:
|
||||||
|
- { path: /mnt/etc/security/limits.conf, content: "* hard core 0" }
|
||||||
|
- { path: /mnt/etc/security/pwquality.conf, content: minlen = 14 }
|
||||||
|
- { path: /mnt/etc/security/pwquality.conf, content: dcredit = -1 }
|
||||||
|
- { path: /mnt/etc/security/pwquality.conf, content: ucredit = -1 }
|
||||||
|
- { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 }
|
||||||
|
- { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 }
|
||||||
|
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: umask 077 }
|
||||||
|
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: export TMOUT=3000 }
|
||||||
|
- { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent }
|
||||||
|
- { path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log" }
|
||||||
|
- { path: /mnt/etc/pam.d/su, content: auth required pam_wheel.so }
|
||||||
|
- path: >-
|
||||||
|
/mnt/etc/{{
|
||||||
|
"pam.d/common-auth"
|
||||||
|
if is_debian | bool
|
||||||
|
else "authselect/system-auth"
|
||||||
|
if os == "fedora"
|
||||||
|
else "pam.d/system-auth"
|
||||||
|
}}
|
||||||
|
content: >-
|
||||||
|
auth required pam_faillock.so onerr=fail audit silent deny=5 unlock_time=900
|
||||||
|
- path: >-
|
||||||
|
/mnt/etc/{{
|
||||||
|
"pam.d/common-account"
|
||||||
|
if is_debian | bool
|
||||||
|
else "authselect/system-auth"
|
||||||
|
if os == "fedora"
|
||||||
|
else "pam.d/system-auth"
|
||||||
|
}}
|
||||||
|
content: account required pam_faillock.so
|
||||||
|
- path: >-
|
||||||
|
/mnt/etc/pam.d/{{
|
||||||
|
"common-password"
|
||||||
|
if is_debian | bool
|
||||||
|
else "passwd"
|
||||||
|
}}
|
||||||
|
content: >-
|
||||||
|
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
|
||||||
|
- { path: /mnt/etc/hosts.deny, content: "ALL: ALL" }
|
||||||
|
- { path: /mnt/etc/hosts.allow, content: "sshd: ALL" }
|
||||||
51
roles/cis/tasks/sshd.yml
Normal file
51
roles/cis/tasks/sshd.yml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
- name: Adjust SSHD config
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/ssh/sshd_config
|
||||||
|
regexp: ^\s*#?{{ item.option }}\s+.*$
|
||||||
|
line: "{{ item.option }} {{ item.value }}"
|
||||||
|
loop:
|
||||||
|
- { 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: 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
|
||||||
|
ansible.builtin.blockinfile:
|
||||||
|
path: /mnt/etc/ssh/sshd_config
|
||||||
|
marker: "# {mark} CIS SSH HARDENING"
|
||||||
|
block: |-
|
||||||
|
## CIS Specific
|
||||||
|
Protocol 2
|
||||||
|
### Ciphers and keying ###
|
||||||
|
RekeyLimit 512M 6h
|
||||||
|
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
|
||||||
|
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
||||||
|
###########################
|
||||||
|
AllowStreamLocalForwarding no
|
||||||
|
PermitUserRC no
|
||||||
|
AllowUsers *
|
||||||
|
AllowGroups *
|
||||||
|
DenyUsers nobody
|
||||||
|
DenyGroups nobody
|
||||||
30
roles/cis/tasks/sysctl.yml
Normal file
30
roles/cis/tasks/sysctl.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
- name: Create a consolidated sysctl configuration file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/sysctl.d/10-cis.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
## CIS Sysctl configurations
|
||||||
|
kernel.yama.ptrace_scope=1
|
||||||
|
kernel.randomize_va_space=2
|
||||||
|
# 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
|
||||||
9
roles/cleanup/defaults/main.yml
Normal file
9
roles/cleanup/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
cleanup_libvirt_image_dir: >-
|
||||||
|
{{
|
||||||
|
system_cfg.path
|
||||||
|
if system_cfg is defined and (system_cfg.path | string | length) > 0
|
||||||
|
else '/var/lib/libvirt/images'
|
||||||
|
}}
|
||||||
|
cleanup_libvirt_cloudinit_path: >-
|
||||||
|
{{ [cleanup_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||||
106
roles/cleanup/tasks/libvirt.yml
Normal file
106
roles/cleanup/tasks/libvirt.yml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
- name: Remove Archiso and cloud-init disks
|
||||||
|
when: hypervisor_type == "libvirt"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Read current VM XML definition
|
||||||
|
community.libvirt.virt:
|
||||||
|
command: get_xml
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
register: cleanup_libvirt_get_xml
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Initialize cleaned VM XML
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_get_xml.get_xml }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Remove boot ISO device from VM XML (target match)
|
||||||
|
community.general.xml:
|
||||||
|
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
||||||
|
xpath: "/domain/devices/disk[target/@dev='sda']"
|
||||||
|
state: absent
|
||||||
|
register: cleanup_libvirt_xml_strip_boot
|
||||||
|
|
||||||
|
- name: Update cleaned VM XML after removing boot ISO
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_boot.xmlstring }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Remove boot ISO device from VM XML (source match)
|
||||||
|
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)
|
||||||
|
community.general.xml:
|
||||||
|
xmlstring: "{{ cleanup_libvirt_domain_xml }}"
|
||||||
|
xpath: "/domain/devices/disk[contains(source/@file, '{{ hostname }}-cloudinit.iso')]"
|
||||||
|
state: absent
|
||||||
|
register: cleanup_libvirt_xml_strip_cloudinit_source
|
||||||
|
|
||||||
|
- name: Update cleaned VM XML after removing cloud-init ISO source match
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml: "{{ cleanup_libvirt_xml_strip_cloudinit_source.xmlstring }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Strip XML declaration for libvirt define
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_libvirt_domain_xml_clean: >-
|
||||||
|
{{
|
||||||
|
cleanup_libvirt_domain_xml
|
||||||
|
| replace('\ufeff', '')
|
||||||
|
| regex_replace("(?is)<\\?xml[^>]*\\?>", "")
|
||||||
|
| regex_replace("(?i)encoding=[\"'][^\"']+[\"']", "")
|
||||||
|
| trim
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Update VM definition without installer media
|
||||||
|
community.libvirt.virt:
|
||||||
|
command: define
|
||||||
|
xml: "{{ cleanup_libvirt_domain_xml_clean }}"
|
||||||
|
|
||||||
|
- name: Remove cloud-init disk
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ cleanup_libvirt_cloudinit_path }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Ensure VM is powered off before restart
|
||||||
|
community.libvirt.virt:
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: destroyed
|
||||||
|
|
||||||
|
- name: Start the VM
|
||||||
|
community.libvirt.virt:
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: running
|
||||||
|
|
||||||
|
- name: Wait for VM to boot up
|
||||||
|
delegate_to: "{{ inventory_hostname }}"
|
||||||
|
ansible.builtin.wait_for_connection:
|
||||||
|
timeout: 300
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
@@ -1,90 +1,8 @@
|
|||||||
---
|
---
|
||||||
- name: Unmount /mnt recursively
|
- name: Cleanup physical install
|
||||||
ansible.builtin.command: umount -l /mnt
|
when: system_cfg.type == "physical"
|
||||||
changed_when: result.rc == 0
|
ansible.builtin.include_tasks: physical.yml
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Setup Cleanup
|
- name: Cleanup virtual install
|
||||||
when: hypervisor == "proxmox"
|
when: system_cfg.type == "virtual"
|
||||||
delegate_to: localhost
|
ansible.builtin.include_tasks: virtual.yml
|
||||||
become: false
|
|
||||||
block:
|
|
||||||
- name: Cleanup Setup Disks
|
|
||||||
community.general.proxmox_disk:
|
|
||||||
api_host: "{{ hypervisor_url }}"
|
|
||||||
api_user: "{{ hypervisor_username }}"
|
|
||||||
api_password: "{{ hypervisor_password }}"
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
vmid: "{{ vm_id }}"
|
|
||||||
disk: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- ide0
|
|
||||||
- ide2
|
|
||||||
|
|
||||||
- name: Remove CD-ROM from VM in vCenter
|
|
||||||
when: hypervisor == "vmware"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
failed_when: false
|
|
||||||
community.vmware.vmware_guest:
|
|
||||||
hostname: "{{ hypervisor_url }}"
|
|
||||||
username: "{{ hypervisor_username }}"
|
|
||||||
password: "{{ hypervisor_password }}"
|
|
||||||
validate_certs: false
|
|
||||||
datacenter: "{{ hypervisor_cluster }}"
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
cdrom:
|
|
||||||
- controller_number: 0
|
|
||||||
unit_number: 0
|
|
||||||
controller_type: sata
|
|
||||||
type: iso
|
|
||||||
iso_path: "{{ boot_iso }}"
|
|
||||||
state: absent
|
|
||||||
- controller_number: 0
|
|
||||||
unit_number: 1
|
|
||||||
controller_type: sata
|
|
||||||
type: iso
|
|
||||||
iso_path: "{{ rhel_iso | default(omit) }}"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Remove Archiso and cloud-init disks
|
|
||||||
when: hypervisor == "libvirt"
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
block:
|
|
||||||
- name: Stop the VM
|
|
||||||
community.libvirt.virt:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
state: shutdown
|
|
||||||
|
|
||||||
- name: Remove cloud-init disk
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}-cloudinit.iso"
|
|
||||||
state: absent
|
|
||||||
|
|
||||||
- name: Get list of CD-ROM devices
|
|
||||||
ansible.builtin.shell: set -o pipefail && virsh --connect qemu:///system domblklist {{ hostname }} --details | grep 'cdrom' | awk '{print $3}'
|
|
||||||
changed_when: false
|
|
||||||
register: cdrom_devices
|
|
||||||
|
|
||||||
- name: Wait for VM to spin down
|
|
||||||
ansible.builtin.wait_for:
|
|
||||||
timeout: 15
|
|
||||||
|
|
||||||
- name: Remove CD-ROM devices
|
|
||||||
when: cdrom_devices.stdout_lines | length > 0
|
|
||||||
ansible.builtin.command: virsh --connect qemu:///system detach-disk {{ hostname }} {{ item }} --persistent
|
|
||||||
with_items: "{{ cdrom_devices.stdout_lines | select('ne', 'sdc') | list }}"
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Start the VM
|
|
||||||
community.libvirt.virt:
|
|
||||||
name: "{{ hostname }}"
|
|
||||||
state: running
|
|
||||||
|
|
||||||
- name: Wait for VM to boot up
|
|
||||||
delegate_to: "{{ inventory_hostname }}"
|
|
||||||
ansible.builtin.wait_for_connection:
|
|
||||||
timeout: 300
|
|
||||||
|
|||||||
13
roles/cleanup/tasks/physical.yml
Normal file
13
roles/cleanup/tasks/physical.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- name: Unmount installer mounts
|
||||||
|
ansible.builtin.include_tasks: unmount.yml
|
||||||
|
|
||||||
|
- name: Trigger reboot into installed system
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- reboot
|
||||||
|
async: 1
|
||||||
|
poll: 0
|
||||||
|
changed_when: true
|
||||||
|
failed_when: false
|
||||||
|
ignore_unreachable: true
|
||||||
27
roles/cleanup/tasks/proxmox.yml
Normal file
27
roles/cleanup/tasks/proxmox.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
- name: Setup Cleanup
|
||||||
|
when: hypervisor_type == "proxmox"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Cleanup Setup Disks
|
||||||
|
community.proxmox.proxmox_disk:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
disk: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- ide0
|
||||||
|
- ide2
|
||||||
|
|
||||||
|
- name: Start the VM
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
state: restarted
|
||||||
4
roles/cleanup/tasks/shutdown.yml
Normal file
4
roles/cleanup/tasks/shutdown.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
- name: Shutdown the VM
|
||||||
|
become: true
|
||||||
|
community.general.shutdown:
|
||||||
23
roles/cleanup/tasks/unmount.yml
Normal file
23
roles/cleanup/tasks/unmount.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
- name: Unmount Disks
|
||||||
|
become: true
|
||||||
|
block:
|
||||||
|
- name: Disable Swap
|
||||||
|
ansible.builtin.command: swapoff -a
|
||||||
|
register: cleanup_swapoff_result
|
||||||
|
changed_when: cleanup_swapoff_result.rc == 0
|
||||||
|
|
||||||
|
- name: Unmount /mnt if mounted
|
||||||
|
ansible.builtin.command: umount -R /mnt
|
||||||
|
register: cleanup_unmount_result
|
||||||
|
changed_when: cleanup_unmount_result.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Verify /mnt is no longer mounted
|
||||||
|
ansible.builtin.command: grep ' /mnt ' /proc/mounts
|
||||||
|
until: cleanup_verify_unmount.rc != 0
|
||||||
|
retries: 5
|
||||||
|
delay: 5
|
||||||
|
register: cleanup_verify_unmount
|
||||||
|
changed_when: false
|
||||||
|
failed_when: cleanup_verify_unmount.rc not in [0, 1]
|
||||||
208
roles/cleanup/tasks/virtual.yml
Normal file
208
roles/cleanup/tasks/virtual.yml
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
---
|
||||||
|
- name: Unmount installer mounts
|
||||||
|
ansible.builtin.include_tasks: unmount.yml
|
||||||
|
|
||||||
|
- name: Shutdown installer environment
|
||||||
|
ansible.builtin.include_tasks: shutdown.yml
|
||||||
|
|
||||||
|
- name: Cleanup hypervisor resources
|
||||||
|
ansible.builtin.include_tasks: proxmox.yml
|
||||||
|
|
||||||
|
- name: Cleanup vCenter resources
|
||||||
|
ansible.builtin.include_tasks: vmware.yml
|
||||||
|
|
||||||
|
- name: Cleanup libvirt resources
|
||||||
|
ansible.builtin.include_tasks: libvirt.yml
|
||||||
|
|
||||||
|
- name: Cleanup Xen resources
|
||||||
|
ansible.builtin.include_tasks: xen.yml
|
||||||
|
|
||||||
|
- name: Determine post-reboot connectivity
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
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
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Check VM accessibility after reboot
|
||||||
|
when:
|
||||||
|
- 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: 300
|
||||||
|
register: cleanup_vm_connection_check
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: VM failed to boot - initiate cleanup
|
||||||
|
when:
|
||||||
|
- cleanup_vm_connection_check is defined
|
||||||
|
- cleanup_vm_connection_check.failed | 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 VM for libvirt
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "libvirt"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.libvirt.virt:
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: destroyed
|
||||||
|
|
||||||
|
- name: Undefine VM for libvirt
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "libvirt"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.libvirt.virt:
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
command: undefine
|
||||||
|
|
||||||
|
- name: Remove VM disk for libvirt
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "libvirt"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ virtualization_libvirt_disks | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
|
- name: Remove cloud-init disk for libvirt
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "libvirt"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ virtualization_libvirt_cloudinit_path }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Remove VM for proxmox
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "proxmox"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
state: stopped
|
||||||
|
|
||||||
|
- name: Delete VM for proxmox
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "proxmox"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
state: absent
|
||||||
|
unprivileged: false
|
||||||
|
|
||||||
|
- name: Remove VM for VMware
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "vmware"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.vmware.vmware_guest:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
folder: "{{ system_cfg.path | default('/') }}"
|
||||||
|
state: poweredoff
|
||||||
|
|
||||||
|
- name: Delete VM for VMware
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "vmware"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.vmware.vmware_guest:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
folder: "{{ system_cfg.path | default('/') }}"
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Destroy Xen VM if running
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "xen"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
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 disk
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "xen"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
state: absent
|
||||||
|
loop: "{{ virtualization_xen_disks | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
|
- name: Remove Xen VM config file
|
||||||
|
when:
|
||||||
|
- hypervisor_type == "xen"
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
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.
|
||||||
40
roles/cleanup/tasks/vmware.yml
Normal file
40
roles/cleanup/tasks/vmware.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
- name: Clean vCenter VM
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Remove CD-ROM from VM in vCenter
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
community.vmware.vmware_guest:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
cdrom:
|
||||||
|
- controller_number: 0
|
||||||
|
unit_number: 0
|
||||||
|
controller_type: sata
|
||||||
|
type: iso
|
||||||
|
iso_path: "{{ boot_iso }}"
|
||||||
|
state: absent
|
||||||
|
- controller_number: 0
|
||||||
|
unit_number: 1
|
||||||
|
controller_type: sata
|
||||||
|
type: iso
|
||||||
|
iso_path: "{{ rhel_iso if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
||||||
|
state: absent
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Start VM in vCenter
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
vmware.vmware.vm_powerstate:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: powered-on
|
||||||
58
roles/cleanup/tasks/xen.yml
Normal file
58
roles/cleanup/tasks/xen.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
- name: Cleanup Xen installer media
|
||||||
|
when: hypervisor_type == "xen"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Ensure Xen disk definitions exist
|
||||||
|
when: virtualization_xen_disks is not defined
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cleanup_xen_disks: "{{ cleanup_xen_disks | default([]) + [cleanup_xen_disk_cfg] }}"
|
||||||
|
vars:
|
||||||
|
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
|
||||||
|
cleanup_xen_disk_cfg: >-
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
'path': (
|
||||||
|
virtualization_xen_disk_path ~ '/' ~ hostname ~ '.qcow2'
|
||||||
|
if ansible_loop.index0 == 0
|
||||||
|
else virtualization_xen_disk_path ~ '/' ~ hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'
|
||||||
|
),
|
||||||
|
'target': 'xvd' ~ device_letter,
|
||||||
|
'size': (item.size | float)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loop: "{{ system_cfg.disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
extended: true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Render Xen VM configuration without installer media
|
||||||
|
vars:
|
||||||
|
xen_installer_media_enabled: false
|
||||||
|
virtualization_xen_disks: "{{ virtualization_xen_disks | default(cleanup_xen_disks | default([])) }}"
|
||||||
|
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
|
||||||
5
roles/configuration/defaults/main.yml
Normal file
5
roles/configuration/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
configuration_motd_enabled: "{{ system_cfg.features.banner.motd | bool }}"
|
||||||
|
configuration_sudo_banner_enabled: "{{ system_cfg.features.banner.sudo | bool }}"
|
||||||
|
configuration_firewall_enabled: "{{ system_cfg.features.firewall.enabled | bool }}"
|
||||||
|
configuration_luks_enabled: "{{ system_cfg.luks.enabled | bool }}"
|
||||||
55
roles/configuration/tasks/banner.yml
Normal file
55
roles/configuration/tasks/banner.yml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
- name: Configure MOTD
|
||||||
|
when: configuration_motd_enabled | 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: Configure sudo banner
|
||||||
|
when: configuration_sudo_banner_enabled | bool
|
||||||
|
block:
|
||||||
|
- name: Create sudoers banner directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/sudoers.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Create sudo banner file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: |
|
||||||
|
I am Groot, and I know what I'm doing.
|
||||||
|
dest: /mnt/etc/sudoers.d/banner
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Enable sudo banner in sudoers
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/sudoers
|
||||||
|
line: "Defaults lecture=@/etc/sudoers.d/banner"
|
||||||
|
state: present
|
||||||
|
create: true
|
||||||
|
mode: "0440"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
validate: "visudo -cf - %s"
|
||||||
65
roles/configuration/tasks/bootloader.yml
Normal file
65
roles/configuration/tasks/bootloader.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
- name: Configure Bootloader
|
||||||
|
block:
|
||||||
|
- name: Install Bootloader
|
||||||
|
vars:
|
||||||
|
configuration_use_efibootmgr: "{{ is_rhel | bool }}"
|
||||||
|
configuration_efi_dir: "{{ partitioning_efi_mountpoint }}"
|
||||||
|
configuration_bootloader_id: >-
|
||||||
|
{{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }}
|
||||||
|
configuration_efi_vendor: >-
|
||||||
|
{{ "redhat" if os | lower == "rhel" 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: "{{ chroot_command }} {{ configuration_bootloader_cmd }}"
|
||||||
|
register: configuration_bootloader_result
|
||||||
|
changed_when: configuration_bootloader_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure lvm2 for non btrfs filesystems
|
||||||
|
when: os | lower == "archlinux" and system_cfg.filesystem != "btrfs"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/mkinitcpio.conf
|
||||||
|
regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)"
|
||||||
|
line: "\\1 lvm2\\2"
|
||||||
|
backrefs: true
|
||||||
|
|
||||||
|
- name: Regenerate initramfs
|
||||||
|
when: os | lower not in ["alpine", "void"]
|
||||||
|
vars:
|
||||||
|
configuration_initramfs_cmd: >-
|
||||||
|
{{
|
||||||
|
'/usr/sbin/mkinitcpio -P'
|
||||||
|
if os | lower == "archlinux"
|
||||||
|
else (
|
||||||
|
'/usr/bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin '
|
||||||
|
+ '/usr/sbin/update-initramfs -u -k all'
|
||||||
|
if is_debian | bool
|
||||||
|
else '/usr/bin/dracut --regenerate-all --force'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} {{ configuration_initramfs_cmd }}"
|
||||||
|
register: configuration_initramfs_result
|
||||||
|
changed_when: configuration_initramfs_result.rc == 0
|
||||||
|
|
||||||
|
- name: Generate grub config
|
||||||
|
vars:
|
||||||
|
configuration_efi_vendor: >-
|
||||||
|
{{ "redhat" if os | lower == "rhel" else os | lower }}
|
||||||
|
configuration_grub_cfg_cmd: >-
|
||||||
|
{{
|
||||||
|
'/usr/sbin/grub2-mkconfig -o '
|
||||||
|
+ partitioning_efi_mountpoint
|
||||||
|
+ '/EFI/' + configuration_efi_vendor + '/grub.cfg'
|
||||||
|
if is_rhel | bool
|
||||||
|
else '/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg'
|
||||||
|
}}
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} {{ configuration_grub_cfg_cmd }}"
|
||||||
|
register: configuration_grub_result
|
||||||
|
changed_when: configuration_grub_result.rc == 0
|
||||||
344
roles/configuration/tasks/encryption.yml
Normal file
344
roles/configuration/tasks/encryption.yml
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
---
|
||||||
|
- name: Configure disk encryption
|
||||||
|
when: system_cfg.luks.enabled | bool
|
||||||
|
vars:
|
||||||
|
configuration_luks_passphrase: >-
|
||||||
|
{{ system_cfg.luks.passphrase | string }}
|
||||||
|
block:
|
||||||
|
- name: Set LUKS configuration facts
|
||||||
|
vars:
|
||||||
|
luks_tpm2_pcrs: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
system_cfg.luks.tpm2.pcrs
|
||||||
|
if system_cfg.luks.tpm2.pcrs is string
|
||||||
|
else (system_cfg.luks.tpm2.pcrs | map('string') | join('+'))
|
||||||
|
)
|
||||||
|
| string
|
||||||
|
| replace(',', '+')
|
||||||
|
| regex_replace('\\s+', '')
|
||||||
|
| regex_replace('^\\+|\\+$', '')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_luks_mapper_name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
|
||||||
|
configuration_luks_device: "{{ partitioning_luks_device }}"
|
||||||
|
configuration_luks_options: "{{ system_cfg.luks.options }}"
|
||||||
|
configuration_luks_auto_method: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.luks.auto | bool)
|
||||||
|
| ternary(
|
||||||
|
system_cfg.luks.method,
|
||||||
|
'manual'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
configuration_luks_tpm2_device: "{{ system_cfg.luks.tpm2.device }}"
|
||||||
|
configuration_luks_tpm2_pcrs: "{{ luks_tpm2_pcrs }}"
|
||||||
|
configuration_luks_keyfile_path: "/etc/cryptsetup-keys.d/{{ system_cfg.luks.mapper }}.key"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Validate LUKS UUID is available
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- configuration_luks_uuid | length > 0
|
||||||
|
fail_msg: LUKS UUID not available. Ensure partitioning ran before configuration.
|
||||||
|
|
||||||
|
- name: Validate LUKS passphrase for auto-decrypt
|
||||||
|
when: configuration_luks_auto_method in ['tpm2', 'keyfile']
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- configuration_luks_passphrase | length > 0
|
||||||
|
fail_msg: system.luks.passphrase must be set for LUKS auto-decrypt.
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Enroll TPM2 for LUKS
|
||||||
|
when: configuration_luks_auto_method == 'tpm2'
|
||||||
|
ansible.builtin.include_tasks: encryption/tpm2.yml
|
||||||
|
|
||||||
|
- name: Configure LUKS keyfile auto-decrypt
|
||||||
|
when: configuration_luks_auto_method == 'keyfile'
|
||||||
|
ansible.builtin.include_tasks: encryption/keyfile.yml
|
||||||
|
|
||||||
|
- name: Build LUKS parameters
|
||||||
|
vars:
|
||||||
|
luks_keyfile_in_use: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
||||||
|
luks_option_list: >-
|
||||||
|
{{
|
||||||
|
(configuration_luks_options | trim).split(',')
|
||||||
|
if configuration_luks_options | trim | length > 0
|
||||||
|
else []
|
||||||
|
}}
|
||||||
|
luks_tpm2_option_list: >-
|
||||||
|
{{
|
||||||
|
(configuration_luks_auto_method == 'tpm2')
|
||||||
|
| ternary(
|
||||||
|
['tpm2-device=' + configuration_luks_tpm2_device]
|
||||||
|
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
|
||||||
|
if configuration_luks_tpm2_pcrs | length > 0 else []),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
luks_crypttab_keyfile: "{{ configuration_luks_keyfile_path if luks_keyfile_in_use else 'none' }}"
|
||||||
|
luks_crypttab_options: >-
|
||||||
|
{{
|
||||||
|
(['luks'] + luks_option_list + luks_tpm2_option_list)
|
||||||
|
| join(',')
|
||||||
|
}}
|
||||||
|
luks_rd_options: "{{ (luks_option_list + luks_tpm2_option_list) | join(',') }}"
|
||||||
|
luks_kernel_args: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name]
|
||||||
|
+ (
|
||||||
|
['rd.luks.options=' + configuration_luks_uuid + '=' + luks_rd_options]
|
||||||
|
if luks_rd_options | length > 0 else []
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path]
|
||||||
|
if luks_keyfile_in_use else []
|
||||||
|
)
|
||||||
|
) | join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_luks_keyfile_in_use: "{{ luks_keyfile_in_use }}"
|
||||||
|
configuration_luks_option_list: "{{ luks_option_list }}"
|
||||||
|
configuration_luks_tpm2_option_list: "{{ luks_tpm2_option_list }}"
|
||||||
|
configuration_luks_crypttab_keyfile: "{{ luks_crypttab_keyfile }}"
|
||||||
|
configuration_luks_crypttab_options: "{{ luks_crypttab_options }}"
|
||||||
|
configuration_luks_rd_options: "{{ luks_rd_options }}"
|
||||||
|
configuration_luks_kernel_args: "{{ luks_kernel_args }}"
|
||||||
|
|
||||||
|
- name: Remove LUKS keyfile if TPM2 auto-decrypt is active
|
||||||
|
when: configuration_luks_auto_method == 'tpm2'
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt{{ configuration_luks_keyfile_path }}
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- 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"
|
||||||
|
|
||||||
|
- name: Ensure keyfile pattern for initramfs-tools
|
||||||
|
when:
|
||||||
|
- is_debian | bool
|
||||||
|
- 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:
|
||||||
|
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
|
||||||
|
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: 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 | bool
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/dracut.conf.d
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Configure dracut for LUKS
|
||||||
|
when: is_rhel | bool
|
||||||
|
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: Read kernel cmdline defaults
|
||||||
|
when: is_rhel | bool
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /mnt/etc/kernel/cmdline
|
||||||
|
register: configuration_kernel_cmdline_slurp
|
||||||
|
|
||||||
|
- name: Build kernel cmdline with LUKS args
|
||||||
|
when: is_rhel | bool
|
||||||
|
vars:
|
||||||
|
kernel_cmdline_current: >-
|
||||||
|
{{ configuration_kernel_cmdline_slurp.content | b64decode | trim }}
|
||||||
|
kernel_cmdline_list: >-
|
||||||
|
{{
|
||||||
|
kernel_cmdline_current.split()
|
||||||
|
if kernel_cmdline_current | length > 0 else []
|
||||||
|
}}
|
||||||
|
kernel_cmdline_filtered: >-
|
||||||
|
{{
|
||||||
|
kernel_cmdline_list
|
||||||
|
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
kernel_cmdline_new: >-
|
||||||
|
{{
|
||||||
|
(kernel_cmdline_filtered + configuration_luks_kernel_args.split())
|
||||||
|
| unique
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_kernel_cmdline_new: "{{ kernel_cmdline_new }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Write kernel cmdline with LUKS args
|
||||||
|
when: is_rhel | bool
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/kernel/cmdline
|
||||||
|
mode: "0644"
|
||||||
|
content: "{{ configuration_kernel_cmdline_new }}\n"
|
||||||
|
|
||||||
|
- name: Find BLS entries
|
||||||
|
when: is_rhel | bool
|
||||||
|
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 | bool
|
||||||
|
- 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 | bool
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /mnt/etc/default/grub
|
||||||
|
register: configuration_grub_slurp
|
||||||
|
|
||||||
|
- name: Build grub command lines with LUKS args
|
||||||
|
when: not is_rhel | bool
|
||||||
|
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
|
||||||
|
when: not is_rhel | bool
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/default/grub
|
||||||
|
regexp: "^GRUB_CMDLINE_LINUX_DEFAULT="
|
||||||
|
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"'
|
||||||
110
roles/configuration/tasks/encryption/keyfile.yml
Normal file
110
roles/configuration/tasks/encryption/keyfile.yml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
---
|
||||||
|
- name: Configure LUKS keyfile auto-decrypt
|
||||||
|
block:
|
||||||
|
- name: Ensure cryptsetup key directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/cryptsetup-keys.d
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0700"
|
||||||
|
|
||||||
|
- name: Ensure LUKS keyfile exists
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt{{ configuration_luks_keyfile_path }}
|
||||||
|
content: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.general.random_string',
|
||||||
|
length=(system_cfg.luks.keysize | int),
|
||||||
|
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
force: false
|
||||||
|
register: configuration_luks_keyfile_copy
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Ensure keyfile permissions
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt{{ configuration_luks_keyfile_path }}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Check whether keyfile already unlocks the LUKS device
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- cryptsetup
|
||||||
|
- luksOpen
|
||||||
|
- --test-passphrase
|
||||||
|
- --key-file
|
||||||
|
- "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
|
- "{{ configuration_luks_device }}"
|
||||||
|
register: configuration_luks_keyfile_unlock_test
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Add keyfile to LUKS header
|
||||||
|
when: configuration_luks_keyfile_unlock_test.rc != 0
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ configuration_luks_device }}"
|
||||||
|
passphrase: "{{ configuration_luks_passphrase }}"
|
||||||
|
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
|
register: configuration_luks_addkey_result
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Regenerate keyfile and retry adding to LUKS header
|
||||||
|
when:
|
||||||
|
- configuration_luks_keyfile_unlock_test.rc != 0
|
||||||
|
- configuration_luks_keyfile_copy is defined and configuration_luks_keyfile_copy.changed | bool
|
||||||
|
- configuration_luks_addkey_result is failed
|
||||||
|
block:
|
||||||
|
- name: Regenerate LUKS keyfile
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt{{ configuration_luks_keyfile_path }}
|
||||||
|
content: >-
|
||||||
|
{{
|
||||||
|
lookup(
|
||||||
|
'community.general.random_string',
|
||||||
|
length=(system_cfg.luks.keysize | int),
|
||||||
|
override_all='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
force: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Retry adding keyfile to LUKS header
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ configuration_luks_device }}"
|
||||||
|
passphrase: "{{ configuration_luks_passphrase }}"
|
||||||
|
new_keyfile: "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
|
register: configuration_luks_addkey_retry
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Re-check whether keyfile unlocks the LUKS device
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- cryptsetup
|
||||||
|
- luksOpen
|
||||||
|
- --test-passphrase
|
||||||
|
- --key-file
|
||||||
|
- "/mnt{{ configuration_luks_keyfile_path }}"
|
||||||
|
- "{{ configuration_luks_device }}"
|
||||||
|
register: configuration_luks_keyfile_unlock_test_after
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Fallback to manual LUKS unlock if keyfile enrollment failed
|
||||||
|
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_luks_auto_method: manual
|
||||||
90
roles/configuration/tasks/encryption/tpm2.yml
Normal file
90
roles/configuration/tasks/encryption/tpm2.yml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
- name: Enroll TPM2 for LUKS
|
||||||
|
block:
|
||||||
|
- name: Create temporary passphrase file for TPM2 enrollment
|
||||||
|
ansible.builtin.tempfile:
|
||||||
|
path: /mnt/tmp
|
||||||
|
prefix: luks-passphrase-
|
||||||
|
state: file
|
||||||
|
register: configuration_luks_tpm2_passphrase_tempfile
|
||||||
|
|
||||||
|
- name: Write passphrase into temporary file for TPM2 enrollment
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
||||||
|
content: "{{ configuration_luks_passphrase }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Enroll TPM2 token
|
||||||
|
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
|
||||||
|
| regex_replace('^/mnt', '')
|
||||||
|
)
|
||||||
|
]
|
||||||
|
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs]
|
||||||
|
if configuration_luks_tpm2_pcrs | length > 0 else [])
|
||||||
|
+ [configuration_luks_device]
|
||||||
|
}}
|
||||||
|
configuration_luks_enroll_chroot_cmd: >-
|
||||||
|
{{ chroot_command }} {{ configuration_luks_enroll_args | join(' ') }}
|
||||||
|
ansible.builtin.command: "{{ configuration_luks_enroll_chroot_cmd }}"
|
||||||
|
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]
|
||||||
|
if configuration_luks_tpm2_pcrs | 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:
|
||||||
|
- name: Fallback to keyfile auto-decrypt
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_luks_auto_method: keyfile
|
||||||
|
always:
|
||||||
|
- name: Remove TPM2 enrollment passphrase file
|
||||||
|
when: configuration_luks_tpm2_passphrase_tempfile.path is defined
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
||||||
|
state: absent
|
||||||
|
changed_when: false
|
||||||
72
roles/configuration/tasks/extras.yml
Normal file
72
roles/configuration/tasks/extras.yml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
- name: Append vim configurations to vimrc
|
||||||
|
ansible.builtin.blockinfile:
|
||||||
|
path: "{{ '/mnt/etc/vim/vimrc' if is_debian | bool else '/mnt/etc/vimrc' }}"
|
||||||
|
block: |
|
||||||
|
set encoding=utf-8
|
||||||
|
set number
|
||||||
|
set autoindent
|
||||||
|
set smartindent
|
||||||
|
set mouse=a
|
||||||
|
insertafter: EOF
|
||||||
|
marker: ""
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Add memory tuning parameters
|
||||||
|
ansible.builtin.blockinfile:
|
||||||
|
path: /mnt/etc/sysctl.d/90-memory.conf
|
||||||
|
create: true
|
||||||
|
block: |
|
||||||
|
vm.swappiness=10
|
||||||
|
vm.vfs_cache_pressure=50
|
||||||
|
vm.dirty_background_ratio=1
|
||||||
|
vm.dirty_ratio=10
|
||||||
|
vm.page-cluster=10
|
||||||
|
marker: ""
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Create zram config
|
||||||
|
when:
|
||||||
|
- (os != "debian" or (os_version | string) != "11") and os != "rhel"
|
||||||
|
- os | lower not in ["alpine", "void"]
|
||||||
|
- system_cfg.features.swap.enabled | bool
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/systemd/zram-generator.conf
|
||||||
|
content: |
|
||||||
|
[zram0]
|
||||||
|
zram-size = ram / 2
|
||||||
|
compression-algorithm = {{ 'zstd' if system_cfg.features.zstd.enabled | bool else 'lz4' }}
|
||||||
|
swap-priority = 100
|
||||||
|
fs-type = swap
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Copy Custom Shell config
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: custom.sh.j2
|
||||||
|
dest: /mnt/etc/profile.d/custom.sh
|
||||||
|
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 == "rhel"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/motd.d/cockpit
|
||||||
|
- /mnt/etc/motd.d/insights-client
|
||||||
65
roles/configuration/tasks/fstab.yml
Normal file
65
roles/configuration/tasks/fstab.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
- name: Generate fstab content
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- genfstab
|
||||||
|
- -LU
|
||||||
|
- /mnt
|
||||||
|
register: configuration_fstab_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Write fstab
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/fstab
|
||||||
|
content: "{{ configuration_fstab_result.stdout }}\n"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Adjust XFS mount options and disable large extent
|
||||||
|
when: os in ["almalinux", "rocky", "rhel"] and system_cfg.filesystem == "xfs"
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: /mnt/etc/fstab
|
||||||
|
regexp: "(xfs.*?)(attr2)"
|
||||||
|
replace: "\\1allocsize=64m"
|
||||||
|
|
||||||
|
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
||||||
|
when: os == "rhel"
|
||||||
|
vars:
|
||||||
|
configuration_fstab_dvd_line: >-
|
||||||
|
{{
|
||||||
|
'/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0'
|
||||||
|
if hypervisor_type == 'vmware'
|
||||||
|
else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0'
|
||||||
|
}}
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/fstab
|
||||||
|
regexp: "^.*\\/dvd.*$"
|
||||||
|
line: "{{ configuration_fstab_dvd_line }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Write image from RHEL ISO to the target machine
|
||||||
|
when: os == "rhel" and hypervisor_type == 'vmware'
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- dd
|
||||||
|
- if=/dev/sr1
|
||||||
|
- of=/mnt/usr/local/install/redhat/rhel.iso
|
||||||
|
- bs=4M
|
||||||
|
creates: /mnt/usr/local/install/redhat/rhel.iso
|
||||||
|
register: configuration_rhel_iso_result
|
||||||
|
changed_when: configuration_rhel_iso_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure TempFS is configured in fstab
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/fstab
|
||||||
|
regexp: "{{ fstab_entry.regexp }}"
|
||||||
|
line: "{{ fstab_entry.line }}"
|
||||||
|
insertafter: EOF
|
||||||
|
loop:
|
||||||
|
- { regexp: "^# TempFS$", line: "# TempFS" }
|
||||||
|
- { 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+/dev/shm\\\\s+", line: "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
||||||
|
loop_control:
|
||||||
|
loop_var: fstab_entry
|
||||||
120
roles/configuration/tasks/grub.yml
Normal file
120
roles/configuration/tasks/grub.yml
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
---
|
||||||
|
- name: Configure grub defaults
|
||||||
|
when: not is_rhel | bool
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
dest: /mnt/etc/default/grub
|
||||||
|
regexp: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
loop:
|
||||||
|
- regexp: ^GRUB_CMDLINE_LINUX_DEFAULT=
|
||||||
|
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
||||||
|
- regexp: ^GRUB_TIMEOUT=
|
||||||
|
line: GRUB_TIMEOUT=1
|
||||||
|
|
||||||
|
- name: Ensure grub defaults file exists for RHEL-based systems
|
||||||
|
when: is_rhel | bool
|
||||||
|
block:
|
||||||
|
- name: Build RHEL kernel command line defaults
|
||||||
|
vars:
|
||||||
|
grub_root_uuid: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
partitioning_main_uuid.stdout
|
||||||
|
if (system_cfg.filesystem | lower) == 'btrfs'
|
||||||
|
else (partitioning_uuid_root | default([]) | first | default(''))
|
||||||
|
)
|
||||||
|
| default('')
|
||||||
|
| trim
|
||||||
|
}}
|
||||||
|
grub_lvm_args: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
['rd.lvm.lv=sys/root']
|
||||||
|
+ (
|
||||||
|
['rd.lvm.lv=sys/swap', 'resume=/dev/mapper/sys-swap']
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (system_cfg.filesystem | lower) != 'btrfs'
|
||||||
|
else []
|
||||||
|
}}
|
||||||
|
grub_root_flags: >-
|
||||||
|
{{ ['rootflags=subvol=@'] if (system_cfg.filesystem | lower) == 'btrfs' else [] }}
|
||||||
|
grub_cmdline_linux_base: >-
|
||||||
|
{{
|
||||||
|
(['crashkernel=auto'] + grub_lvm_args)
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
grub_kernel_cmdline_base: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(['root=UUID=' + grub_root_uuid]
|
||||||
|
if grub_root_uuid | length > 0 else [])
|
||||||
|
+ ['ro', 'crashkernel=auto']
|
||||||
|
+ grub_lvm_args
|
||||||
|
+ grub_root_flags
|
||||||
|
)
|
||||||
|
| join(' ')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_grub_cmdline_linux_base: "{{ grub_cmdline_linux_base }}"
|
||||||
|
configuration_kernel_cmdline_base: "{{ grub_kernel_cmdline_base }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Check if grub defaults file exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /mnt/etc/default/grub
|
||||||
|
register: configuration_grub_defaults_stat
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create default grub configuration
|
||||||
|
when: not configuration_grub_defaults_stat.stat.exists
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/default/grub
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
GRUB_TIMEOUT=1
|
||||||
|
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
|
||||||
|
GRUB_DEFAULT=saved
|
||||||
|
GRUB_DISABLE_SUBMENU=true
|
||||||
|
GRUB_TERMINAL_OUTPUT="console"
|
||||||
|
GRUB_CMDLINE_LINUX="{{ configuration_grub_cmdline_linux_base }}"
|
||||||
|
GRUB_DISABLE_RECOVERY="true"
|
||||||
|
GRUB_ENABLE_BLSCFG=true
|
||||||
|
|
||||||
|
- name: Ensure kernel cmdline directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/kernel
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Write kernel cmdline defaults
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/kernel/cmdline
|
||||||
|
mode: "0644"
|
||||||
|
content: "{{ configuration_kernel_cmdline_base }}\n"
|
||||||
|
|
||||||
|
- name: Find BLS entries
|
||||||
|
ansible.builtin.find:
|
||||||
|
paths: /mnt/boot/loader/entries
|
||||||
|
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
|
||||||
|
when: partitioning_grub_enable_cryptodisk | bool
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/default/grub
|
||||||
|
regexp: "^GRUB_ENABLE_CRYPTODISK="
|
||||||
|
line: GRUB_ENABLE_CRYPTODISK=y
|
||||||
101
roles/configuration/tasks/locales.yml
Normal file
101
roles/configuration/tasks/locales.yml
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
- name: Reload systemd in installer environment
|
||||||
|
when: ansible_service_mgr == 'systemd'
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Set local timezone
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /usr/share/zoneinfo/Europe/Vienna
|
||||||
|
dest: /mnt/etc/localtime
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Setup locales
|
||||||
|
block:
|
||||||
|
- name: Configure locale.gen
|
||||||
|
when: not is_rhel | bool
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
dest: /mnt/etc/locale.gen
|
||||||
|
regexp: "{{ item.regex }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
loop:
|
||||||
|
- { regex: en_US\.UTF-8 UTF-8, line: en_US.UTF-8 UTF-8 }
|
||||||
|
|
||||||
|
- name: Generate locales
|
||||||
|
when: not is_rhel | bool
|
||||||
|
ansible.builtin.command: "{{ chroot_command }} /usr/sbin/locale-gen"
|
||||||
|
register: configuration_locale_result
|
||||||
|
changed_when: configuration_locale_result.rc == 0
|
||||||
|
|
||||||
|
|
||||||
|
- name: Set hostname
|
||||||
|
vars:
|
||||||
|
configuration_dns_domain: "{{ (system_cfg.network.dns.search | default([]) | first | default('')) | string }}"
|
||||||
|
configuration_hostname_fqdn: >-
|
||||||
|
{{
|
||||||
|
hostname
|
||||||
|
if '.' in hostname
|
||||||
|
else (
|
||||||
|
hostname + '.' + configuration_dns_domain
|
||||||
|
if configuration_dns_domain | length > 0
|
||||||
|
else hostname
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ configuration_hostname_fqdn }}"
|
||||||
|
dest: /mnt/etc/hostname
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Add host entry to /etc/hosts
|
||||||
|
vars:
|
||||||
|
configuration_dns_domain: "{{ (system_cfg.network.dns.search | default([]) | first | default('')) | string }}"
|
||||||
|
configuration_hostname_fqdn: >-
|
||||||
|
{{
|
||||||
|
hostname
|
||||||
|
if '.' in hostname
|
||||||
|
else (
|
||||||
|
hostname + '.' + configuration_dns_domain
|
||||||
|
if configuration_dns_domain | length > 0
|
||||||
|
else hostname
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
configuration_hostname_short: "{{ hostname.split('.')[0] }}"
|
||||||
|
configuration_hostname_entries: >-
|
||||||
|
{{ [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_ip }} {{ configuration_hostname_entries }}
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/hosts
|
||||||
|
line: "{{ configuration_hosts_line }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Create vconsole.conf
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: KEYMAP=us
|
||||||
|
dest: /mnt/etc/vconsole.conf
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Create locale.conf
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: LANG=en_US.UTF-8
|
||||||
|
dest: /mnt/etc/locale.conf
|
||||||
|
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,267 +1,18 @@
|
|||||||
---
|
---
|
||||||
- name: Configuration
|
- name: Include configuration tasks
|
||||||
block:
|
ansible.builtin.include_tasks: "{{ configuration_task }}"
|
||||||
- name: Generate fstab
|
loop:
|
||||||
ansible.builtin.shell: genfstab -LU /mnt > /mnt/etc/fstab
|
- banner.yml
|
||||||
changed_when: result.rc == 0
|
- fstab.yml
|
||||||
register: result
|
- locales.yml
|
||||||
|
- services.yml
|
||||||
- name: Remove depricated attr2 and disable large extent
|
- grub.yml
|
||||||
when: os in ["almalinux", "rhel8", "rhel9", "rocky"] and filesystem == "xfs"
|
- encryption.yml
|
||||||
ansible.builtin.replace:
|
- bootloader.yml
|
||||||
path: /mnt/etc/fstab
|
- extras.yml
|
||||||
regexp: '(xfs.*?)(attr2)'
|
- network.yml
|
||||||
replace: '\1allocsize=64m'
|
- users.yml
|
||||||
|
- sudo.yml
|
||||||
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
- selinux.yml
|
||||||
when: os in ["rhel8", "rhel9"]
|
loop_control:
|
||||||
ansible.builtin.lineinfile:
|
loop_var: configuration_task
|
||||||
path: /mnt/etc/fstab
|
|
||||||
regexp: '^.*\/dvd.*$'
|
|
||||||
line: "{{ '/usr/local/install/redhat/rhel.iso /usr/local/install/redhat/dvd iso9660 loop,nofail 0 0' if hypervisor == 'vmware'
|
|
||||||
else '/dev/sr0 /usr/local/install/redhat/dvd iso9660 ro,relatime,nojoliet,check=s,map=n,nofail 0 0' }}"
|
|
||||||
state: present
|
|
||||||
backrefs: true
|
|
||||||
|
|
||||||
- name: Write image from RHEL ISO to the target machine
|
|
||||||
when: os in ["rhel8", "rhel9"] and hypervisor == 'vmware'
|
|
||||||
ansible.builtin.command: dd if=/dev/sr1 of=/mnt/usr/local/install/redhat/rhel.iso bs=4M
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Append TempFS to fstab
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/fstab
|
|
||||||
line: "{{ item }}"
|
|
||||||
insertafter: EOF
|
|
||||||
with_items:
|
|
||||||
- ""
|
|
||||||
- "# TempFS"
|
|
||||||
- tmpfs /tmp tmpfs defaults,nosuid,nodev,noexec 0 0
|
|
||||||
- tmpfs /var/tmp tmpfs defaults,nosuid,nodev,noexec 0 0
|
|
||||||
- tmpfs /dev/shm tmpfs defaults,noexec 0 0
|
|
||||||
|
|
||||||
- name: Set local timezone
|
|
||||||
ansible.builtin.command: "{{ item }}"
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
with_items:
|
|
||||||
- systemctl daemon-reload
|
|
||||||
- arch-chroot /mnt ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
|
|
||||||
|
|
||||||
- name: Setup locales
|
|
||||||
block:
|
|
||||||
- name: Configure locale.gen
|
|
||||||
when: os | lower not in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rocky']
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
dest: /mnt/etc/locale.gen
|
|
||||||
regexp: "{{ item.regex }}"
|
|
||||||
line: "{{ item.line }}"
|
|
||||||
loop:
|
|
||||||
- { regex: en_US\.UTF-8 UTF-8, line: en_US.UTF-8 UTF-8 }
|
|
||||||
|
|
||||||
- name: Generate locales
|
|
||||||
when: os | lower not in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rocky']
|
|
||||||
ansible.builtin.command: arch-chroot /mnt /usr/sbin/locale-gen
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Set hostname
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: "{{ hostname }}"
|
|
||||||
dest: /mnt/etc/hostname
|
|
||||||
mode: '0644'
|
|
||||||
|
|
||||||
- name: Add host entry to /etc/hosts
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/hosts
|
|
||||||
line: "{{ ansible_host }} {{ hostname }}"
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Create vconsole.conf
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: KEYMAP=us
|
|
||||||
dest: /mnt/etc/vconsole.conf
|
|
||||||
mode: '0644'
|
|
||||||
|
|
||||||
- name: Create locale.conf
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: LANG=en_US.UTF-8
|
|
||||||
dest: /mnt/etc/locale.conf
|
|
||||||
mode: '0644'
|
|
||||||
|
|
||||||
- name: SSH permit Password
|
|
||||||
ansible.builtin.replace:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
regexp: "#PasswordAuthentication yes"
|
|
||||||
replace: PasswordAuthentication yes
|
|
||||||
|
|
||||||
- name: SSH permit root login
|
|
||||||
ansible.builtin.replace:
|
|
||||||
path: /mnt/etc/ssh/sshd_config
|
|
||||||
regexp: "^#?PermitRootLogin.*"
|
|
||||||
replace: "PermitRootLogin yes"
|
|
||||||
|
|
||||||
- name: Enable Systemd Services
|
|
||||||
ansible.builtin.command: >
|
|
||||||
arch-chroot /mnt systemctl enable NetworkManager
|
|
||||||
{{
|
|
||||||
' ssh' if os | lower in ['ubuntu', 'ubuntu-lts'] else
|
|
||||||
(' sshd' if os | lower not in ['debian11', 'debian12'] else '')
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
'logrotate systemd-resolved systemd-timesyncd systemd-networkd'
|
|
||||||
if os | lower == 'archlinux' else ''
|
|
||||||
}}
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Configure grub
|
|
||||||
when: os | lower not in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rocky']
|
|
||||||
block:
|
|
||||||
- name: Add commandline information to grub config
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
dest: /mnt/etc/default/grub
|
|
||||||
regexp: ^GRUB_CMDLINE_LINUX_DEFAULT=
|
|
||||||
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
|
||||||
|
|
||||||
- name: Change Grub time
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
dest: /mnt/etc/default/grub
|
|
||||||
regexp: ^GRUB_TIMEOUT=
|
|
||||||
line: GRUB_TIMEOUT=1
|
|
||||||
|
|
||||||
- name: Configure Bootloader
|
|
||||||
block:
|
|
||||||
- name: Install Bootloader
|
|
||||||
ansible.builtin.command: arch-chroot /mnt
|
|
||||||
{% if os | lower not in ["archlinux", "debian11", "debian12", "ubuntu", "ubuntu-lts"] %} /usr/sbin/efibootmgr
|
|
||||||
-c -L '{{ os }}' -d "{{ install_drive }}" -p 1
|
|
||||||
-l '\efi\EFI\{% if os | lower in ["rhel8", "rhel9"] %}redhat{% else %}{{ os | lower }}{% endif %}\shimx64.efi'
|
|
||||||
{% else %}/usr/sbin/grub-install --target=x86_64-efi --efi-directory={{ "/boot/efi" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot" }}
|
|
||||||
--bootloader-id={{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }}
|
|
||||||
{% endif %}
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Generate grub config
|
|
||||||
ansible.builtin.command: arch-chroot /mnt
|
|
||||||
{% if os | lower not in ["archlinux", "debian11", "debian12", "ubuntu", "ubuntu-lts"] %}
|
|
||||||
/usr/sbin/grub2-mkconfig -o /boot/efi/EFI/{% if os | lower in ["rhel8", "rhel9"] %}redhat{% else %}{{ os | lower }}{% endif %}/grub.cfg
|
|
||||||
{% else %}
|
|
||||||
/usr/sbin/grub-mkconfig -o {{ "/boot/efi/EFI/ubuntu/grub.cfg" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot/grub/grub.cfg" }}
|
|
||||||
{% endif %}
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Regenerate initramfs
|
|
||||||
when: os | lower not in ["debian11", "debian12", "ubuntu", "ubuntu-lts"]
|
|
||||||
ansible.builtin.command: arch-chroot /mnt
|
|
||||||
{% if os | lower == "archlinux" %} /usr/sbin/mkinitcpio -P
|
|
||||||
{% elif os | lower not in ["debian11", "debian12", "ubuntu", "ubuntu-lts", "archlinux"] %} /usr/bin/dracut --regenerate-all --force
|
|
||||||
{% else %} echo "Skipping initramfs regeneration"
|
|
||||||
{% endif %}
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Extra Configuration
|
|
||||||
block:
|
|
||||||
- name: Append lines to vimrc
|
|
||||||
failed_when: false
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: "{{ '/mnt/etc/vim/vimrc' if os | lower in ['debian11', 'debian12', 'ubuntu', 'ubuntu-lts'] else '/mnt/etc/vimrc' }}"
|
|
||||||
line: "{{ item }}"
|
|
||||||
insertafter: EOF
|
|
||||||
with_items:
|
|
||||||
- set encoding=utf-8
|
|
||||||
- set number
|
|
||||||
- set autoindent
|
|
||||||
- set smartindent
|
|
||||||
- set mouse=a
|
|
||||||
|
|
||||||
- name: Copy FirstRun Script
|
|
||||||
when: os | lower != "archlinux"
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: firstrun.sh.j2
|
|
||||||
dest: /mnt/root/firstrun.sh
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Copy Custom Shell config
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: custom.sh.j2
|
|
||||||
dest: /mnt/etc/profile.d/custom.sh
|
|
||||||
mode: '0644'
|
|
||||||
|
|
||||||
- name: Setup Network
|
|
||||||
block:
|
|
||||||
- name: Generate UUID for Network Profile
|
|
||||||
ansible.builtin.command: uuidgen
|
|
||||||
changed_when: net_uuid.rc == 0
|
|
||||||
register: net_uuid
|
|
||||||
|
|
||||||
- name: Retrieve Network Interface Name
|
|
||||||
ansible.builtin.shell: set -o pipefail && ip r | awk 'NR==1 {print $5}'
|
|
||||||
changed_when: net_inf.rc == 0
|
|
||||||
register: net_inf
|
|
||||||
|
|
||||||
- name: Register MAC Address of the Network Interface
|
|
||||||
ansible.builtin.shell: ip link show "{{ net_inf.stdout }}" | awk '/link\/ether/ {print $2}' | tr '[:lower:]' '[:upper:]'
|
|
||||||
register: net_mac
|
|
||||||
changed_when: net_mac.rc == 0
|
|
||||||
|
|
||||||
- name: Copy NetworkManager keyfile
|
|
||||||
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'
|
|
||||||
|
|
||||||
- name: Setup user account
|
|
||||||
block:
|
|
||||||
- name: Create user account
|
|
||||||
ansible.builtin.command: "{{ item }}"
|
|
||||||
with_items:
|
|
||||||
- arch-chroot /mnt /usr/sbin/useradd --create-home --user-group --groups
|
|
||||||
{{ "sudo" if os | lower in ["debian11", "debian12", "ubuntu", "ubuntu-lts"] else "wheel" }}
|
|
||||||
{{ user_name }} --password {{ user_password | password_hash('sha512') }} --shell /bin/bash
|
|
||||||
- arch-chroot /mnt /usr/sbin/usermod --password '{{ root_password | password_hash('sha512') }}' root --shell /bin/bash
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Add SSH public key to authorized_keys
|
|
||||||
when: user_public_key is defined
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/home/{{ user_name }}/.ssh/authorized_keys
|
|
||||||
line: "{{ user_public_key }}"
|
|
||||||
owner: 1000
|
|
||||||
group: 1000
|
|
||||||
mode: "0600"
|
|
||||||
create: true
|
|
||||||
|
|
||||||
- name: Give sudo access to wheel group
|
|
||||||
ansible.builtin.copy:
|
|
||||||
content: "{{ '%sudo ALL=(ALL) ALL' if os | lower in ['debian11', 'debian12', 'ubuntu', 'ubuntu-lts'] else '%wheel ALL=(ALL) ALL' }}"
|
|
||||||
dest: /mnt/etc/sudoers.d/01-wheel
|
|
||||||
mode: "0440"
|
|
||||||
validate: /usr/sbin/visudo --check --file=%s
|
|
||||||
|
|
||||||
- name: Fix SELinux
|
|
||||||
block:
|
|
||||||
- name: Relabel the filesystem
|
|
||||||
when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rocky']
|
|
||||||
ansible.builtin.command: "arch-chroot /mnt /sbin/fixfiles onboot"
|
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
|
|
||||||
- name: Disable SELinux
|
|
||||||
when: os | lower == "fedora"
|
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /mnt/etc/selinux/config
|
|
||||||
regexp: ^SELINUX=
|
|
||||||
line: SELINUX=permissive
|
|
||||||
|
|||||||
165
roles/configuration/tasks/network.yml
Normal file
165
roles/configuration/tasks/network.yml
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
---
|
||||||
|
- 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
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- ip
|
||||||
|
- -o
|
||||||
|
- link
|
||||||
|
- show
|
||||||
|
register: configuration_ip_link
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Resolve network interface and MAC address
|
||||||
|
vars:
|
||||||
|
configuration_net_inf_from_facts: "{{ (ansible_default_ipv4 | default({})).get('interface', '') }}"
|
||||||
|
configuration_net_inf_from_ip: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
configuration_ip_link.stdout
|
||||||
|
| default('')
|
||||||
|
| regex_findall('^[0-9]+: ([^:]+):', multiline=True)
|
||||||
|
| reject('equalto', 'lo')
|
||||||
|
| list
|
||||||
|
| first
|
||||||
|
)
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
configuration_net_inf_detected: >-
|
||||||
|
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
|
||||||
|
configuration_net_inf_regex: "{{ configuration_net_inf_detected | 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_detected, {}).get('macaddress', '')
|
||||||
|
)
|
||||||
|
| default(
|
||||||
|
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_detected, {}).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('')
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_net_inf: "{{ configuration_net_inf_detected }}"
|
||||||
|
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 Network Interface Name
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- configuration_net_inf | length > 0
|
||||||
|
fail_msg: Failed to detect an active network interface.
|
||||||
|
|
||||||
|
- name: Validate Network Interface MAC Address
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- configuration_net_mac | length > 0
|
||||||
|
fail_msg: Failed to detect the MAC address for network interface {{ configuration_net_inf }}.
|
||||||
|
|
||||||
|
- name: Configure NetworkManager profile
|
||||||
|
when: os | lower not in ["alpine", "void"]
|
||||||
|
block:
|
||||||
|
- name: Copy NetworkManager keyfile
|
||||||
|
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"
|
||||||
|
|
||||||
|
- name: Configure Alpine networking
|
||||||
|
when: os | lower == "alpine"
|
||||||
|
vars:
|
||||||
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
|
configuration_alpine_static: >-
|
||||||
|
{{
|
||||||
|
system_cfg.network.ip is defined
|
||||||
|
and system_cfg.network.ip | string | length > 0
|
||||||
|
and system_cfg.network.prefix is defined
|
||||||
|
and (system_cfg.network.prefix | string | length) > 0
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Write Alpine network interfaces
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/network/interfaces
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
auto {{ configuration_net_inf }}
|
||||||
|
iface {{ configuration_net_inf }} inet {{ 'static' if configuration_alpine_static | bool else 'dhcp' }}
|
||||||
|
{% if configuration_alpine_static | bool %}
|
||||||
|
address {{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
||||||
|
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||||
|
gateway {{ system_cfg.network.gateway }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
- name: Set Alpine DNS resolvers
|
||||||
|
when: configuration_dns_list | length > 0
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
{% for resolver in configuration_dns_list %}
|
||||||
|
nameserver {{ resolver }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
- name: Configure Void networking
|
||||||
|
when: os | lower == "void"
|
||||||
|
vars:
|
||||||
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
|
configuration_void_static: >-
|
||||||
|
{{
|
||||||
|
system_cfg.network.ip is defined
|
||||||
|
and system_cfg.network.ip | string | length > 0
|
||||||
|
and system_cfg.network.prefix is defined
|
||||||
|
and (system_cfg.network.prefix | string | length) > 0
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Write dhcpcd configuration for static networking
|
||||||
|
when: configuration_void_static | bool
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/dhcpcd.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
interface {{ configuration_net_inf }}
|
||||||
|
static ip_address={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}
|
||||||
|
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||||
|
static routers={{ system_cfg.network.gateway }}
|
||||||
|
{% endif %}
|
||||||
|
{% if configuration_dns_list | length > 0 %}
|
||||||
|
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
||||||
|
{% endif %}
|
||||||
19
roles/configuration/tasks/selinux.yml
Normal file
19
roles/configuration/tasks/selinux.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
- name: Fix SELinux
|
||||||
|
when: is_rhel | bool
|
||||||
|
block:
|
||||||
|
- name: Fix SELinux by pre-labeling the filesystem before first boot
|
||||||
|
when: os in ['almalinux', 'rocky', 'rhel'] and system_cfg.features.selinux.enabled | bool
|
||||||
|
ansible.builtin.command: >
|
||||||
|
{{ chroot_command }} /sbin/setfiles -v -F
|
||||||
|
-e /dev -e /proc -e /sys -e /run
|
||||||
|
/etc/selinux/targeted/contexts/files/file_contexts /
|
||||||
|
register: configuration_setfiles_result
|
||||||
|
changed_when: configuration_setfiles_result.rc == 0
|
||||||
|
|
||||||
|
- name: Disable SELinux
|
||||||
|
when: os | lower == "fedora" or not system_cfg.features.selinux.enabled | bool
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/selinux/config
|
||||||
|
regexp: ^SELINUX=
|
||||||
|
line: SELINUX=permissive
|
||||||
79
roles/configuration/tasks/services.yml
Normal file
79
roles/configuration/tasks/services.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
- name: Enable Systemd Services
|
||||||
|
when: os | lower not in ['alpine', 'void']
|
||||||
|
ansible.builtin.command: >
|
||||||
|
{{ chroot_command }} systemctl enable NetworkManager
|
||||||
|
{{ ' 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 '' }}
|
||||||
|
{{
|
||||||
|
(' ssh' if is_debian | bool else ' sshd')
|
||||||
|
if system_cfg.features.ssh.enabled | bool else ''
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
'logrotate systemd-resolved systemd-timesyncd systemd-networkd'
|
||||||
|
if os | lower == 'archlinux' else ''
|
||||||
|
}}
|
||||||
|
register: configuration_enable_services_result
|
||||||
|
changed_when: configuration_enable_services_result.rc == 0
|
||||||
|
|
||||||
|
- name: Enable OpenRC services
|
||||||
|
when: os | lower == 'alpine'
|
||||||
|
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
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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 }}"
|
||||||
|
when: item.stat.exists
|
||||||
|
|
||||||
|
- name: Enable runit services
|
||||||
|
when: os | lower == 'void'
|
||||||
|
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
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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 }}"
|
||||||
|
when: item.stat.exists
|
||||||
7
roles/configuration/tasks/sudo.yml
Normal file
7
roles/configuration/tasks/sudo.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Give sudo access to wheel group
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ '%sudo ALL=(ALL) ALL' if is_debian | bool else '%wheel ALL=(ALL) ALL' }}"
|
||||||
|
dest: /mnt/etc/sudoers.d/01-wheel
|
||||||
|
mode: "0440"
|
||||||
|
validate: /usr/sbin/visudo --check --file=%s
|
||||||
37
roles/configuration/tasks/users.yml
Normal file
37
roles/configuration/tasks/users.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
- name: Create user account
|
||||||
|
vars:
|
||||||
|
configuration_user_group: >-
|
||||||
|
{{ "sudo" if is_debian | bool else "wheel" }}
|
||||||
|
configuration_useradd_cmd: >-
|
||||||
|
{{ chroot_command }} /usr/sbin/useradd --create-home --user-group
|
||||||
|
--groups {{ configuration_user_group }} {{ system_cfg.user.name }}
|
||||||
|
--password {{ system_cfg.user.password | password_hash('sha512') }} --shell /bin/bash
|
||||||
|
configuration_root_cmd: >-
|
||||||
|
{{ chroot_command }} /usr/sbin/usermod --password
|
||||||
|
'{{ system_cfg.root.password | password_hash('sha512') }}' root --shell /bin/bash
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "{{ configuration_useradd_cmd }}"
|
||||||
|
- "{{ configuration_root_cmd }}"
|
||||||
|
register: configuration_user_result
|
||||||
|
changed_when: configuration_user_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure .ssh directory exists
|
||||||
|
when: system_cfg.user.key | length > 0
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/home/{{ system_cfg.user.name }}/.ssh
|
||||||
|
state: directory
|
||||||
|
owner: 1000
|
||||||
|
group: 1000
|
||||||
|
mode: "0700"
|
||||||
|
|
||||||
|
- name: Add SSH public key to authorized_keys
|
||||||
|
when: system_cfg.user.key | length > 0
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/home/{{ system_cfg.user.name }}/.ssh/authorized_keys
|
||||||
|
line: "{{ system_cfg.user.key }}"
|
||||||
|
owner: 1000
|
||||||
|
group: 1000
|
||||||
|
mode: "0600"
|
||||||
|
create: true
|
||||||
@@ -9,4 +9,7 @@ PROMPT_COMMAND="history -a;$PROMPT_COMMAND"
|
|||||||
|
|
||||||
# History Size
|
# History Size
|
||||||
HISTFILESIZE=
|
HISTFILESIZE=
|
||||||
HISTSIZE=
|
HISTSIZE=
|
||||||
|
|
||||||
|
# Enable vi mode
|
||||||
|
set -o vi
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
[connection]
|
[connection]
|
||||||
id=LAN
|
id=LAN
|
||||||
uuid={{ net_uuid.stdout }}
|
uuid={{ configuration_net_uuid }}
|
||||||
type=ethernet
|
type=ethernet
|
||||||
interface-name={{ net_inf.stdout }}
|
|
||||||
|
|
||||||
[ethernet]
|
|
||||||
mac-address={{ net_mac.stdout }}
|
|
||||||
|
|
||||||
[ipv4]
|
[ipv4]
|
||||||
address={{ vm_ip }},{{ vm_gw }}
|
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||||
dns={{ vm_dns }}
|
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||||
|
{% if system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
||||||
|
address1={{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}{{ (',' ~ system_cfg.network.gateway) if (system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length) else '' }}
|
||||||
method=manual
|
method=manual
|
||||||
|
{% else %}
|
||||||
|
method=auto
|
||||||
|
{% endif %}
|
||||||
|
{% if dns_list %}
|
||||||
|
dns={{ dns_list | join(';') }}
|
||||||
|
{% endif %}
|
||||||
|
{% if dns_list %}
|
||||||
|
ignore-auto-dns=true
|
||||||
|
{% endif %}
|
||||||
|
{% if search_list %}
|
||||||
|
dns-search={{ search_list | join(';') }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
[ipv6]
|
[ipv6]
|
||||||
addr-gen-mode=stable-privacy
|
addr-gen-mode=stable-privacy
|
||||||
|
|||||||
@@ -1,130 +1,265 @@
|
|||||||
---
|
---
|
||||||
- name: Configre work environment
|
- name: Configure work environment
|
||||||
become: true
|
become: "{{ hypervisor_type != 'vmware' }}"
|
||||||
block:
|
block:
|
||||||
- name: Wait for connection
|
- name: Wait for connection
|
||||||
ansible.builtin.wait_for_connection:
|
ansible.builtin.wait_for_connection:
|
||||||
timeout: 60
|
timeout: 180
|
||||||
delay: 5
|
delay: 5
|
||||||
|
|
||||||
- name: Gather facts
|
- name: Gather facts
|
||||||
ansible.builtin.setup:
|
ansible.builtin.setup:
|
||||||
|
|
||||||
- name: Check if host is booted from the Arch install media
|
- name: Check for live environment markers
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: /run/archiso
|
path: "{{ item }}"
|
||||||
register: archiso_stat
|
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
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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'))
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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: Abort if the host is not booted from the Arch install media
|
- 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:
|
ansible.builtin.fail:
|
||||||
msg: This host is not booted from the Arch install media!
|
msg: This host is not booted from the Arch install media!
|
||||||
when: not archiso_stat.stat.exists
|
|
||||||
|
|
||||||
- name: Setect Interface
|
- name: Select primary Network Interface
|
||||||
when: hypervisor == "vmware"
|
when: hypervisor_type == "vmware"
|
||||||
ansible.builtin.shell: "set -o pipefail && ip l | awk -F': ' '!/lo/{print $2; exit}'"
|
ansible.builtin.set_fact:
|
||||||
changed_when: interface_name.rc == 0
|
environment_interface_name: >-
|
||||||
register: interface_name
|
{{
|
||||||
|
(
|
||||||
|
(ansible_facts.interfaces | default(ansible_facts['ansible_interfaces'] | default([])))
|
||||||
|
| reject('equalto', 'lo')
|
||||||
|
| list
|
||||||
|
| first
|
||||||
|
)
|
||||||
|
| default('')
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Set IP-Address
|
- name: Set IP-Address
|
||||||
when: hypervisor == "vmware"
|
when:
|
||||||
ansible.builtin.command: "ip addr replace {{ ansible_host }}/{{ vm_nms | default(24) }} dev {{ interface_name.stdout }}"
|
- hypervisor_type == "vmware"
|
||||||
changed_when: result.rc == 0
|
- system_cfg.network.ip is defined and system_cfg.network.ip | string | length > 0
|
||||||
register: result
|
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
|
- name: Set Default Gateway
|
||||||
when: hypervisor == "vmware"
|
when:
|
||||||
ansible.builtin.command: "ip route replace default via {{ vm_gw }}"
|
- hypervisor_type == "vmware"
|
||||||
changed_when: result.rc == 0
|
- system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length > 0
|
||||||
register: result
|
- 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: Synchronize clock via NTP
|
- name: Synchronize clock via NTP
|
||||||
ansible.builtin.command: timedatectl set-ntp true
|
ansible.builtin.command: timedatectl set-ntp true
|
||||||
changed_when: result.rc == 0
|
register: environment_ntp_result
|
||||||
register: result
|
changed_when: environment_ntp_result.rc == 0
|
||||||
|
|
||||||
- name: Configure SSH for root login
|
- name: Configure SSH for root login
|
||||||
when: hypervisor == "vmware" and vmware_ssh | bool
|
when: hypervisor_type == "vmware" and hypervisor_cfg.ssh | bool
|
||||||
block:
|
block:
|
||||||
- name: Allow empty passwords temporarily
|
- name: Allow login
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
path: /etc/ssh/sshd_config
|
path: /etc/ssh/sshd_config
|
||||||
regexp: "^#?PermitEmptyPasswords.*"
|
regexp: "{{ item.regexp }}"
|
||||||
replace: "PermitEmptyPasswords yes"
|
replace: "{{ item.replace }}"
|
||||||
|
loop:
|
||||||
- name: Allow root login
|
- regexp: "^#?PermitEmptyPasswords.*"
|
||||||
ansible.builtin.replace:
|
replace: "PermitEmptyPasswords yes"
|
||||||
path: /etc/ssh/sshd_config
|
- regexp: "^#?PermitRootLogin.*"
|
||||||
regexp: "^#?PermitRootLogin.*"
|
replace: "PermitRootLogin yes"
|
||||||
replace: "PermitRootLogin yes"
|
|
||||||
|
|
||||||
- name: Reload SSH service to apply changes
|
- name: Reload SSH service to apply changes
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: sshd
|
name: sshd
|
||||||
state: reloaded
|
state: reloaded
|
||||||
|
|
||||||
- name: Set connection back to SSH
|
- name: Set SSH connection for VMware
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
ansible_connection: ssh
|
ansible_connection: ssh
|
||||||
ansible_user: "root"
|
ansible_user: root
|
||||||
ansible_password: ""
|
|
||||||
ansible_become_password: ""
|
|
||||||
ansible_ssh_extra_args: '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
|
|
||||||
|
|
||||||
- name: Speed-up Bootstrap process
|
- name: Prepare installer environment
|
||||||
ansible.builtin.lineinfile:
|
|
||||||
path: /etc/pacman.conf
|
|
||||||
regexp: ^#ParallelDownloads =
|
|
||||||
line: ParallelDownloads = 20
|
|
||||||
|
|
||||||
- name: Wait for Pacman
|
|
||||||
ansible.builtin.wait_for:
|
|
||||||
timeout: 15
|
|
||||||
|
|
||||||
- name: Setup Pacman
|
|
||||||
community.general.pacman:
|
|
||||||
update_cache: true
|
|
||||||
force: true
|
|
||||||
name: "{{ item.name }}"
|
|
||||||
state: latest
|
|
||||||
loop:
|
|
||||||
- { name: glibc }
|
|
||||||
- { name: dnf, os: [almalinux, fedora, rhel9, rhel8, rocky] }
|
|
||||||
- { name: debootstrap, os: [debian11, debian12, ubuntu, ubuntu-lts] }
|
|
||||||
- { name: debian-archive-keyring, os: [debian11, debian12] }
|
|
||||||
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
|
||||||
when: "'os' not in item or os in item.os"
|
|
||||||
retries: 4
|
|
||||||
delay: 15
|
|
||||||
|
|
||||||
- name: Prepare /iso mount and repository for RHEL-based systems
|
|
||||||
when: os | lower in ["rhel8", "rhel9"]
|
|
||||||
block:
|
block:
|
||||||
- name: Create /iso directory
|
- name: Speed-up Bootstrap process
|
||||||
ansible.builtin.file:
|
when: not (custom_iso | bool)
|
||||||
path: /usr/local/install/redhat/dvd
|
ansible.builtin.lineinfile:
|
||||||
state: directory
|
path: /etc/pacman.conf
|
||||||
mode: '0755'
|
regexp: ^#ParallelDownloads =
|
||||||
|
line: ParallelDownloads = 20
|
||||||
|
|
||||||
- name: Mount RHEL ISO
|
- name: Wait for pacman lock to be released
|
||||||
ansible.posix.mount:
|
when: not (custom_iso | bool)
|
||||||
src: "{{ '/dev/sr1' if hypervisor == 'vmware' else '/dev/sr2' }}"
|
ansible.builtin.wait_for:
|
||||||
path: /usr/local/install/redhat/dvd
|
path: /var/lib/pacman/db.lck
|
||||||
fstype: iso9660
|
state: absent
|
||||||
opts: "ro,loop"
|
timeout: 120
|
||||||
state: mounted
|
changed_when: false
|
||||||
|
|
||||||
- name: Configure RHEL Repos for installation
|
- name: Setup Pacman
|
||||||
when: os | lower in ["almalinux", "fedora", "rhel8", "rhel9", "rocky"]
|
when:
|
||||||
|
- not (custom_iso | bool)
|
||||||
|
- item.os is not defined or (os_resolved | default(os)) in item.os
|
||||||
|
community.general.pacman:
|
||||||
|
update_cache: true
|
||||||
|
force: true
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: latest
|
||||||
|
loop:
|
||||||
|
- { name: glibc }
|
||||||
|
- { name: dnf, os: [almalinux8, almalinux9, almalinux10, fedora40, fedora41, fedora42, fedora43, rhel8, rhel9, rhel10, rocky8, rocky9, rocky10] }
|
||||||
|
- { name: debootstrap, os: [debian10, debian11, debian12, debian13, debianunstable, ubuntu, ubuntu-lts] }
|
||||||
|
- { name: debian-archive-keyring, os: [debian10, debian11, debian12, debian13, debianunstable] }
|
||||||
|
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
||||||
|
retries: 4
|
||||||
|
delay: 15
|
||||||
|
|
||||||
|
- 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: Select RHEL ISO device
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
environment_rhel_iso_device: >-
|
||||||
|
{{
|
||||||
|
'/dev/sr2'
|
||||||
|
if hypervisor_type == 'libvirt'
|
||||||
|
else '/dev/sr1'
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Configure RHEL Repos for installation
|
||||||
|
when: is_rhel | bool
|
||||||
|
block:
|
||||||
|
- name: Select repository template
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
environment_repo_template: >-
|
||||||
|
{{
|
||||||
|
(os_resolved | default(os)) | lower
|
||||||
|
if os == 'rhel'
|
||||||
|
else os | lower
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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: "{{ environment_repo_template }}.repo.j2"
|
||||||
|
dest: /etc/yum.repos.d/{{ environment_repo_template }}.repo
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Check for third-party preparation tasks
|
||||||
|
run_once: true
|
||||||
|
become: false
|
||||||
|
delegate_to: localhost
|
||||||
|
vars:
|
||||||
|
ansible_connection: local
|
||||||
block:
|
block:
|
||||||
- name: Create directories for repository files and RPM GPG keys
|
- name: Resolve third-party preparation task path
|
||||||
ansible.builtin.file:
|
ansible.builtin.set_fact:
|
||||||
path: /etc/yum.repos.d
|
environment_thirdparty_tasks_path: >-
|
||||||
state: directory
|
{{
|
||||||
mode: '0755'
|
thirdparty_preparation_tasks_path
|
||||||
|
if thirdparty_preparation_tasks_path | regex_search('^/')
|
||||||
|
else playbook_dir + '/' + thirdparty_preparation_tasks_path
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Create RHEL repository file
|
- name: Stat third-party preparation tasks
|
||||||
ansible.builtin.template:
|
ansible.builtin.stat:
|
||||||
src: "{{ os | lower }}.repo.j2"
|
path: "{{ environment_thirdparty_tasks_path }}"
|
||||||
dest: /etc/yum.repos.d/{{ os | lower }}.repo
|
register: environment_thirdparty_tasks_stat
|
||||||
mode: '0644'
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Run third-party preparation tasks
|
||||||
|
when:
|
||||||
|
- thirdparty_preparation_tasks_path | length > 0
|
||||||
|
- environment_thirdparty_tasks_stat.stat.exists
|
||||||
|
ansible.builtin.include_tasks: >-
|
||||||
|
{{
|
||||||
|
thirdparty_preparation_tasks_path
|
||||||
|
if thirdparty_preparation_tasks_path | regex_search('^/')
|
||||||
|
else playbook_dir + '/' + thirdparty_preparation_tasks_path
|
||||||
|
}}
|
||||||
|
|||||||
95
roles/global_defaults/defaults/main.yml
Normal file
95
roles/global_defaults/defaults/main.yml
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
# User input. Normalized into hypervisor_cfg + hypervisor_type.
|
||||||
|
hypervisor:
|
||||||
|
type: "none"
|
||||||
|
hypervisor_defaults:
|
||||||
|
type: "none"
|
||||||
|
url: ""
|
||||||
|
username: ""
|
||||||
|
password: ""
|
||||||
|
host: ""
|
||||||
|
storage: ""
|
||||||
|
datacenter: ""
|
||||||
|
cluster: ""
|
||||||
|
certs: false
|
||||||
|
ssh: false
|
||||||
|
|
||||||
|
custom_iso: false
|
||||||
|
thirdparty_preparation_tasks_path: "dropins/preparation.yml"
|
||||||
|
|
||||||
|
system_defaults:
|
||||||
|
type: "virtual" # virtual|physical
|
||||||
|
os: ""
|
||||||
|
version: ""
|
||||||
|
filesystem: ""
|
||||||
|
name: ""
|
||||||
|
id: ""
|
||||||
|
cpus: 0
|
||||||
|
memory: 0 # MiB
|
||||||
|
balloon: 0 # MiB
|
||||||
|
network:
|
||||||
|
bridge: ""
|
||||||
|
vlan: ""
|
||||||
|
ip: ""
|
||||||
|
prefix: ""
|
||||||
|
gateway: ""
|
||||||
|
dns:
|
||||||
|
servers: []
|
||||||
|
search: []
|
||||||
|
path: ""
|
||||||
|
packages: []
|
||||||
|
disks: []
|
||||||
|
user:
|
||||||
|
name: ""
|
||||||
|
password: ""
|
||||||
|
key: ""
|
||||||
|
root:
|
||||||
|
password: ""
|
||||||
|
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
|
||||||
|
chroot:
|
||||||
|
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
|
||||||
|
|
||||||
|
system_disk_defaults:
|
||||||
|
size: 0
|
||||||
|
device: ""
|
||||||
|
mount:
|
||||||
|
path: ""
|
||||||
|
fstype: ""
|
||||||
|
label: ""
|
||||||
|
opts: "defaults"
|
||||||
22
roles/global_defaults/tasks/hypervisor.yml
Normal file
22
roles/global_defaults/tasks/hypervisor.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure hypervisor input is a dictionary
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hypervisor: "{{ hypervisor | default({}) }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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: Normalize hypervisor configuration
|
||||||
|
vars:
|
||||||
|
merged: "{{ hypervisor_defaults | combine(hypervisor, recursive=True) }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
hypervisor_cfg: "{{ merged }}"
|
||||||
|
hypervisor_type: "{{ merged.type | string | lower }}"
|
||||||
|
changed_when: false
|
||||||
79
roles/global_defaults/tasks/main.yml
Normal file
79
roles/global_defaults/tasks/main.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
- name: Global defaults loaded
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: Global defaults loaded.
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Normalize hypervisor inputs
|
||||||
|
ansible.builtin.include_tasks: hypervisor.yml
|
||||||
|
|
||||||
|
- name: Normalize system inputs
|
||||||
|
ansible.builtin.include_tasks: system.yml
|
||||||
|
|
||||||
|
- name: Validate variables
|
||||||
|
ansible.builtin.include_tasks: validation.yml
|
||||||
|
|
||||||
|
- name: Set OS family flags
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
is_rhel: "{{ os | lower in ['almalinux', 'fedora', 'rhel', 'rocky'] }}"
|
||||||
|
is_debian: "{{ os | lower in ['debian', 'ubuntu', 'ubuntu-lts'] }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Normalize OS version for keying
|
||||||
|
when:
|
||||||
|
- os_version is defined
|
||||||
|
- (os_version | string | length) > 0
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
os_version_major: "{{ (os_version | string).split('.')[0] }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Resolve final OS key with version
|
||||||
|
when:
|
||||||
|
- os_version is defined
|
||||||
|
- (os_version | string | length) > 0
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
os_resolved: >-
|
||||||
|
{{
|
||||||
|
'debian' + os_version | string if os == 'debian'
|
||||||
|
else 'fedora' + os_version | string if os == 'fedora'
|
||||||
|
else 'rocky' + os_version_major if os == 'rocky'
|
||||||
|
else 'almalinux' + os_version_major if os == 'almalinux'
|
||||||
|
else 'rhel' + os_version_major if os == 'rhel'
|
||||||
|
else os
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set chroot command wrapper
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
chroot_command: >-
|
||||||
|
{{
|
||||||
|
'systemd-nspawn -D /mnt'
|
||||||
|
if (system_cfg.features.chroot.tool | default('arch-chroot')) == 'systemd-nspawn'
|
||||||
|
else (system_cfg.features.chroot.tool | default('arch-chroot')) ~ ' /mnt'
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set Python interpreter for RHEL-based installers
|
||||||
|
when:
|
||||||
|
- ansible_python_interpreter is not defined
|
||||||
|
- is_rhel | bool
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set SSH access
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "virtual"
|
||||||
|
- hypervisor_type != "vmware"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ansible_user: "{{ system_cfg.user.name }}"
|
||||||
|
ansible_password: "{{ system_cfg.user.password }}"
|
||||||
|
ansible_become_password: "{{ system_cfg.user.password }}"
|
||||||
|
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set connection for VMware
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ansible_connection: vmware_tools
|
||||||
|
changed_when: false
|
||||||
258
roles/global_defaults/tasks/system.yml
Normal file
258
roles/global_defaults/tasks/system.yml
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure system input is a dictionary
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system: "{{ system | default({}) }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Validate system input types
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system is mapping
|
||||||
|
- system.network is not defined or system.network is mapping
|
||||||
|
- system.user is not defined or system.user 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, user, root, luks, features) must be dictionaries."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Build normalized system configuration
|
||||||
|
vars:
|
||||||
|
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
||||||
|
system_type: "{{ 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
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_cfg:
|
||||||
|
type: "{{ system_type }}"
|
||||||
|
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' 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 }}"
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
||||||
|
dns:
|
||||||
|
servers: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
system_raw.network.dns.servers
|
||||||
|
if system_raw.network.dns.servers is iterable and system_raw.network.dns.servers is not string
|
||||||
|
else (system_raw.network.dns.servers | string).split(',')
|
||||||
|
)
|
||||||
|
| map('trim')
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
search: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
system_raw.network.dns.search
|
||||||
|
if system_raw.network.dns.search is iterable and system_raw.network.dns.search is not string
|
||||||
|
else (system_raw.network.dns.search | string).split(',')
|
||||||
|
)
|
||||||
|
| map('trim')
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
path: "{{ system_raw.path | 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
|
||||||
|
}}
|
||||||
|
disks: "{{ system_raw.disks | default([]) }}"
|
||||||
|
user:
|
||||||
|
name: "{{ system_raw.user.name | string }}"
|
||||||
|
password: "{{ system_raw.user.password | string }}"
|
||||||
|
key: "{{ system_raw.user.key | string }}"
|
||||||
|
root:
|
||||||
|
password: "{{ system_raw.root.password | string }}"
|
||||||
|
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 }}"
|
||||||
|
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 }}"
|
||||||
|
chroot:
|
||||||
|
tool: "{{ system_raw.features.chroot.tool | string }}"
|
||||||
|
hostname: "{{ system_name }}"
|
||||||
|
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||||
|
os_version: "{{ system_raw.version | default('') | string }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Normalize system disks input
|
||||||
|
vars:
|
||||||
|
system_disks: "{{ system_cfg.disks | default([]) }}"
|
||||||
|
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
system_disk_device_prefix: >-
|
||||||
|
{{
|
||||||
|
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.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: []
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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) }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- 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 }}"
|
||||||
|
changed_when: false
|
||||||
331
roles/global_defaults/tasks/validation.yml
Normal file
331
roles/global_defaults/tasks/validation.yml
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
---
|
||||||
|
- name: Validate core variables
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg is defined
|
||||||
|
- system_cfg is mapping
|
||||||
|
- system_cfg.type is defined
|
||||||
|
- system_cfg.type in ["virtual", "physical"]
|
||||||
|
- hypervisor_cfg is defined
|
||||||
|
- hypervisor_cfg is mapping
|
||||||
|
- hypervisor_type is defined
|
||||||
|
- hypervisor_type in ["libvirt", "proxmox", "vmware", "xen", "none"]
|
||||||
|
- system_cfg.filesystem is defined
|
||||||
|
- system_cfg.filesystem in ["btrfs", "ext4", "xfs"]
|
||||||
|
- install_drive is defined
|
||||||
|
- install_drive | string | length > 0
|
||||||
|
- hostname is defined
|
||||||
|
- hostname | string | length > 0
|
||||||
|
fail_msg: Invalid core variables were specified, please check your inventory/vars.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate hypervisor relationship
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.type == "physical" or hypervisor_type in ["libvirt", "proxmox", "vmware", "xen"]
|
||||||
|
fail_msg: "hypervisor.type must be one of: libvirt, proxmox, vmware, xen when system.type=virtual."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate hypervisor schema
|
||||||
|
vars:
|
||||||
|
hypervisor_allowed_keys: "{{ hypervisor_defaults | dict2items | map(attribute='key') | list }}"
|
||||||
|
hypervisor_keys: "{{ (hypervisor | default({})) | dict2items | map(attribute='key') | list }}"
|
||||||
|
hypervisor_unknown_keys: "{{ hypervisor_keys | difference(hypervisor_allowed_keys) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- hypervisor_unknown_keys | length == 0
|
||||||
|
fail_msg: "Unsupported hypervisor keys: {{ hypervisor_unknown_keys | join(', ') }}"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system schema
|
||||||
|
vars:
|
||||||
|
system_allowed_keys: "{{ system_defaults | dict2items | map(attribute='key') | list }}"
|
||||||
|
system_keys: "{{ (system | default({})) | dict2items | map(attribute='key') | list }}"
|
||||||
|
system_unknown_keys: "{{ system_keys | difference(system_allowed_keys) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_unknown_keys | length == 0
|
||||||
|
fail_msg: "Unsupported system keys: {{ system_unknown_keys | join(', ') }}."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate nested system mappings
|
||||||
|
loop:
|
||||||
|
- network
|
||||||
|
- user
|
||||||
|
- root
|
||||||
|
- luks
|
||||||
|
- features
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system[item] is not defined or system[item] is mapping
|
||||||
|
fail_msg: "system.{{ item }} must be a dictionary."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system sub-dict schemas
|
||||||
|
loop:
|
||||||
|
- network
|
||||||
|
- user
|
||||||
|
- root
|
||||||
|
- luks
|
||||||
|
loop_control:
|
||||||
|
label: "system.{{ item }}"
|
||||||
|
vars:
|
||||||
|
sub_input: "{{ (system[item] | default({})) | dict2items | map(attribute='key') | list }}"
|
||||||
|
sub_allowed: "{{ system_defaults[item] | dict2items | map(attribute='key') | list }}"
|
||||||
|
sub_unknown: "{{ sub_input | difference(sub_allowed) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- sub_unknown | length == 0
|
||||||
|
fail_msg: "Unsupported system.{{ item }} keys: {{ sub_unknown | join(', ') }}"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system.luks.tpm2 schema
|
||||||
|
vars:
|
||||||
|
tpm2_input: >-
|
||||||
|
{{
|
||||||
|
(system.luks if (system.luks is defined and system.luks is mapping) else {}).tpm2
|
||||||
|
| default({})
|
||||||
|
}}
|
||||||
|
tpm2_allowed_keys: "{{ system_defaults.luks.tpm2 | dict2items | map(attribute='key') | list }}"
|
||||||
|
tpm2_unknown: "{{ (tpm2_input | dict2items | map(attribute='key') | list) | difference(tpm2_allowed_keys) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system.luks is not defined or system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
||||||
|
- tpm2_unknown | length == 0
|
||||||
|
fail_msg: "Unsupported system.luks.tpm2 keys: {{ tpm2_unknown | join(', ') }}"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system.features schema
|
||||||
|
vars:
|
||||||
|
features_allowed_keys: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
||||||
|
features_unknown: >-
|
||||||
|
{{
|
||||||
|
((system.features | default({})) | dict2items | map(attribute='key') | list)
|
||||||
|
| difference(features_allowed_keys)
|
||||||
|
}}
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- features_unknown | length == 0
|
||||||
|
fail_msg: "Unsupported system.features keys: {{ features_unknown | join(', ') }}"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system.features leaf schemas
|
||||||
|
loop: "{{ system_defaults.features | dict2items }}"
|
||||||
|
loop_control:
|
||||||
|
label: "system.features.{{ item.key }}"
|
||||||
|
vars:
|
||||||
|
feature_input: "{{ (system.features | default({}))[item.key] | default({}) }}"
|
||||||
|
feature_allowed_keys: "{{ item.value | dict2items | map(attribute='key') | list }}"
|
||||||
|
feature_unknown: "{{ (feature_input | dict2items | map(attribute='key') | list) | difference(feature_allowed_keys) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- feature_input is mapping
|
||||||
|
- feature_unknown | length == 0
|
||||||
|
fail_msg: "Unsupported system.features.{{ item.key }} keys: {{ feature_unknown | join(', ') }}"
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate OS and version inputs
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- os is defined
|
||||||
|
- os in ["almalinux", "alpine", "archlinux", "debian", "fedora", "opensuse", "rhel", "rocky", "ubuntu", "ubuntu-lts", "void"]
|
||||||
|
- >-
|
||||||
|
os not in ["debian", "fedora", "rocky", "almalinux", "rhel"]
|
||||||
|
or (os_version is defined and (os_version | string | length) > 0)
|
||||||
|
- >-
|
||||||
|
os_version is not defined or (os_version | string | length) == 0
|
||||||
|
or (
|
||||||
|
os == "debian" and (os_version | string) in ["10", "11", "12", "13", "unstable"]
|
||||||
|
) or (
|
||||||
|
os == "fedora" and (os_version | string) in ["40", "41", "42", "43"]
|
||||||
|
) or (
|
||||||
|
os in ["rocky", "almalinux"]
|
||||||
|
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
||||||
|
) or (
|
||||||
|
os == "rhel"
|
||||||
|
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
||||||
|
) or (
|
||||||
|
os in ["alpine", "archlinux", "opensuse", "ubuntu", "ubuntu-lts", "void"]
|
||||||
|
)
|
||||||
|
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate RHEL ISO requirement
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- os != "rhel" or (rhel_iso is defined and (rhel_iso | string | length) > 0)
|
||||||
|
fail_msg: "rhel_iso is required when os=rhel."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate Proxmox hypervisor inputs
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "virtual"
|
||||||
|
- hypervisor_type == "proxmox"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- hypervisor_cfg.url | string | length > 0
|
||||||
|
- hypervisor_cfg.username | string | length > 0
|
||||||
|
- hypervisor_cfg.password | string | length > 0
|
||||||
|
- hypervisor_cfg.host | string | length > 0
|
||||||
|
- hypervisor_cfg.storage | string | length > 0
|
||||||
|
- system_cfg.id | string | length > 0
|
||||||
|
- system_cfg.network.bridge | string | length > 0
|
||||||
|
fail_msg: "Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage), system.id, and system.network.bridge."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate VMware hypervisor inputs
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "virtual"
|
||||||
|
- hypervisor_type == "vmware"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- hypervisor_cfg.url | string | length > 0
|
||||||
|
- hypervisor_cfg.username | string | length > 0
|
||||||
|
- hypervisor_cfg.password | string | length > 0
|
||||||
|
- hypervisor_cfg.datacenter | string | length > 0
|
||||||
|
- hypervisor_cfg.cluster | string | length > 0
|
||||||
|
- hypervisor_cfg.storage | string | length > 0
|
||||||
|
- system_cfg.network.bridge | string | length > 0
|
||||||
|
fail_msg: "Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage) and system.network.bridge."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate Xen hypervisor inputs
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "virtual"
|
||||||
|
- hypervisor_type == "xen"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.network.bridge | string | length > 0
|
||||||
|
fail_msg: "Missing required Xen inputs. Define system.network.bridge."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate virtual installer ISO requirement
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.type == "physical" or (boot_iso is defined and (boot_iso | string | length) > 0)
|
||||||
|
fail_msg: "boot_iso is required when system.type=virtual."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate firewall and feature flags
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.features.firewall.backend is defined
|
||||||
|
- system_cfg.features.firewall.backend in ["firewalld", "ufw"]
|
||||||
|
- system_cfg.features.firewall.toolkit is defined
|
||||||
|
- system_cfg.features.firewall.toolkit in ["iptables", "nftables"]
|
||||||
|
- system_cfg.features.firewall.enabled is defined
|
||||||
|
- system_cfg.features.banner.motd is defined
|
||||||
|
- system_cfg.features.banner.sudo is defined
|
||||||
|
- system_cfg.luks.enabled is defined
|
||||||
|
- system_cfg.features.chroot.tool is defined
|
||||||
|
- system_cfg.features.chroot.tool in ["arch-chroot", "chroot", "systemd-nspawn"]
|
||||||
|
fail_msg: Invalid feature flags were specified, please check your inventory/vars.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate virtual system sizing
|
||||||
|
when: system_cfg.type == "virtual"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.cpus is defined and (system_cfg.cpus | int) > 0
|
||||||
|
- system_cfg.memory is defined and (system_cfg.memory | int) > 0
|
||||||
|
- system_cfg.disks is defined and (system_cfg.disks | length) > 0
|
||||||
|
- (system_cfg.disks[0].size | float) > 0
|
||||||
|
- (system_cfg.disks[0].size | float) >= 20
|
||||||
|
- >-
|
||||||
|
system_cfg.filesystem != "btrfs"
|
||||||
|
or (
|
||||||
|
(system_cfg.disks[0].size | float)
|
||||||
|
>= (
|
||||||
|
(
|
||||||
|
(system_cfg.memory | float / 1024 >= 16.0)
|
||||||
|
| ternary(
|
||||||
|
(system_cfg.memory | float / 2048),
|
||||||
|
[system_cfg.memory | float / 1024, 4.0] | max
|
||||||
|
)
|
||||||
|
)
|
||||||
|
+ 5.5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fail_msg: "Invalid system sizing. Check system.cpus, system.memory, and system.disks[0].size."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate all virtual disks have a positive size
|
||||||
|
when: system_cfg.type == "virtual"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.size is defined
|
||||||
|
- (item.size | float) > 0
|
||||||
|
fail_msg: "Each system disk must have a positive size when system.type=virtual: {{ item | to_json }}"
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system_cfg.disks | default([]) }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
- name: Validate primary disk mount is not used
|
||||||
|
when:
|
||||||
|
- system_cfg.disks is defined
|
||||||
|
- system_cfg.disks | length > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (system_cfg.disks[0].mount.path | default('') | string | trim) == ''
|
||||||
|
fail_msg: "system.disks[0].mount.path must be empty; use system.disks[1:] for additional mounts."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate disk mountpoint inputs
|
||||||
|
vars:
|
||||||
|
system_disk_mounts: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.disks | default([]))
|
||||||
|
| map(attribute='mount')
|
||||||
|
| map(attribute='path')
|
||||||
|
| map('string')
|
||||||
|
| map('trim')
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_disk_mounts | length == (system_disk_mounts | unique | list | length)
|
||||||
|
fail_msg: "Duplicate disk mountpoints found in system.disks."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate disk mount definitions
|
||||||
|
when: system_cfg.disks is defined
|
||||||
|
vars:
|
||||||
|
reserved_mounts:
|
||||||
|
- /boot
|
||||||
|
- /boot/efi
|
||||||
|
- /home
|
||||||
|
- /swap
|
||||||
|
- /var
|
||||||
|
- /var/cache/pacman/pkg
|
||||||
|
- /var/log
|
||||||
|
- /var/log/audit
|
||||||
|
disk_mount: "{{ (item.mount.path | default('') | string) | trim }}"
|
||||||
|
disk_fstype: "{{ (item.mount.fstype | default('') | string) | trim }}"
|
||||||
|
disk_device: "{{ (item.device | default('') | string) | trim }}"
|
||||||
|
disk_size: "{{ item.size | default(0) }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- disk_mount == "" or disk_mount.startswith("/")
|
||||||
|
- disk_mount == "" or disk_mount != "/"
|
||||||
|
- disk_mount == "" or disk_mount not in reserved_mounts
|
||||||
|
- disk_mount == "" or disk_fstype in ["btrfs", "ext4", "xfs"]
|
||||||
|
- disk_mount == "" or system_cfg.type == "virtual" or (disk_device | length) > 0
|
||||||
|
- disk_mount == "" or system_cfg.type != "virtual" or (disk_size | float) > 0
|
||||||
|
fail_msg: "Invalid system disk entry: {{ item | to_json }}"
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system_cfg.disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
- name: Validate static IP requirements
|
||||||
|
when: system_cfg.network.ip is defined and (system_cfg.network.ip | string | length) > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.network.prefix is defined
|
||||||
|
- (system_cfg.network.prefix | int) > 0
|
||||||
|
fail_msg: "system.network.prefix is required when system.network.ip is set."
|
||||||
|
quiet: true
|
||||||
134
roles/partitioning/defaults/main.yml
Normal file
134
roles/partitioning/defaults/main.yml
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
---
|
||||||
|
partitioning_btrfs_compress_opt: "{{ 'compress=zstd:15' if system_cfg.features.zstd.enabled | bool else '' }}"
|
||||||
|
partitioning_boot_partition_suffix: 1
|
||||||
|
partitioning_main_partition_suffix: 2
|
||||||
|
partitioning_efi_size_mib: 512
|
||||||
|
partitioning_efi_start_mib: 1
|
||||||
|
partitioning_efi_end_mib: "{{ (partitioning_efi_start_mib | int) + (partitioning_efi_size_mib | int) }}"
|
||||||
|
partitioning_boot_size_mib: 1024
|
||||||
|
partitioning_use_full_disk: true
|
||||||
|
partitioning_separate_boot: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.luks.enabled | bool)
|
||||||
|
and (os | lower not in ['archlinux'])
|
||||||
|
}}
|
||||||
|
partitioning_boot_fs_fstype: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.filesystem | lower)
|
||||||
|
if (system_cfg.filesystem | lower) != 'btrfs'
|
||||||
|
else ('xfs' if is_rhel else 'ext4')
|
||||||
|
}}
|
||||||
|
partitioning_boot_fs_partition_suffix: >-
|
||||||
|
{{
|
||||||
|
((partitioning_boot_partition_suffix | int) + 1)
|
||||||
|
if (partitioning_separate_boot | bool) else ''
|
||||||
|
}}
|
||||||
|
partitioning_root_partition_suffix: >-
|
||||||
|
{{
|
||||||
|
(partitioning_main_partition_suffix | int)
|
||||||
|
+ (1 if (partitioning_separate_boot | bool) else 0)
|
||||||
|
}}
|
||||||
|
partitioning_efi_mountpoint: >-
|
||||||
|
{{
|
||||||
|
'/boot/efi'
|
||||||
|
if (partitioning_separate_boot | bool)
|
||||||
|
else (
|
||||||
|
'/boot/efi'
|
||||||
|
if is_rhel or (os in ['ubuntu', 'ubuntu-lts'] or (os == 'debian' and (os_version | string) in ['11', '12', '13']))
|
||||||
|
else '/boot'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
partitioning_boot_end_mib: "{{ (partitioning_efi_end_mib | int) + (partitioning_boot_size_mib | int) }}"
|
||||||
|
partitioning_reserved_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(partitioning_efi_size_mib | float)
|
||||||
|
+ ((partitioning_boot_size_mib | float) if (partitioning_separate_boot | bool) else 0)
|
||||||
|
) / 1024
|
||||||
|
}}
|
||||||
|
partitioning_layout: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'number': 1,
|
||||||
|
'part_start': (partitioning_efi_start_mib | string) + 'MiB',
|
||||||
|
'part_end': (partitioning_efi_end_mib | string) + 'MiB',
|
||||||
|
'name': 'efi',
|
||||||
|
'flags': ['boot', 'esp']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'number': 2,
|
||||||
|
'part_start': (partitioning_efi_end_mib | string) + 'MiB',
|
||||||
|
'part_end': (partitioning_boot_end_mib | string) + 'MiB',
|
||||||
|
'name': 'boot'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'number': 3,
|
||||||
|
'part_start': (partitioning_boot_end_mib | string) + 'MiB',
|
||||||
|
'name': 'root'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if partitioning_separate_boot | bool else
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'number': 1,
|
||||||
|
'part_start': (partitioning_efi_start_mib | string) + 'MiB',
|
||||||
|
'part_end': (partitioning_efi_end_mib | string) + 'MiB',
|
||||||
|
'name': 'boot',
|
||||||
|
'flags': ['boot', 'esp']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'number': 2,
|
||||||
|
'part_start': (partitioning_efi_end_mib | string) + 'MiB',
|
||||||
|
'name': 'root'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
partitioning_grub_enable_cryptodisk: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.luks.enabled | bool)
|
||||||
|
and not (partitioning_separate_boot | bool)
|
||||||
|
and (partitioning_efi_mountpoint == '/boot/efi')
|
||||||
|
}}
|
||||||
|
partitioning_luks_device: "{{ install_drive ~ (partitioning_root_partition_suffix | string) }}"
|
||||||
|
partitioning_root_device: >-
|
||||||
|
{{
|
||||||
|
'/dev/mapper/' + system_cfg.luks.mapper
|
||||||
|
if (system_cfg.luks.enabled | bool)
|
||||||
|
else install_drive ~ (partitioning_root_partition_suffix | string)
|
||||||
|
}}
|
||||||
|
partitioning_disk_size_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
partitioning_vm_size
|
||||||
|
if (partitioning_vm_size is defined and (partitioning_vm_size | float) > 0)
|
||||||
|
else (
|
||||||
|
(
|
||||||
|
(system_cfg.disks | default([]) | first | default({})).size
|
||||||
|
if system_cfg is defined
|
||||||
|
else 0
|
||||||
|
) | default(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| float
|
||||||
|
}}
|
||||||
|
partitioning_memory_mb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
partitioning_vm_memory
|
||||||
|
if (partitioning_vm_memory is defined and (partitioning_vm_memory | float) > 0)
|
||||||
|
else (
|
||||||
|
(system_cfg.memory if system_cfg is defined else 0)
|
||||||
|
| default(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| float
|
||||||
|
}}
|
||||||
|
partitioning_swap_size_gb: >-
|
||||||
|
{{
|
||||||
|
((partitioning_memory_mb / 1024) >= 16.0)
|
||||||
|
| ternary(
|
||||||
|
(partitioning_memory_mb / 2048) | int,
|
||||||
|
[partitioning_memory_mb / 1024, 4.0] | max | int
|
||||||
|
)
|
||||||
|
}}
|
||||||
@@ -3,46 +3,78 @@
|
|||||||
block:
|
block:
|
||||||
- name: Create btrfs filesystem in main volume
|
- name: Create btrfs filesystem in main volume
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: "{{ install_drive }}{{ main_partition_suffix }}"
|
dev: "{{ partitioning_root_device }}"
|
||||||
fstype: btrfs
|
fstype: btrfs
|
||||||
force: true
|
force: true
|
||||||
|
opts: >-
|
||||||
|
{{
|
||||||
|
'-K'
|
||||||
|
if (system_cfg.luks.enabled | bool)
|
||||||
|
and not ('discard' in (system_cfg.luks.options | lower))
|
||||||
|
else omit
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Prepare BTRFS Subvolume
|
- name: Prepare BTRFS Subvolume
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: /mnt
|
path: /mnt
|
||||||
src: "{{ install_drive }}{{ main_partition_suffix }}"
|
src: "{{ partitioning_root_device }}"
|
||||||
fstype: btrfs
|
fstype: btrfs
|
||||||
opts: rw,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async
|
opts: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
'rw',
|
||||||
|
'relatime',
|
||||||
|
partitioning_btrfs_compress_opt,
|
||||||
|
'ssd',
|
||||||
|
'space_cache=v2',
|
||||||
|
'discard=async'
|
||||||
|
]
|
||||||
|
| reject('equalto', '')
|
||||||
|
| join(',')
|
||||||
|
}}
|
||||||
state: mounted
|
state: mounted
|
||||||
|
|
||||||
- name: Enable quotas on Btrfs filesystem
|
- name: Enable quotas on Btrfs filesystem
|
||||||
ansible.builtin.command: btrfs quota enable /mnt
|
ansible.builtin.command: btrfs quota enable /mnt
|
||||||
changed_when: result.rc == 0
|
register: partitioning_btrfs_quota_result
|
||||||
register: result
|
changed_when: false
|
||||||
|
|
||||||
- name: Make root subvolumes
|
- name: Make root subvolumes
|
||||||
when: cis | bool or item.subvol not in ['var_log', 'var_log_audit']
|
when:
|
||||||
|
- system_cfg.features.cis.enabled or item.subvol not in ['var_log_audit']
|
||||||
|
- system_cfg.features.swap.enabled | bool or item.subvol != 'swap'
|
||||||
ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||||
changed_when: result.rc == 0
|
args:
|
||||||
register: result
|
creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||||
loop:
|
loop:
|
||||||
- { subvol: root }
|
- { subvol: root }
|
||||||
|
- { subvol: swap }
|
||||||
- { subvol: home }
|
- { subvol: home }
|
||||||
- { subvol: var }
|
- { subvol: var }
|
||||||
|
- { subvol: pkg }
|
||||||
- { subvol: var_log }
|
- { subvol: var_log }
|
||||||
- { subvol: var_log_audit }
|
- { subvol: var_log_audit }
|
||||||
|
register: partitioning_btrfs_subvol_result
|
||||||
|
|
||||||
- name: Set quotas for subvolumes
|
- name: Set quotas for subvolumes
|
||||||
when: cis | bool or item.subvol not in ['var_log', 'var_log_audit']
|
when: system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
loop:
|
loop:
|
||||||
- { subvol: home, quota: 2G }
|
- { subvol: home, quota: 2G }
|
||||||
|
register: partitioning_btrfs_qgroup_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create a Btrfs swap file
|
||||||
|
when: system_cfg.features.swap.enabled | bool
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
btrfs filesystem mkswapfile --size {{ partitioning_swap_size_gb }}g --uuid clear /mnt/@swap/swapfile
|
||||||
|
args:
|
||||||
|
creates: /mnt/@swap/swapfile
|
||||||
|
register: partitioning_btrfs_swap_result
|
||||||
|
|
||||||
- name: Unmount Partition
|
- name: Unmount Partition
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: /mnt
|
path: /mnt
|
||||||
src: "{{ install_drive }}{{ main_partition_suffix }}"
|
src: "{{ partitioning_root_device }}"
|
||||||
fstype: btrfs
|
fstype: btrfs
|
||||||
state: unmounted
|
state: unmounted
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Create and format ext4 logical volumes
|
- name: Create and format ext4 logical volumes
|
||||||
when: cis | bool or item.lv not in ['var_log', 'var_log_audit']
|
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: /dev/sys/{{ item.lv }}
|
dev: /dev/sys/{{ item.lv }}
|
||||||
fstype: ext4
|
fstype: ext4
|
||||||
@@ -13,13 +13,15 @@
|
|||||||
- { lv: var_log_audit }
|
- { lv: var_log_audit }
|
||||||
|
|
||||||
- name: Remove Unsupported features for older Systems
|
- name: Remove Unsupported features for older Systems
|
||||||
when: (os | lower in ['almalinux', 'debian11', 'rhel8', 'rhel9', 'rocky']) and (cis | bool or item.lv not in ['var_log', 'var_log_audit'])
|
when: >
|
||||||
|
(os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11'))
|
||||||
|
and (system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'])
|
||||||
ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/sys/{{ item.lv }}"
|
ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/sys/{{ item.lv }}"
|
||||||
changed_when: result.rc == 0
|
|
||||||
register: result
|
|
||||||
loop:
|
loop:
|
||||||
- { lv: root }
|
- { lv: root }
|
||||||
- { lv: home }
|
- { lv: home }
|
||||||
- { lv: var }
|
- { lv: var }
|
||||||
- { lv: var_log }
|
- { lv: var_log }
|
||||||
- { lv: var_log_audit }
|
- { lv: var_log_audit }
|
||||||
|
register: partitioning_ext4_tune_result
|
||||||
|
changed_when: partitioning_ext4_tune_result.rc == 0
|
||||||
|
|||||||
85
roles/partitioning/tasks/extra_disks.yml
Normal file
85
roles/partitioning/tasks/extra_disks.yml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
- name: Determine additional disks to auto-mount
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
partitioning_extra_disks: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.disks | default([]))[1:]
|
||||||
|
| selectattr('mount.path')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Validate additional disks do not target install_drive
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.device is defined
|
||||||
|
- item.device | string | length > 0
|
||||||
|
- item.device != install_drive
|
||||||
|
- item.partition is defined
|
||||||
|
- item.partition | string | length > 0
|
||||||
|
- item.mount.fstype is defined
|
||||||
|
- item.mount.fstype in ['btrfs', 'ext4', 'xfs']
|
||||||
|
- item.mount.path is defined
|
||||||
|
- item.mount.path | string | length > 0
|
||||||
|
- item.mount.path.startswith('/')
|
||||||
|
- item.mount.path != '/'
|
||||||
|
fail_msg: "Invalid additional disk definition: {{ item | to_json }}"
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
- name: Partition additional disks
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
community.general.parted:
|
||||||
|
device: "{{ item.device }}"
|
||||||
|
label: gpt
|
||||||
|
number: 1
|
||||||
|
part_start: "1MiB"
|
||||||
|
part_end: "100%"
|
||||||
|
name: "{{ (item.mount.label | default('') | string | length > 0) | ternary(item.mount.label, 'data') }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.device }}"
|
||||||
|
|
||||||
|
- name: Settle partition tables for additional disks
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.builtin.command: udevadm settle
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create filesystems on additional disks
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
community.general.filesystem:
|
||||||
|
dev: "{{ item.partition }}"
|
||||||
|
fstype: "{{ item.mount.fstype }}"
|
||||||
|
opts: "{{ ('-L ' ~ item.mount.label) if (item.mount.label | default('') | string | length) > 0 else omit }}"
|
||||||
|
force: true
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.partition }}"
|
||||||
|
|
||||||
|
- name: Ensure mount directories exist for additional disks
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/mnt{{ item.mount.path }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.mount.path }}"
|
||||||
|
|
||||||
|
- name: Mount additional disks for fstab generation
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: "/mnt{{ item.mount.path }}"
|
||||||
|
src: "{{ item.partition }}"
|
||||||
|
fstype: "{{ item.mount.fstype }}"
|
||||||
|
opts: "{{ item.mount.opts | default('defaults') }}"
|
||||||
|
state: mounted
|
||||||
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.mount.path }}"
|
||||||
@@ -1,142 +1,665 @@
|
|||||||
---
|
---
|
||||||
|
- name: Detect system memory for swap sizing
|
||||||
|
when:
|
||||||
|
- system_cfg.features.swap.enabled | bool
|
||||||
|
- partitioning_vm_memory is not defined or (partitioning_vm_memory | float) <= 0
|
||||||
|
- system_cfg is not defined or (system_cfg.memory | default(0) | float) <= 0
|
||||||
|
block:
|
||||||
|
- name: Read system memory
|
||||||
|
ansible.builtin.command: awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo
|
||||||
|
register: partitioning_memtotal_mb
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Set partitioning vm memory default
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
partitioning_vm_memory: "{{ (partitioning_memtotal_mb.stdout | default('4096') | int) | float }}"
|
||||||
|
|
||||||
|
- name: Set partitioning vm_size for physical installs
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "physical"
|
||||||
|
- partitioning_vm_size is not defined or (partitioning_vm_size | float) <= 0
|
||||||
|
- install_drive | length > 0
|
||||||
|
block:
|
||||||
|
- name: Detect install drive size
|
||||||
|
ansible.builtin.command: "lsblk -b -dn -o SIZE {{ install_drive }}"
|
||||||
|
register: partitioning_disk_size_bytes
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set partitioning vm_size from install drive size
|
||||||
|
when:
|
||||||
|
- partitioning_disk_size_bytes.stdout is defined
|
||||||
|
- (partitioning_disk_size_bytes.stdout | trim | length) > 0
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
partitioning_vm_size: >-
|
||||||
|
{{
|
||||||
|
(partitioning_disk_size_bytes.stdout | trim | int / 1024 / 1024 / 1024)
|
||||||
|
| round(2, 'floor')
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Partition install drive
|
- name: Partition install drive
|
||||||
block:
|
block:
|
||||||
- name: Prepare partitions
|
- name: Prepare partitions
|
||||||
failed_when: false
|
block:
|
||||||
ansible.builtin.command: "{{ item.cmd }}"
|
- name: Disable swap
|
||||||
changed_when: result.rc == 0
|
ansible.builtin.command: swapoff -a
|
||||||
register: result
|
register: partitioning_swapoff_result
|
||||||
loop:
|
changed_when: partitioning_swapoff_result.rc == 0
|
||||||
- { cmd: umount -l /mnt }
|
failed_when: false
|
||||||
- { cmd: vgremove -f sys }
|
|
||||||
- { cmd: 'find /dev -wholename "{{ install_drive }}*" -exec wipefs --force --all {} \;' }
|
- name: Find mounts under /mnt
|
||||||
loop_control:
|
ansible.builtin.command: findmnt -R /mnt -n -o TARGET
|
||||||
label: "{{ item.cmd }}"
|
register: partitioning_mounted_paths
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Unmount /mnt mounts
|
||||||
|
when: partitioning_mounted_paths.stdout_lines | length > 0
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: unmounted
|
||||||
|
loop: "{{ partitioning_mounted_paths.stdout_lines | reverse }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Remove LVM volume group
|
||||||
|
community.general.lvg:
|
||||||
|
vg: sys
|
||||||
|
state: absent
|
||||||
|
force: true
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Close LUKS mapper
|
||||||
|
when: system_cfg.luks.enabled | bool
|
||||||
|
community.crypto.luks_device:
|
||||||
|
name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
state: closed
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Remove LUKS mapper device
|
||||||
|
when: system_cfg.luks.enabled | bool
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dmsetup remove --force --retry {{ system_cfg.luks.mapper }}
|
||||||
|
register: partitioning_dmsetup_remove
|
||||||
|
changed_when: partitioning_dmsetup_remove.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Remove LUKS signatures
|
||||||
|
when: system_cfg.luks.enabled | bool
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ partitioning_luks_device }}"
|
||||||
|
state: absent
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Wipe filesystem signatures
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
find /dev -wholename "{{ install_drive }}*" -exec wipefs --force --all {} \;
|
||||||
|
register: partitioning_wipefs_result
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Refresh kernel partition table
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "partprobe {{ install_drive }}"
|
||||||
|
- "blockdev --rereadpt {{ install_drive }}"
|
||||||
|
- "udevadm settle"
|
||||||
|
register: partitioning_partprobe_result
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
- name: Define partitions
|
- name: Define partitions
|
||||||
community.general.parted:
|
block:
|
||||||
device: "{{ install_drive }}"
|
- name: Create partition layout
|
||||||
label: gpt
|
community.general.parted:
|
||||||
number: "{{ item.number }}"
|
device: "{{ install_drive }}"
|
||||||
part_end: "{{ item.part_end | default(omit) }}"
|
label: gpt
|
||||||
part_start: "{{ item.part_start | default(omit) }}"
|
number: "{{ item.number }}"
|
||||||
name: "{{ item.name }}"
|
part_end: "{{ item.part_end | default(omit) }}"
|
||||||
flags: "{{ item.flags | default(omit) }}"
|
part_start: "{{ item.part_start | default(omit) }}"
|
||||||
state: present
|
name: "{{ item.name }}"
|
||||||
|
flags: "{{ item.flags | default(omit) }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ partitioning_layout }}"
|
||||||
|
rescue:
|
||||||
|
- name: Refresh kernel partition table after failure
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "partprobe {{ install_drive }}"
|
||||||
|
- "blockdev --rereadpt {{ install_drive }}"
|
||||||
|
- "udevadm settle"
|
||||||
|
register: partitioning_partprobe_retry
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Retry partition layout
|
||||||
|
community.general.parted:
|
||||||
|
device: "{{ install_drive }}"
|
||||||
|
label: gpt
|
||||||
|
number: "{{ item.number }}"
|
||||||
|
part_end: "{{ item.part_end | default(omit) }}"
|
||||||
|
part_start: "{{ item.part_start | default(omit) }}"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
flags: "{{ item.flags | default(omit) }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ partitioning_layout }}"
|
||||||
|
|
||||||
|
- name: Settle partition table
|
||||||
|
ansible.builtin.command: "{{ item }}"
|
||||||
loop:
|
loop:
|
||||||
- { number: 1, part_end: 500MiB, name: boot, flags: [boot, esp] }
|
- "partprobe {{ install_drive }}"
|
||||||
- { number: 2, part_start: 500MiB, name: root }
|
- "udevadm settle"
|
||||||
|
register: partitioning_partprobe_settle
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Configure LUKS encryption
|
||||||
|
when: system_cfg.luks.enabled | bool
|
||||||
|
block:
|
||||||
|
- name: Validate LUKS passphrase
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (system_cfg.luks.passphrase | string | length) > 0
|
||||||
|
fail_msg: system.luks.passphrase must be set when LUKS is enabled.
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Ensure LUKS container exists
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ partitioning_luks_device }}"
|
||||||
|
state: present
|
||||||
|
type: "{{ system_cfg.luks.type }}"
|
||||||
|
cipher: "{{ system_cfg.luks.cipher }}"
|
||||||
|
hash: "{{ system_cfg.luks.hash }}"
|
||||||
|
keysize: "{{ system_cfg.luks.bits }}"
|
||||||
|
pbkdf:
|
||||||
|
algorithm: "{{ system_cfg.luks.pbkdf }}"
|
||||||
|
iteration_time: "{{ (system_cfg.luks.iter | float) / 1000 }}"
|
||||||
|
passphrase: "{{ system_cfg.luks.passphrase | string }}"
|
||||||
|
register: partitioning_luks_format_result
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Force-close LUKS mapper
|
||||||
|
community.crypto.luks_device:
|
||||||
|
name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
state: closed
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Force-remove LUKS mapper device
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dmsetup remove --force --retry {{ system_cfg.luks.mapper }}
|
||||||
|
register: partitioning_dmsetup_remove_after_format
|
||||||
|
changed_when: partitioning_dmsetup_remove_after_format.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Settle udev after removing LUKS mapper
|
||||||
|
ansible.builtin.command: udevadm settle
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Ensure LUKS mapper is opened
|
||||||
|
block:
|
||||||
|
- name: Open LUKS device
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ partitioning_luks_device }}"
|
||||||
|
state: opened
|
||||||
|
name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
passphrase: "{{ system_cfg.luks.passphrase | string }}"
|
||||||
|
allow_discards: "{{ 'discard' in (system_cfg.luks.options | lower) }}"
|
||||||
|
register: partitioning_luks_open_result
|
||||||
|
no_log: true
|
||||||
|
rescue:
|
||||||
|
- name: Force-close stale LUKS mapper
|
||||||
|
community.crypto.luks_device:
|
||||||
|
name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
state: closed
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Force-remove stale LUKS mapper device
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
dmsetup remove --force --retry {{ system_cfg.luks.mapper }}
|
||||||
|
register: partitioning_dmsetup_remove_retry
|
||||||
|
changed_when: partitioning_dmsetup_remove_retry.rc == 0
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Settle udev after removing stale LUKS mapper
|
||||||
|
ansible.builtin.command: udevadm settle
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Retry opening LUKS device
|
||||||
|
community.crypto.luks_device:
|
||||||
|
device: "{{ partitioning_luks_device }}"
|
||||||
|
state: opened
|
||||||
|
name: "{{ system_cfg.luks.mapper }}"
|
||||||
|
passphrase: "{{ system_cfg.luks.passphrase | string }}"
|
||||||
|
allow_discards: "{{ 'discard' in (system_cfg.luks.options | lower) }}"
|
||||||
|
register: partitioning_luks_open_retry
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Get LUKS UUID
|
||||||
|
ansible.builtin.command: "cryptsetup luksUUID {{ partitioning_luks_device }}"
|
||||||
|
register: partitioning_luks_uuid_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Store LUKS UUID
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
partitioning_luks_uuid: "{{ partitioning_luks_uuid_result.stdout | trim }}"
|
||||||
|
|
||||||
- name: Create LVM logical volumes
|
- name: Create LVM logical volumes
|
||||||
when: filesystem != 'btrfs'
|
when: system_cfg.filesystem != 'btrfs'
|
||||||
block:
|
block:
|
||||||
- name: Create LVM volume group
|
- name: Create LVM volume group
|
||||||
community.general.lvg:
|
community.general.lvg:
|
||||||
vg: sys
|
vg: sys
|
||||||
pvs: "{{ install_drive }}{{ main_partition_suffix }}"
|
pvs: "{{ partitioning_root_device }}"
|
||||||
|
|
||||||
- name: Create LVM logical volumes
|
- name: Create LVM logical volumes
|
||||||
when: cis | bool or item.lv not in ['var_log', 'var_log_audit']
|
when:
|
||||||
|
- system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||||
|
- system_cfg.features.swap.enabled | bool or item.lv != 'swap'
|
||||||
|
vars:
|
||||||
|
partitioning_lvm_extent_reserve_count: 10
|
||||||
|
partitioning_lvm_extent_size_mib: 4
|
||||||
|
partitioning_lvm_extent_reserve_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(partitioning_lvm_extent_reserve_count | float)
|
||||||
|
* (partitioning_lvm_extent_size_mib | float)
|
||||||
|
/ 1024
|
||||||
|
) | round(2, 'ceil')
|
||||||
|
}}
|
||||||
|
partitioning_lvm_swap_target_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(partitioning_memory_mb | float / 1024),
|
||||||
|
4
|
||||||
|
] | max | float
|
||||||
|
)
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else 0
|
||||||
|
}}
|
||||||
|
partitioning_lvm_swap_cap_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
4
|
||||||
|
+ [
|
||||||
|
(partitioning_disk_size_gb | float) - 20,
|
||||||
|
0
|
||||||
|
] | max
|
||||||
|
)
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else 0
|
||||||
|
}}
|
||||||
|
partitioning_lvm_swap_target_limited_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
[
|
||||||
|
partitioning_lvm_swap_target_gb,
|
||||||
|
partitioning_lvm_swap_cap_gb
|
||||||
|
] | min
|
||||||
|
)
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else 0
|
||||||
|
}}
|
||||||
|
partitioning_lvm_swap_max_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
(partitioning_disk_size_gb | float)
|
||||||
|
- (partitioning_reserved_gb | float)
|
||||||
|
- (system_cfg.features.cis.enabled | ternary(7.5, 0))
|
||||||
|
- partitioning_lvm_extent_reserve_gb
|
||||||
|
- 4
|
||||||
|
),
|
||||||
|
0
|
||||||
|
] | max
|
||||||
|
)
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else 0
|
||||||
|
}}
|
||||||
|
partitioning_lvm_available_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(partitioning_disk_size_gb | float)
|
||||||
|
- (partitioning_reserved_gb | float)
|
||||||
|
- (system_cfg.features.cis.enabled | ternary(7.5, 0))
|
||||||
|
- partitioning_lvm_extent_reserve_gb
|
||||||
|
- partitioning_lvm_swap_target_limited_gb
|
||||||
|
) | float
|
||||||
|
}}
|
||||||
|
partitioning_lvm_home_gb: >-
|
||||||
|
{{
|
||||||
|
([([(((partitioning_disk_size_gb | float) - 20) * 0.1), 2] | max), 20] | min)
|
||||||
|
}}
|
||||||
|
partitioning_lvm_root_default_gb: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
(
|
||||||
|
((partitioning_lvm_available_gb | float) < 4)
|
||||||
|
| ternary(
|
||||||
|
4,
|
||||||
|
(
|
||||||
|
((partitioning_lvm_available_gb | float) > 12)
|
||||||
|
| ternary(
|
||||||
|
((partitioning_disk_size_gb | float) * 0.4)
|
||||||
|
| round(0, 'ceil'),
|
||||||
|
partitioning_lvm_available_gb
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
4
|
||||||
|
] | max
|
||||||
|
}}
|
||||||
|
partitioning_lvm_swap_gb: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
[
|
||||||
|
partitioning_lvm_swap_target_limited_gb,
|
||||||
|
partitioning_lvm_swap_max_gb
|
||||||
|
] | min | round(2, 'floor')
|
||||||
|
)
|
||||||
|
if system_cfg.features.swap.enabled | bool
|
||||||
|
else 0
|
||||||
|
}}
|
||||||
|
partitioning_lvm_root_full_gb: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
(
|
||||||
|
(partitioning_disk_size_gb | float)
|
||||||
|
- (partitioning_reserved_gb | float)
|
||||||
|
- (partitioning_lvm_swap_gb | float)
|
||||||
|
- partitioning_lvm_extent_reserve_gb
|
||||||
|
- (
|
||||||
|
(partitioning_lvm_home_gb | float) + 5.5
|
||||||
|
if system_cfg.features.cis.enabled
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
4
|
||||||
|
] | max | round(2, 'floor')
|
||||||
|
}}
|
||||||
|
partitioning_lvm_root_gb: >-
|
||||||
|
{{
|
||||||
|
partitioning_lvm_root_full_gb
|
||||||
|
if partitioning_use_full_disk | bool
|
||||||
|
else partitioning_lvm_root_default_gb
|
||||||
|
}}
|
||||||
community.general.lvol:
|
community.general.lvol:
|
||||||
vg: sys
|
vg: sys
|
||||||
lv: "{{ item.lv }}"
|
lv: "{{ item.lv }}"
|
||||||
size: "{{ item.size }}"
|
size: "{{ item.size }}"
|
||||||
state: present
|
state: present
|
||||||
loop:
|
loop:
|
||||||
- { lv: root, size: 12G }
|
- lv: root
|
||||||
- { lv: home, size: 2G }
|
size: "{{ partitioning_lvm_root_gb | string + 'G' }}"
|
||||||
- { lv: var, size: 2G }
|
- lv: swap
|
||||||
- { lv: var_log, size: 2G }
|
size: "{{ partitioning_lvm_swap_gb | string + 'G' }}"
|
||||||
- { lv: var_log_audit, size: 1.5G }
|
- lv: home
|
||||||
|
size: "{{ partitioning_lvm_home_gb | string + 'G' }}"
|
||||||
|
- { lv: var, size: "2G" }
|
||||||
|
- { lv: var_log, size: "2G" }
|
||||||
|
- { lv: var_log_audit, size: "1.5G" }
|
||||||
|
|
||||||
- name: Create filesystems
|
- name: Create filesystems
|
||||||
block:
|
block:
|
||||||
- name: Create FAT32 filesystem in boot partition
|
- name: Create FAT32 filesystem in boot partition
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: "{{ install_drive }}{{ boot_partition_suffix }}"
|
dev: "{{ install_drive }}{{ partitioning_boot_partition_suffix }}"
|
||||||
fstype: vfat
|
fstype: vfat
|
||||||
opts: -F32 -n BOOT
|
opts: -F32 -n BOOT
|
||||||
force: true
|
force: true
|
||||||
|
|
||||||
|
- name: Create filesystem for /boot partition
|
||||||
|
when: partitioning_separate_boot | bool
|
||||||
|
community.general.filesystem:
|
||||||
|
dev: "{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}"
|
||||||
|
fstype: "{{ partitioning_boot_fs_fstype }}"
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Remove unsupported ext4 features from /boot
|
||||||
|
when:
|
||||||
|
- partitioning_separate_boot | bool
|
||||||
|
- partitioning_boot_fs_fstype == 'ext4'
|
||||||
|
- os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11')
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
tune2fs -O "^orphan_file,^metadata_csum_seed"
|
||||||
|
"{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}"
|
||||||
|
register: partitioning_boot_ext4_tune_result
|
||||||
|
changed_when: partitioning_boot_ext4_tune_result.rc == 0
|
||||||
|
|
||||||
|
- name: Create swap filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.swap.enabled | bool
|
||||||
|
community.general.filesystem:
|
||||||
|
fstype: swap
|
||||||
|
dev: /dev/sys/swap
|
||||||
|
|
||||||
- name: Create filesystem
|
- name: Create filesystem
|
||||||
ansible.builtin.include_tasks: "{{ filesystem }}.yml"
|
ansible.builtin.include_tasks: "{{ system_cfg.filesystem }}.yml"
|
||||||
|
|
||||||
- name: Get UUID for boot filesystem
|
- name: Get UUID for boot filesystem
|
||||||
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ boot_partition_suffix }}'
|
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_partition_suffix }}'
|
||||||
|
register: partitioning_boot_uuid
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for /boot filesystem
|
||||||
|
when: partitioning_separate_boot | bool
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}'
|
||||||
|
register: partitioning_boot_fs_uuid
|
||||||
changed_when: false
|
changed_when: false
|
||||||
register: boot_uuid
|
|
||||||
|
|
||||||
- name: Get UUID for main filesystem
|
- name: Get UUID for main filesystem
|
||||||
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ main_partition_suffix }}'
|
ansible.builtin.command: blkid -s UUID -o value '{{ partitioning_root_device }}'
|
||||||
|
register: partitioning_main_uuid
|
||||||
changed_when: false
|
changed_when: false
|
||||||
register: main_uuid
|
|
||||||
|
|
||||||
- name: Get UUIDs for LVM filesystems
|
- name: Get UUID for LVM root filesystem
|
||||||
when: filesystem != 'btrfs' and (cis | bool or item not in ['var_log', 'var_log_audit'])
|
when: system_cfg.filesystem != 'btrfs'
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/{{ item }}
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/root
|
||||||
|
register: partitioning_uuid_root_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for LVM swap filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.swap.enabled | bool
|
||||||
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/swap
|
||||||
|
register: partitioning_uuid_swap_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for LVM home filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.cis.enabled
|
||||||
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/home
|
||||||
|
register: partitioning_uuid_home_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for LVM var filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.cis.enabled
|
||||||
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var
|
||||||
|
register: partitioning_uuid_var_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for LVM var_log filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.cis.enabled
|
||||||
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log
|
||||||
|
register: partitioning_uuid_var_log_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get UUID for LVM var_log_audit filesystem
|
||||||
|
when:
|
||||||
|
- system_cfg.filesystem != 'btrfs'
|
||||||
|
- system_cfg.features.cis.enabled
|
||||||
|
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log_audit
|
||||||
|
register: partitioning_uuid_var_log_audit_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
register: uuid_result
|
|
||||||
loop:
|
|
||||||
- root
|
|
||||||
- home
|
|
||||||
- var
|
|
||||||
- var_log
|
|
||||||
- var_log_audit
|
|
||||||
|
|
||||||
- name: Assign UUIDs to Variables
|
- name: Assign UUIDs to Variables
|
||||||
|
when: system_cfg.filesystem != 'btrfs'
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
uuid_root: "{{ uuid_result.results[0].stdout_lines }}"
|
partitioning_uuid_root: "{{ partitioning_uuid_root_result.stdout_lines | default([]) }}"
|
||||||
uuid_home: "{{ uuid_result.results[1].stdout_lines }}"
|
partitioning_uuid_swap: >-
|
||||||
uuid_var: "{{ uuid_result.results[2].stdout_lines }}"
|
{{
|
||||||
uuid_var_log: "{{ uuid_result.results[3].stdout_lines if cis == true else '' }}"
|
partitioning_uuid_swap_result.stdout_lines | default([])
|
||||||
uuid_var_log_audit: "{{ uuid_result.results[4].stdout_lines if cis == true else '' }}"
|
if system_cfg.features.swap.enabled | bool
|
||||||
when: filesystem != 'btrfs'
|
else ''
|
||||||
|
}}
|
||||||
|
partitioning_uuid_home: >-
|
||||||
|
{{
|
||||||
|
partitioning_uuid_home_result.stdout_lines | default([])
|
||||||
|
if system_cfg.features.cis.enabled
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
partitioning_uuid_var: >-
|
||||||
|
{{
|
||||||
|
partitioning_uuid_var_result.stdout_lines | default([])
|
||||||
|
if system_cfg.features.cis.enabled
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
partitioning_uuid_var_log: >-
|
||||||
|
{{
|
||||||
|
partitioning_uuid_var_log_result.stdout_lines | default([])
|
||||||
|
if system_cfg.features.cis.enabled
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
partitioning_uuid_var_log_audit: >-
|
||||||
|
{{
|
||||||
|
partitioning_uuid_var_log_audit_result.stdout_lines | default([])
|
||||||
|
if system_cfg.features.cis.enabled
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Mount filesystems
|
- name: Mount filesystems
|
||||||
block:
|
block:
|
||||||
- name: Mount filesystems and subvolumes
|
- name: Mount filesystems and subvolumes
|
||||||
when: cis | bool or (not cis and item.path != '/var/log' and item.path != '/var/log/audit')
|
when:
|
||||||
|
- >-
|
||||||
|
system_cfg.features.cis.enabled or (
|
||||||
|
not system_cfg.features.cis.enabled and (
|
||||||
|
(system_cfg.filesystem == 'btrfs' and item.path in ['/home', '/var/log', '/var/cache/pacman/pkg'])
|
||||||
|
or (item.path not in ['/home', '/var', '/var/log', '/var/log/audit', '/var/cache/pacman/pkg'])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
- >-
|
||||||
|
not (item.path in ['/swap', '/var/cache/pacman/pkg'] and system_cfg.filesystem != 'btrfs')
|
||||||
|
- system_cfg.features.swap.enabled | bool or item.path != '/swap'
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: /mnt{{ item.path }}
|
path: /mnt{{ item.path }}
|
||||||
src: "{{ 'UUID=' + (main_uuid.stdout if filesystem == 'btrfs' else item.uuid) }}"
|
src: "{{ 'UUID=' + (partitioning_main_uuid.stdout if system_cfg.filesystem == 'btrfs' else item.uuid) }}"
|
||||||
fstype: "{{ filesystem }}"
|
fstype: "{{ system_cfg.filesystem }}"
|
||||||
opts: "{{ item.opts }}"
|
opts: "{{ item.opts }}"
|
||||||
state: mounted
|
state: mounted
|
||||||
loop:
|
loop:
|
||||||
- path: ""
|
- path: ""
|
||||||
uuid: "{{ uuid_root[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_root[0] | default(omit) }}"
|
||||||
opts: "{{ 'defaults' if filesystem != 'btrfs' else 'rw,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@' }}"
|
opts: >-
|
||||||
|
{{
|
||||||
|
'defaults'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'relatime', partitioning_btrfs_compress_opt, 'ssd', 'space_cache=v2',
|
||||||
|
'discard=async', 'subvol=@'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
|
- path: /swap
|
||||||
|
opts: >-
|
||||||
|
{{
|
||||||
|
[
|
||||||
|
'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd',
|
||||||
|
'space_cache=v2', 'discard=async', 'subvol=@swap'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
- path: /home
|
- path: /home
|
||||||
uuid: "{{ uuid_home[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_home[0] | default(omit) }}"
|
||||||
opts: "{{ 'defaults,nosuid,nodev' if filesystem != 'btrfs'
|
opts: >-
|
||||||
else 'rw,nosuid,nodev,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@home' }}"
|
{{
|
||||||
|
'defaults,nosuid,nodev'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd',
|
||||||
|
'space_cache=v2', 'discard=async', 'subvol=@home'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
- path: /var
|
- path: /var
|
||||||
uuid: "{{ uuid_var[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_var[0] | default(omit) }}"
|
||||||
opts: "{{ 'defaults,nosuid,nodev' if filesystem != 'btrfs'
|
opts: >-
|
||||||
else 'rw,nosuid,nodev,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var' }}"
|
{{
|
||||||
|
'defaults,nosuid,nodev'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'nosuid', 'nodev', 'relatime', partitioning_btrfs_compress_opt, 'ssd',
|
||||||
|
'space_cache=v2', 'discard=async', 'subvol=@var'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
- path: /var/log
|
- path: /var/log
|
||||||
uuid: "{{ uuid_var_log[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_var_log[0] | default(omit) }}"
|
||||||
opts: "{{ 'defaults,nosuid,nodev,noexec' if filesystem != 'btrfs'
|
opts: >-
|
||||||
else 'rw,nosuid,nodev,noexec,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var_log' }}"
|
{{
|
||||||
|
'defaults,nosuid,nodev,noexec'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
|
||||||
|
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
|
- path: /var/cache/pacman/pkg
|
||||||
|
uuid: "{{ partitioning_uuid_root | default([]) | first | default(omit) }}"
|
||||||
|
opts: >-
|
||||||
|
{{
|
||||||
|
'defaults,nosuid,nodev,noexec'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
|
||||||
|
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@pkg'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
- path: /var/log/audit
|
- path: /var/log/audit
|
||||||
uuid: "{{ uuid_var_log_audit[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_var_log_audit[0] | default(omit) }}"
|
||||||
opts: "{{ 'defaults,nosuid,nodev,noexec' if filesystem != 'btrfs'
|
opts: >-
|
||||||
else 'rw,nosuid,nodev,noexec,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var_log_audit' }}"
|
{{
|
||||||
|
'defaults,nosuid,nodev,noexec'
|
||||||
|
if system_cfg.filesystem != 'btrfs'
|
||||||
|
else [
|
||||||
|
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', partitioning_btrfs_compress_opt,
|
||||||
|
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit'
|
||||||
|
] | reject('equalto', '') | join(',')
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Mount tmp and var_tmp filesystems
|
- name: Mount /boot filesystem
|
||||||
|
when: partitioning_separate_boot | bool
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: /mnt{{ item.path }}
|
path: /mnt/boot
|
||||||
src: tmpfs
|
src: "UUID={{ partitioning_boot_fs_uuid.stdout }}"
|
||||||
fstype: tmpfs
|
fstype: "{{ partitioning_boot_fs_fstype }}"
|
||||||
opts: defaults,nosuid,nodev,noexec
|
opts: defaults
|
||||||
state: mounted
|
state: mounted
|
||||||
loop:
|
|
||||||
- { path: /tmp }
|
|
||||||
- { path: /var/tmp }
|
|
||||||
|
|
||||||
- name: Mount boot filesystem
|
- name: Mount boot filesystem
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: "{{ '/mnt/boot/efi' if os | lower in ['rhel8', 'ubuntu', 'ubuntu-lts'] else '/mnt/boot' }}"
|
path: "/mnt{{ partitioning_efi_mountpoint }}"
|
||||||
src: UUID={{ boot_uuid.stdout }}
|
src: UUID={{ partitioning_boot_uuid.stdout }}
|
||||||
fstype: vfat
|
fstype: vfat
|
||||||
state: mounted
|
state: mounted
|
||||||
|
|
||||||
|
- name: Activate swap
|
||||||
|
when: system_cfg.features.swap.enabled | bool
|
||||||
|
vars:
|
||||||
|
partitioning_swap_cmd: >-
|
||||||
|
{{ 'swapon /mnt/swap/swapfile' if system_cfg.filesystem == 'btrfs' else 'swapon -U ' + partitioning_uuid_swap[0] }}
|
||||||
|
ansible.builtin.command: "{{ partitioning_swap_cmd }}"
|
||||||
|
register: partitioning_swap_activate_result
|
||||||
|
changed_when: partitioning_swap_activate_result.rc == 0
|
||||||
|
|
||||||
|
- name: Mount additional disks
|
||||||
|
ansible.builtin.include_tasks: extra_disks.yml
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Create and format XFS logical volumes
|
- name: Create and format XFS logical volumes
|
||||||
when: cis | bool or item.lv not in ['var_log', 'var_log_audit']
|
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: /dev/sys/{{ item.lv }}
|
dev: /dev/sys/{{ item.lv }}
|
||||||
fstype: xfs
|
fstype: xfs
|
||||||
|
|||||||
113
roles/system_check/tasks/main.yml
Normal file
113
roles/system_check/tasks/main.yml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
- name: VM existence protection check
|
||||||
|
when: system_cfg.type == "virtual"
|
||||||
|
block:
|
||||||
|
- name: Check if VM already exists on libvirt
|
||||||
|
when: hypervisor_type == "libvirt"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.libvirt.virt:
|
||||||
|
command: list_vms
|
||||||
|
register: system_check_libvirt_existing_vms
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Abort if VM already exists on libvirt
|
||||||
|
when: hypervisor_type == "libvirt"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- hostname not in system_check_libvirt_existing_vms.domains | default([])
|
||||||
|
fail_msg: |
|
||||||
|
VM {{ hostname }} already exists on libvirt hypervisor.
|
||||||
|
To avoid data loss, the playbook will not overwrite or delete existing VMs.
|
||||||
|
Please choose a different hostname or remove the existing VM manually before proceeding.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Check if VM already exists on Proxmox
|
||||||
|
when: hypervisor_type == "proxmox"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
community.proxmox.proxmox_vm_info:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
type: qemu
|
||||||
|
register: system_check_proxmox_check_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Abort if VM already exists on Proxmox
|
||||||
|
when: hypervisor_type == "proxmox"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_check_proxmox_check_result.proxmox_vms | default([]) | length == 0
|
||||||
|
fail_msg: |
|
||||||
|
VM {{ hostname }} (ID: {{ system_cfg.id }}) already exists on Proxmox hypervisor.
|
||||||
|
To avoid data loss, the playbook will not overwrite or delete existing VMs.
|
||||||
|
Please choose a different hostname or VM ID, or remove the existing VM manually before proceeding.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Check if VM already exists in vCenter
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
delegate_to: localhost
|
||||||
|
community.vmware.vmware_guest_info:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
folder: "{{ system_cfg.path if system_cfg.path | length > 0 else omit }}"
|
||||||
|
register: system_check_vmware_check_result
|
||||||
|
failed_when: false
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Fail if vCenter lookup failed unexpectedly
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- not system_check_vmware_check_result.failed
|
||||||
|
or (system_check_vmware_check_result.msg is search('non-existing VM'))
|
||||||
|
fail_msg: |
|
||||||
|
Unable to verify VM existence in vCenter.
|
||||||
|
{{ system_check_vmware_check_result.msg | default('Unknown error') }}
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Abort if VM already exists in vCenter
|
||||||
|
when: hypervisor_type == "vmware"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_check_vmware_check_result.instance is not defined
|
||||||
|
fail_msg: |
|
||||||
|
VM {{ hostname }} already exists in vCenter.
|
||||||
|
To avoid data loss, the playbook will not overwrite or delete existing VMs.
|
||||||
|
Please choose a different hostname or remove the existing VM manually before proceeding.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Check if VM already exists on Xen
|
||||||
|
when: hypervisor_type == "xen"
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- xl
|
||||||
|
- list
|
||||||
|
register: system_check_xen_existing_vms
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Abort if VM already exists on Xen
|
||||||
|
when: hypervisor_type == "xen"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- >-
|
||||||
|
not (
|
||||||
|
system_check_xen_existing_vms.stdout | default('')
|
||||||
|
is search('(?m)^' ~ (hostname | ansible.builtin.regex_escape) ~ '\\s+\\d+\\s')
|
||||||
|
)
|
||||||
|
fail_msg: |
|
||||||
|
VM {{ hostname }} already exists on Xen hypervisor.
|
||||||
|
To avoid data loss, the playbook will not overwrite or delete existing VMs.
|
||||||
|
Please choose a different hostname or remove the existing VM manually before proceeding.
|
||||||
|
quiet: true
|
||||||
24
roles/virtualization/defaults/main.yml
Normal file
24
roles/virtualization/defaults/main.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
virtualization_libvirt_image_dir: >-
|
||||||
|
{{
|
||||||
|
system_cfg.path
|
||||||
|
if system_cfg is defined and (system_cfg.path | string | length) > 0
|
||||||
|
else '/var/lib/libvirt/images'
|
||||||
|
}}
|
||||||
|
virtualization_libvirt_disk_path: >-
|
||||||
|
{{ [virtualization_libvirt_image_dir, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
|
||||||
|
virtualization_libvirt_cloudinit_path: >-
|
||||||
|
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||||
|
virtualization_mac_address: >-
|
||||||
|
{{ '52:54:00' | community.general.random_mac(seed=hostname) }}
|
||||||
|
virtualization_xen_disk_path: /var/lib/xen/images
|
||||||
|
|
||||||
|
virtualization_tpm2_enabled: >-
|
||||||
|
{{
|
||||||
|
(system_cfg.luks.enabled | bool)
|
||||||
|
and (system_cfg.luks.auto | bool)
|
||||||
|
and (
|
||||||
|
(system_cfg.luks.method | lower)
|
||||||
|
== 'tpm2'
|
||||||
|
)
|
||||||
|
}}
|
||||||
@@ -1,41 +1,65 @@
|
|||||||
---
|
---
|
||||||
- name: Check if VM disk exists
|
- name: Build disk definitions
|
||||||
delegate_to: localhost
|
ansible.builtin.set_fact:
|
||||||
ansible.builtin.stat:
|
virtualization_libvirt_disks: "{{ virtualization_libvirt_disks | default([]) + [virtualization_libvirt_disk_cfg] }}"
|
||||||
path: "{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}.qcow2"
|
vars:
|
||||||
register: vm_disk_stat
|
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
|
||||||
- name: Create VM disk
|
virtualization_libvirt_disk_cfg: >-
|
||||||
when: not vm_disk_stat.stat.exists
|
{{
|
||||||
delegate_to: localhost
|
{
|
||||||
ansible.builtin.command: qemu-img create -f qcow2 {{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}.qcow2 {{ vm_size }}G
|
'path': (
|
||||||
changed_when: result.rc == 0
|
virtualization_libvirt_disk_path
|
||||||
register: result
|
if ansible_loop.index0 == 0
|
||||||
|
else ([virtualization_libvirt_image_dir, hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'] | ansible.builtin.path_join)
|
||||||
- name: Generate Random MAC Address
|
),
|
||||||
delegate_to: localhost
|
'target': 'vd' ~ device_letter,
|
||||||
ansible.builtin.shell: set -o pipefail && openssl rand -hex 5 | sed 's/\(..\)/\1:/g; s/.$//' | sed 's/^/02:/'
|
'bus': 'virtio',
|
||||||
|
'format': 'qcow2',
|
||||||
|
'size': (item.size | float)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loop: "{{ system_cfg.disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
extended: true
|
||||||
changed_when: false
|
changed_when: false
|
||||||
register: mac_address_output
|
|
||||||
|
- name: Create VM disks
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- qemu-img
|
||||||
|
- create
|
||||||
|
- -f
|
||||||
|
- qcow2
|
||||||
|
- "{{ item.path }}"
|
||||||
|
- "{{ item.size }}G"
|
||||||
|
creates: "{{ item.path }}"
|
||||||
|
loop: "{{ virtualization_libvirt_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Render cloud config templates
|
- name: Render cloud config templates
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: "{{ item.src }}"
|
src: "{{ item.src }}"
|
||||||
dest: /tmp/{{ item.dest_prefix }}-{{ hostname }}.yml
|
dest: /tmp/{{ item.dest_prefix }}-{{ hostname }}.yml
|
||||||
mode: '0644'
|
mode: "0644"
|
||||||
loop:
|
loop:
|
||||||
- { src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data }
|
- { src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data }
|
||||||
- { src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config }
|
- { src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config }
|
||||||
|
|
||||||
- name: Create cloud-init disk
|
- name: Create cloud-init disk
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command:
|
||||||
cloud-localds {{ vm_path | default('/var/lib/libvirt/images/') }}/{{ hostname }}-cloudinit.iso
|
argv:
|
||||||
/tmp/cloud-user-data-{{ hostname }}.yml
|
- cloud-localds
|
||||||
-N /tmp/cloud-network-config-{{ hostname }}.yml
|
- "{{ virtualization_libvirt_cloudinit_path }}"
|
||||||
changed_when: result.rc == 0
|
- "/tmp/cloud-user-data-{{ hostname }}.yml"
|
||||||
register: result
|
- -N
|
||||||
|
- "/tmp/cloud-network-config-{{ hostname }}.yml"
|
||||||
|
creates: "{{ virtualization_libvirt_cloudinit_path }}"
|
||||||
|
|
||||||
- name: Create VM using libvirt
|
- name: Create VM using libvirt
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
@@ -48,3 +72,9 @@
|
|||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: running
|
state: running
|
||||||
|
register: virtualization_libvirt_start_result
|
||||||
|
|
||||||
|
- name: Set VM created fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_vm_created_in_run: true
|
||||||
|
when: virtualization_libvirt_start_result is defined and virtualization_libvirt_start_result.changed | bool
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
---
|
---
|
||||||
- name: Create Virtual Machine
|
- name: Create Virtual Machine
|
||||||
ansible.builtin.include_tasks: "{{ hypervisor }}.yml"
|
ansible.builtin.include_tasks: "{{ hypervisor_type }}.yml"
|
||||||
|
|||||||
@@ -1,51 +1,80 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy VM on Proxmox
|
- name: Deploy VM on Proxmox
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
community.general.proxmox_kvm:
|
vars:
|
||||||
api_host: "{{ hypervisor_url }}"
|
virtualization_proxmox_scsi: >-
|
||||||
api_user: "{{ hypervisor_username }}"
|
{%- set out = {} -%}
|
||||||
api_password: "{{ hypervisor_password }}"
|
{%- for disk in system_cfg.disks -%}
|
||||||
ciuser: "{{ user_name }}"
|
{%- set _ = out.update({ 'scsi' ~ loop.index0: hypervisor_cfg.storage ~ ':' ~ (disk.size | int) }) -%}
|
||||||
cipassword: "{{ user_password }}"
|
{%- endfor -%}
|
||||||
|
{{ out }}
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
ciuser: "{{ system_cfg.user.name }}"
|
||||||
|
cipassword: "{{ system_cfg.user.password }}"
|
||||||
ciupgrade: false
|
ciupgrade: false
|
||||||
node: "{{ hypervisor_node }}"
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
vmid: "{{ vm_id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
cpu: host
|
cpu: host
|
||||||
cores: "{{ vm_cpus }}"
|
cores: "{{ system_cfg.cpus }}"
|
||||||
memory: "{{ vm_memory }}"
|
memory: "{{ system_cfg.memory }}"
|
||||||
balloon: "{{ vm_ballo | default(omit) }}"
|
balloon: "{{ system_cfg.balloon if system_cfg.balloon is defined and system_cfg.balloon | int > 0 else omit }}"
|
||||||
numa_enabled: true
|
numa_enabled: true
|
||||||
hotplug: network,disk
|
hotplug: network,disk
|
||||||
|
update: "{{ virtualization_tpm2_enabled | bool }}"
|
||||||
|
update_unsafe: "{{ virtualization_tpm2_enabled | bool }}"
|
||||||
bios: ovmf
|
bios: ovmf
|
||||||
|
machine: "{{ 'q35' if virtualization_tpm2_enabled | bool else omit }}"
|
||||||
boot: ac
|
boot: ac
|
||||||
scsihw: virtio-scsi-single
|
scsihw: virtio-scsi-single
|
||||||
scsi:
|
scsi: "{{ virtualization_proxmox_scsi }}"
|
||||||
scsi0: "{{ hypervisor_storage }}:{{ vm_size }}"
|
|
||||||
efidisk0:
|
efidisk0:
|
||||||
efitype: 4m
|
efitype: 4m
|
||||||
format: raw
|
format: raw
|
||||||
pre_enrolled_keys: false
|
pre_enrolled_keys: false
|
||||||
storage: "{{ hypervisor_storage }}"
|
storage: "{{ hypervisor_cfg.storage }}"
|
||||||
|
tpmstate0: >-
|
||||||
|
{{
|
||||||
|
{'storage': hypervisor_cfg.storage, 'version': '2.0'}
|
||||||
|
if virtualization_tpm2_enabled | bool
|
||||||
|
else omit
|
||||||
|
}}
|
||||||
ide:
|
ide:
|
||||||
ide0: "{{ boot_iso }},media=cdrom"
|
ide0: "{{ boot_iso }},media=cdrom"
|
||||||
ide1: "{{ rhel_iso | default(omit) }},media=cdrom"
|
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
||||||
ide2: "{{ hypervisor_storage }}:cloudinit"
|
ide2: "{{ hypervisor_cfg.storage }}:cloudinit"
|
||||||
net:
|
net:
|
||||||
net0: virtio,bridge={{ vm_nif }}{% if vlan_name is defined and vlan_name %},tag={{ vlan_name }}{% endif %}
|
net0: >-
|
||||||
|
virtio,bridge={{ system_cfg.network.bridge }}{% if system_cfg.network.vlan is defined and system_cfg.network.vlan | string | length > 0 %},tag={{ system_cfg.network.vlan }}{% endif %}
|
||||||
ipconfig:
|
ipconfig:
|
||||||
ipconfig0: ip={{ vm_ip }},gw={{ vm_gw }}
|
ipconfig0: >-
|
||||||
nameservers: "{{ vm_dns }}"
|
{{
|
||||||
|
'ip=' ~ system_cfg.network.ip ~ '/' ~ system_cfg.network.prefix
|
||||||
|
~ (',gw=' ~ system_cfg.network.gateway if system_cfg.network.gateway is defined and system_cfg.network.gateway | length else '')
|
||||||
|
if system_cfg.network.ip is defined and system_cfg.network.ip | string | length
|
||||||
|
else 'ip=dhcp'
|
||||||
|
}}
|
||||||
|
nameservers: "{{ system_cfg.network.dns.servers if system_cfg.network.dns.servers | length else omit }}"
|
||||||
|
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
||||||
onboot: true
|
onboot: true
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Start VM on Proxmox
|
- name: Start VM on Proxmox
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
community.general.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
api_password: "{{ hypervisor_password }}"
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
node: "{{ hypervisor_node }}"
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
vmid: "{{ vm_id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
state: started
|
state: started
|
||||||
|
register: virtualization_proxmox_start_result
|
||||||
|
|
||||||
|
- name: Set VM created fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_vm_created_in_run: true
|
||||||
|
when: virtualization_proxmox_start_result is defined and virtualization_proxmox_start_result.changed | bool
|
||||||
|
|||||||
@@ -1,45 +1,102 @@
|
|||||||
|
---
|
||||||
|
- name: Build vCenter disk list
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_vmware_disks: "{{ virtualization_vmware_disks | default([]) + [virtualization_vmware_disk_cfg] }}"
|
||||||
|
vars:
|
||||||
|
virtualization_vmware_disk_cfg:
|
||||||
|
size_gb: "{{ item.size | int }}"
|
||||||
|
type: thin
|
||||||
|
datastore: "{{ hypervisor_cfg.storage }}"
|
||||||
|
loop: "{{ system_cfg.disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Create VM in vCenter
|
- name: Create VM in vCenter
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
password: "{{ hypervisor_password }}"
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
validate_certs: false
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
datacenter: "{{ hypervisor_cluster }}"
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
cluster: "{{ hypervisor_node }}"
|
cluster: "{{ hypervisor_cfg.cluster }}"
|
||||||
folder: "{{ vm_path }}"
|
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
guest_id: otherGuest64
|
guest_id: otherLinux64Guest
|
||||||
state: poweredon
|
annotation: |
|
||||||
disk:
|
{{ note if note is defined else '' }}
|
||||||
- size_gb: "{{ vm_size }}"
|
state: "{{ 'poweredoff' if virtualization_tpm2_enabled | bool else 'poweredon' }}"
|
||||||
type: thin
|
disk: "{{ virtualization_vmware_disks }}"
|
||||||
datastore: "{{ hypervisor_storage }}"
|
|
||||||
hardware:
|
hardware:
|
||||||
memory_mb: "{{ vm_memory }}"
|
memory_mb: "{{ system_cfg.memory }}"
|
||||||
num_cpus: "{{ vm_cpus }}"
|
num_cpus: "{{ system_cfg.cpus }}"
|
||||||
boot_firmware: efi
|
boot_firmware: efi
|
||||||
secure_boot: false
|
secure_boot: false
|
||||||
cdrom:
|
cdrom: >-
|
||||||
- controller_number: 0
|
{{
|
||||||
unit_number: 0
|
[ {
|
||||||
controller_type: sata
|
"controller_number": 0,
|
||||||
state: present
|
"unit_number": 0,
|
||||||
type: iso
|
"controller_type": "sata",
|
||||||
iso_path: "{{ boot_iso }}"
|
"state": "present",
|
||||||
- controller_number: 0
|
"type": "iso",
|
||||||
unit_number: 1
|
"iso_path": boot_iso
|
||||||
controller_type: sata
|
} ]
|
||||||
state: present
|
+
|
||||||
type: iso
|
( [ {
|
||||||
iso_path: "{{ rhel_iso | default(omit) }}"
|
"controller_number": 0,
|
||||||
|
"unit_number": 1,
|
||||||
|
"controller_type": "sata",
|
||||||
|
"state": "present",
|
||||||
|
"type": "iso",
|
||||||
|
"iso_path": rhel_iso
|
||||||
|
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
||||||
|
}}
|
||||||
networks:
|
networks:
|
||||||
- name: "{{ vm_nif }}"
|
- name: "{{ system_cfg.network.bridge }}"
|
||||||
type: dhcp
|
type: dhcp
|
||||||
vlan: "{{ vlan_name | default(omit) }}"
|
vlan: "{{ system_cfg.network.vlan if system_cfg.network.vlan is defined and system_cfg.network.vlan | string | length > 0 else omit }}"
|
||||||
register: vmware_guest_result
|
register: virtualization_vmware_create_result
|
||||||
failed_when:
|
|
||||||
- vmware_guest_result.failed is defined and vmware_guest_result.failed
|
- name: Set VM created fact when VM was powered on during creation
|
||||||
- "'error' in vmware_guest_result"
|
ansible.builtin.set_fact:
|
||||||
- "'failed' in vmware_guest_result"
|
virtualization_vm_created_in_run: true
|
||||||
- vmware_guest_result.rc is defined and vmware_guest_result.rc != 0
|
when:
|
||||||
|
- virtualization_vmware_create_result is defined
|
||||||
|
- not virtualization_tpm2_enabled | bool
|
||||||
|
- virtualization_vmware_create_result.changed | bool
|
||||||
|
|
||||||
|
- name: Ensure vTPM2 is enabled when required
|
||||||
|
when: virtualization_tpm2_enabled | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
community.vmware.vmware_guest_tpm:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Start VM in vCenter
|
||||||
|
when: virtualization_tpm2_enabled | bool
|
||||||
|
delegate_to: localhost
|
||||||
|
vmware.vmware.vm_powerstate:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
state: powered-on
|
||||||
|
register: virtualization_vmware_start_result
|
||||||
|
|
||||||
|
- name: Set VM created fact when VM was started separately (TPM2 case)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_vm_created_in_run: true
|
||||||
|
when:
|
||||||
|
- virtualization_tpm2_enabled | bool
|
||||||
|
- virtualization_vmware_start_result is defined
|
||||||
|
- virtualization_vmware_start_result.changed | bool
|
||||||
|
|||||||
79
roles/virtualization/tasks/xen.yml
Normal file
79
roles/virtualization/tasks/xen.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy VM on Xen
|
||||||
|
block:
|
||||||
|
- name: Build disk definitions
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_xen_disks: "{{ virtualization_xen_disks | default([]) + [virtualization_xen_disk_cfg] }}"
|
||||||
|
vars:
|
||||||
|
device_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
device_letter: "{{ device_letter_map[ansible_loop.index0] }}"
|
||||||
|
virtualization_xen_disk_cfg: >-
|
||||||
|
{{
|
||||||
|
{
|
||||||
|
'path': (
|
||||||
|
virtualization_xen_disk_path ~ '/' ~ hostname ~ '.qcow2'
|
||||||
|
if ansible_loop.index0 == 0
|
||||||
|
else virtualization_xen_disk_path ~ '/' ~ hostname ~ '-disk' ~ ansible_loop.index0 ~ '.qcow2'
|
||||||
|
),
|
||||||
|
'target': 'xvd' ~ device_letter,
|
||||||
|
'size': (item.size | float)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loop: "{{ system_cfg.disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
extended: true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create VM disks for Xen
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- qemu-img
|
||||||
|
- create
|
||||||
|
- -f
|
||||||
|
- qcow2
|
||||||
|
- "{{ item.path }}"
|
||||||
|
- "{{ item.size }}G"
|
||||||
|
creates: "{{ item.path }}"
|
||||||
|
loop: "{{ virtualization_xen_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
|
- name: Render Xen VM configuration
|
||||||
|
delegate_to: localhost
|
||||||
|
vars:
|
||||||
|
xen_installer_media_enabled: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: xen.cfg.j2
|
||||||
|
dest: /tmp/xen-{{ hostname }}.cfg
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Create Xen VM
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- xl
|
||||||
|
- create
|
||||||
|
- /tmp/xen-{{ hostname }}.cfg
|
||||||
|
register: virtualization_xen_create_result
|
||||||
|
changed_when: virtualization_xen_create_result.rc == 0
|
||||||
|
|
||||||
|
- name: Ensure VM is running
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- xl
|
||||||
|
- list
|
||||||
|
register: virtualization_xen_list_result
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Set VM created fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
virtualization_vm_created_in_run: true
|
||||||
|
when:
|
||||||
|
- virtualization_xen_list_result is defined
|
||||||
|
- >-
|
||||||
|
virtualization_xen_list_result.stdout | default('')
|
||||||
|
is search('(?m)^' ~ (hostname | ansible.builtin.regex_escape) ~ '\\s+\\d+\\s')
|
||||||
@@ -3,9 +3,40 @@ network:
|
|||||||
ethernets:
|
ethernets:
|
||||||
id0:
|
id0:
|
||||||
match:
|
match:
|
||||||
macaddress: "{{ mac_address_output.stdout }}"
|
macaddress: "{{ virtualization_mac_address }}"
|
||||||
|
{% set has_static = system_cfg.network.ip is defined and system_cfg.network.ip | string | length %}
|
||||||
|
{% set dns_list = system_cfg.network.dns.servers | default([]) %}
|
||||||
|
{% set search_list = system_cfg.network.dns.search | default([]) %}
|
||||||
|
{% if has_static %}
|
||||||
addresses:
|
addresses:
|
||||||
- "{{ vm_ip }}"
|
- "{{ system_cfg.network.ip }}/{{ system_cfg.network.prefix }}"
|
||||||
gateway4: "{{ vm_gw }}"
|
{% if system_cfg.network.gateway is defined and system_cfg.network.gateway | string | length %}
|
||||||
|
gateway4: "{{ system_cfg.network.gateway }}"
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
dhcp4: true
|
||||||
|
{% if dns_list | length or search_list | length %}
|
||||||
|
dhcp4-overrides:
|
||||||
|
{% if dns_list | length %}
|
||||||
|
use-dns: false
|
||||||
|
{% endif %}
|
||||||
|
{% if search_list | length %}
|
||||||
|
use-domains: false
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if dns_list or search_list %}
|
||||||
nameservers:
|
nameservers:
|
||||||
addresses: ['1.1.1.1', '1.0.0.1']
|
{% if dns_list %}
|
||||||
|
addresses:
|
||||||
|
{% for dns in dns_list %}
|
||||||
|
- "{{ dns }}"
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if search_list %}
|
||||||
|
search:
|
||||||
|
{% for search in search_list %}
|
||||||
|
- "{{ search }}"
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
hostname: "archiso"
|
hostname: "archiso"
|
||||||
ssh_pwauth: true
|
ssh_pwauth: true
|
||||||
|
package_update: false
|
||||||
|
package_upgrade: false
|
||||||
users:
|
users:
|
||||||
- name: "{{ user_name }}"
|
- name: "{{ system_cfg.user.name }}"
|
||||||
primary_group: "{{ user_name }}"
|
primary_group: "{{ system_cfg.user.name }}"
|
||||||
groups: users
|
groups: users
|
||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
passwd: "{{ user_password | password_hash('sha512') }}"
|
passwd: "{{ system_cfg.user.password | password_hash('sha512') }}"
|
||||||
lock_passwd: False
|
lock_passwd: False
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<domain type='kvm'>
|
<domain type='kvm'>
|
||||||
<name>{{ hostname }}</name>
|
<name>{{ hostname }}</name>
|
||||||
<memory>{{ vm_memory | int * 1024 }}</memory>
|
<memory>{{ system_cfg.memory | int * 1024 }}</memory>
|
||||||
{% if vm_ballo is defined %}<currentMemory>{{ vm_ballo | int * 1024 }}</currentMemory>{% endif %}
|
{% if system_cfg.balloon is defined and system_cfg.balloon | int > 0 %}<currentMemory>{{ system_cfg.balloon | int * 1024 }}</currentMemory>{% endif %}
|
||||||
<vcpu placement='static'>{{ vm_cpus }}</vcpu>
|
<vcpu placement='static'>{{ system_cfg.cpus }}</vcpu>
|
||||||
<os>
|
<os>
|
||||||
<type arch='x86_64' machine="pc-q35-8.0">hvm</type>
|
<type arch='x86_64' machine="pc-q35-8.0">hvm</type>
|
||||||
<bootmenu enable='no'/>
|
<bootmenu enable='no'/>
|
||||||
<boot dev='hd'/>
|
<boot dev='hd'/>
|
||||||
<boot dev='cdrom'/>
|
<boot dev='cdrom'/>
|
||||||
<loader readonly="yes" type="pflash">/usr/share/edk2/x64/OVMF_CODE.secboot.fd</loader>
|
<loader readonly="yes" type="pflash">/usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd</loader>
|
||||||
<nvram template="/usr/share/edk2/x64/OVMF_VARS.fd"/>
|
<nvram template="/usr/share/edk2/x64/OVMF_VARS.4m.fd"/>
|
||||||
</os>
|
</os>
|
||||||
<features>
|
<features>
|
||||||
<acpi/>
|
<acpi/>
|
||||||
@@ -22,11 +22,13 @@
|
|||||||
<on_reboot>restart</on_reboot>
|
<on_reboot>restart</on_reboot>
|
||||||
<on_crash>destroy</on_crash>
|
<on_crash>destroy</on_crash>
|
||||||
<devices>
|
<devices>
|
||||||
|
{% for disk in virtualization_libvirt_disks | default([]) %}
|
||||||
<disk type='file' device='disk'>
|
<disk type='file' device='disk'>
|
||||||
<driver name='qemu' type='qcow2'/>
|
<driver name='qemu' type='{{ disk.format }}'/>
|
||||||
<source file='{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}.qcow2'/>
|
<source file='{{ disk.path }}'/>
|
||||||
<target dev='vda' bus='virtio'/>
|
<target dev='{{ disk.target }}' bus='{{ disk.bus }}'/>
|
||||||
</disk>
|
</disk>
|
||||||
|
{% endfor %}
|
||||||
<disk type="file" device="cdrom">
|
<disk type="file" device="cdrom">
|
||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ boot_iso }}"/>
|
<source file="{{ boot_iso }}"/>
|
||||||
@@ -34,10 +36,10 @@
|
|||||||
</disk>
|
</disk>
|
||||||
<disk type="file" device="cdrom">
|
<disk type="file" device="cdrom">
|
||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}-cloudinit.iso"/>
|
<source file="{{ virtualization_libvirt_cloudinit_path }}"/>
|
||||||
<target dev="sdb" bus="sata"/>
|
<target dev="sdb" bus="sata"/>
|
||||||
</disk>
|
</disk>
|
||||||
{% if rhel_iso is defined %}
|
{% if rhel_iso is defined and rhel_iso | length > 0 %}
|
||||||
<disk type="file" device="cdrom">
|
<disk type="file" device="cdrom">
|
||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ rhel_iso }}"/>
|
<source file="{{ rhel_iso }}"/>
|
||||||
@@ -45,10 +47,15 @@
|
|||||||
</disk>
|
</disk>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<interface type='network'>
|
<interface type='network'>
|
||||||
<mac address="{{ mac_address_output.stdout }}"/>
|
<mac address="{{ virtualization_mac_address }}"/>
|
||||||
<source network='default'/>
|
<source network='{{ system_cfg.network.bridge if (system_cfg.network.bridge | default('' ) | string | length) > 0 else "default" }}'/>
|
||||||
<model type='virtio'/>
|
<model type='virtio'/>
|
||||||
</interface>
|
</interface>
|
||||||
|
{% if virtualization_tpm2_enabled %}
|
||||||
|
<tpm model='tpm-crb'>
|
||||||
|
<backend type='emulator' version='2.0'/>
|
||||||
|
</tpm>
|
||||||
|
{% endif %}
|
||||||
<input type="tablet" bus="usb"/>
|
<input type="tablet" bus="usb"/>
|
||||||
<input type="mouse" bus="ps2"/>
|
<input type="mouse" bus="ps2"/>
|
||||||
<input type="keyboard" bus="ps2"/>
|
<input type="keyboard" bus="ps2"/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ metadata_expire=86400
|
|||||||
repo_gpgcheck=0
|
repo_gpgcheck=0
|
||||||
type=rpm
|
type=rpm
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
gpgkey=https://getfedora.org/static/fedora.gpg
|
gpgkey=https://fedoraproject.org/fedora.gpg
|
||||||
skip_if_unavailable=False
|
skip_if_unavailable=False
|
||||||
|
|
||||||
[fedora-updates]
|
[fedora-updates]
|
||||||
@@ -21,5 +21,5 @@ repo_gpgcheck=0
|
|||||||
type=rpm
|
type=rpm
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
metadata_expire=86400
|
metadata_expire=86400
|
||||||
gpgkey=https://getfedora.org/static/fedora.gpg
|
gpgkey=https://fedoraproject.org/fedora.gpg
|
||||||
skip_if_unavailable=False
|
skip_if_unavailable=False
|
||||||
|
|||||||
13
templates/rhel10.repo.j2
Normal file
13
templates/rhel10.repo.j2
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[rhel10-baseos]
|
||||||
|
name=RHEL 10 BaseOS
|
||||||
|
baseurl=file:///usr/local/install/redhat/dvd/BaseOS
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release
|
||||||
|
|
||||||
|
[rhel10-appstream]
|
||||||
|
name=RHEL 10 AppStream
|
||||||
|
baseurl=file:///usr/local/install/redhat/dvd/AppStream
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release
|
||||||
17
templates/xen.cfg.j2
Normal file
17
templates/xen.cfg.j2
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
builder = "hvm"
|
||||||
|
name = "{{ hostname }}"
|
||||||
|
memory = "{{ system_cfg.memory }}"
|
||||||
|
vcpus = "{{ system_cfg.cpus }}"
|
||||||
|
disk = [
|
||||||
|
{%- for disk in virtualization_xen_disks | default([]) -%}
|
||||||
|
'file:{{ disk.path }},{{ disk.target }},w'{% if not loop.last or xen_installer_media_enabled | bool %}, {% endif %}
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- if xen_installer_media_enabled | bool -%}
|
||||||
|
'{{ boot_iso }},,hdc,cdrom'{% if rhel_iso is defined and rhel_iso | length > 0 %}, '{{ rhel_iso }},,hdd,cdrom'{% endif %}
|
||||||
|
{%- endif -%}
|
||||||
|
]
|
||||||
|
vif = [ 'bridge={{ system_cfg.network.bridge }},model=e1000' ]
|
||||||
|
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
||||||
|
on_crash = "preserve"
|
||||||
|
on_poweroff = "destroy"
|
||||||
|
serial = "pty"
|
||||||
62
vars_baremetal_example.yml
Normal file
62
vars_baremetal_example.yml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
# Example variables for baremetal installs.
|
||||||
|
hypervisor:
|
||||||
|
type: "none"
|
||||||
|
|
||||||
|
system:
|
||||||
|
type: "physical"
|
||||||
|
os: "archlinux"
|
||||||
|
filesystem: "btrfs" # btrfs|ext4|xfs
|
||||||
|
name: "{{ inventory_hostname }}"
|
||||||
|
cpus: 8
|
||||||
|
memory: 16384
|
||||||
|
network:
|
||||||
|
ip: "{{ ansible_host | default('') }}"
|
||||||
|
prefix: 24
|
||||||
|
gateway: "10.0.0.1"
|
||||||
|
dns:
|
||||||
|
servers:
|
||||||
|
- "1.1.1.1"
|
||||||
|
disks:
|
||||||
|
- device: "/dev/sda"
|
||||||
|
size: 120
|
||||||
|
- device: "/dev/sdb"
|
||||||
|
size: 500
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: ext4
|
||||||
|
user:
|
||||||
|
name: "admin"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "ssh-ed25519 AAAA..."
|
||||||
|
root:
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
luks:
|
||||||
|
enabled: true
|
||||||
|
passphrase: "CHANGE_ME"
|
||||||
|
mapper: "SYSTEM_DECRYPTED"
|
||||||
|
auto: true
|
||||||
|
method: "tpm2"
|
||||||
|
tpm2:
|
||||||
|
device: "auto"
|
||||||
|
pcrs: "7"
|
||||||
|
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"
|
||||||
111
vars_example.yml
111
vars_example.yml
@@ -1,21 +1,94 @@
|
|||||||
vm_ip: "{{ inventory_hostname }}/{{ vm_nms }}"
|
---
|
||||||
install_type: "virtual"
|
# Example variables for virtual provisioning.
|
||||||
cis: false
|
custom_iso: false
|
||||||
|
|
||||||
hypervisor_url: "192.168.0.2"
|
hypervisor:
|
||||||
hypervisor_username: "root@pam"
|
type: "proxmox" # libvirt|proxmox|vmware|xen|none
|
||||||
hypervisor_password: "SomePassword"
|
url: "pve01.example.com"
|
||||||
hypervisor_node: "NodeName"
|
username: "root@pam"
|
||||||
hypervisor_storage: "local-btrfs"
|
password: "CHANGE_ME"
|
||||||
boot_iso: "local-btrfs:iso/archlinux-x86_64.iso"
|
host: "pve01"
|
||||||
rhel_iso: "local-btrfs:rhel-9.4-x86_64-dvd.iso"
|
storage: "local-lvm"
|
||||||
|
datacenter: "dc01"
|
||||||
|
cluster: "cluster01"
|
||||||
|
certs: false
|
||||||
|
ssh: true # VMware only; enables temporary SSH in installer
|
||||||
|
|
||||||
# For VMware-Tools
|
system:
|
||||||
ansible_vmware_host: "{{ hypervisor_url }}"
|
type: "virtual" # virtual|physical
|
||||||
ansible_vmware_user: "{{ hypervisor_username }}"
|
os: "archlinux"
|
||||||
ansible_vmware_password: "{{ hypervisor_password }}"
|
version: ""
|
||||||
ansible_vmware_guest_path: "/{{ hypervisor_cluster }}/vm{{ vm_path }}/{{ hostname }}"
|
filesystem: "btrfs" # btrfs|ext4|xfs
|
||||||
ansible_vmware_validate_certs: no
|
name: "{{ inventory_hostname }}"
|
||||||
ansible_vmware_tools_user: "root"
|
id: 100
|
||||||
ansible_vmware_tools_password: ""
|
cpus: 4
|
||||||
vmware_ssh: true
|
memory: 8192
|
||||||
|
balloon: 0
|
||||||
|
network:
|
||||||
|
bridge: "vmbr0"
|
||||||
|
ip: "{{ inventory_hostname }}"
|
||||||
|
prefix: 24
|
||||||
|
gateway: "10.0.0.1"
|
||||||
|
dns:
|
||||||
|
servers:
|
||||||
|
- "1.1.1.1"
|
||||||
|
- "1.0.0.1"
|
||||||
|
search:
|
||||||
|
- "example.com"
|
||||||
|
path: "/Lab/Example"
|
||||||
|
disks:
|
||||||
|
- size: 80
|
||||||
|
- size: 200
|
||||||
|
mount:
|
||||||
|
path: /data
|
||||||
|
fstype: xfs
|
||||||
|
label: DATA
|
||||||
|
opts: defaults
|
||||||
|
user:
|
||||||
|
name: "ops"
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
key: "ssh-ed25519 AAAA..."
|
||||||
|
root:
|
||||||
|
password: "CHANGE_ME"
|
||||||
|
luks:
|
||||||
|
enabled: false
|
||||||
|
passphrase: "CHANGE_ME"
|
||||||
|
mapper: "SYSTEM_DECRYPTED"
|
||||||
|
auto: true
|
||||||
|
method: "tpm2"
|
||||||
|
tpm2:
|
||||||
|
device: "auto"
|
||||||
|
pcrs: "7"
|
||||||
|
keysize: 64
|
||||||
|
options: "discard,tries=3"
|
||||||
|
type: "luks2"
|
||||||
|
cipher: "aes-xts-plain64"
|
||||||
|
hash: "sha512"
|
||||||
|
iter: 4000
|
||||||
|
bits: 512
|
||||||
|
pbkdf: "argon2id"
|
||||||
|
urandom: true
|
||||||
|
verify: true
|
||||||
|
packages:
|
||||||
|
- jq
|
||||||
|
- tmux
|
||||||
|
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: true
|
||||||
|
sudo: true
|
||||||
|
chroot:
|
||||||
|
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
|
||||||
|
|||||||
Reference in New Issue
Block a user