Compare commits
9 Commits
378d9a88c2
...
d0b26a57ef
| Author | SHA1 | Date | |
|---|---|---|---|
| d0b26a57ef | |||
| f73982d502 | |||
| d92b89b001 | |||
| 3362aad149 | |||
| 2e59d7d27c | |||
| d0bcbb95d8 | |||
| 0181f9104a | |||
| 4de84a7312 | |||
| 04d5e99e56 |
2
.ansible-lint
Normal file
2
.ansible-lint
Normal file
@@ -0,0 +1,2 @@
|
||||
skip_list:
|
||||
- run-once
|
||||
163
README.md
163
README.md
@@ -1,6 +1,6 @@
|
||||
# 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 system bootstrap processes in an Infrastructure-as-Code manner.
|
||||
|
||||
# Info
|
||||
|
||||
@@ -8,13 +8,15 @@ Most of the roles are adaptable for use with systems beyond ArchLinux, requiring
|
||||
|
||||
**NOTE**:
|
||||
|
||||
- For RHEL 8 and RHEL 9, repository access requires the `rhel_iso` variable. This variable specifies a local ISO or proxy repository.
|
||||
- For RHEL 8, RHEL 9, and RHEL 10, repository access requires the `rhel_iso` variable. This variable specifies a local ISO or proxy repository.
|
||||
- RHEL systems do not support `btrfs`. Use `ext4` or `xfs` as alternatives.
|
||||
- For RHEL 8, `xfs` may cause installation issues; `ext4` is recommended.
|
||||
- `custom_iso: true` skips ArchISO validation and pacman setup, your installer ISO must provide the tools required by the selected roles.
|
||||
|
||||
# Supported Distributions
|
||||
|
||||
This playbook supports multiple Linux distributions with specific versions tailored to each. Below is a list of supported distributions:
|
||||
This playbook supports multiple Linux distributions with specific versions tailored to each.
|
||||
Below is a list of supported distributions:
|
||||
|
||||
| `os` | Distribution |
|
||||
| ---------- | ---------------------------------- |
|
||||
@@ -51,22 +53,70 @@ The playbook uses the ArchLinux ISO as a foundational tool to provides an effici
|
||||
|
||||
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.
|
||||
|
||||
### 2.1 Core Provisioning
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ----------------------- | ---------------------------------------------------------- | ----------------------------------------- |
|
||||
| `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` |
|
||||
| `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` |
|
||||
| `hypervisor` | Type of hypervisor. | `libvirt`, `proxmox`, `vmware`, `none` |
|
||||
| `vmware_ssh` | If Ansible should use SSH after base VM setup on VMware. | `true`, `false (default)` |
|
||||
| `hypervisor_datacenter` | Name of the hypervisor datacenter. | `default-datacenter` |
|
||||
| `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` |
|
||||
| `hypervisor_node` | Hypervisor node name. | `node01` |
|
||||
| `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` |
|
||||
| `hypervisor` | Type of hypervisor. | `libvirt`, `proxmox`, `vmware`, `none` |
|
||||
| `install_drive` | Drive where the system will be installed. | `/dev/sda` |
|
||||
| `boot_iso` | Path to the boot ISO image. | `local-btrfs:iso/archlinux-x86_64.iso` |
|
||||
| `rhel_iso` | Path to the RHEL ISO file, required for RHEL 8/9/10. | `local-btrfs:iso/rhel-9.4-x86_64-dvd.iso` |
|
||||
| `custom_iso` (optional) | Skip ArchISO checks and pacman setup on installer media. | `true`, `false (default)` |
|
||||
| `cis` (optional) | Adjusts the installation to be CIS level 3 conformant. | `true`, `false` |
|
||||
| `selinux` (optional) | Toggle SELinux where supported. | `true`, `false` |
|
||||
|
||||
### 2.2 Hypervisor Access (virtual installs)
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ----------------------- | ---------------------------------------------------------- | -------------------- |
|
||||
| `hypervisor_url` | URL/IP address for the hypervisor interface. | `192.168.0.2` |
|
||||
| `hypervisor_username` | Username for hypervisor authentication. | `root@pam` |
|
||||
| `hypervisor_password` | Password for hypervisor authentication. | `123456` |
|
||||
| `hypervisor_datacenter` | Name of the hypervisor datacenter. | `default-datacenter` |
|
||||
| `hypervisor_cluster` | Name of the hypervisor cluster. | `default-cluster` |
|
||||
| `hypervisor_node` | Hypervisor node name. | `node01` |
|
||||
| `hypervisor_storage` | Storage identifier for VM disks. | `local-btrfs` |
|
||||
| `vm_path` (optional) | Libvirt image dir or VMware folder path. | `/var/lib/libvirt/images` |
|
||||
| `vmware_ssh` | If Ansible should use SSH after base VMware setup. | `true`, `false` |
|
||||
| `vlan_name` (optional) | VLAN for the VM's network interface. | `vlan100` |
|
||||
| `note` (optional) | VMware VM annotation. | `Provisioned by Ansible` |
|
||||
|
||||
### 2.3 VMware Tools connection (VMware installs)
|
||||
|
||||
These are required when `hypervisor: vmware` uses the `vmware_tools` connection.
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ------------------------------- | ------------------------------------------ | -------------------------------------- |
|
||||
| `ansible_vmware_tools_user` | Guest OS user for guest operations. | `root` |
|
||||
| `ansible_vmware_tools_password` | Guest OS password for guest operations. | `""` |
|
||||
| `ansible_vmware_guest_path` | VM inventory path (datacenter + folder). | `/dc01/vm/Folder/vm01.example.com` |
|
||||
| `ansible_vmware_host` | vCenter/ESXi hostname. | `vcenter01.example.com` |
|
||||
| `ansible_vmware_user` | vCenter/ESXi username. | `administrator@vsphere.local` |
|
||||
| `ansible_vmware_password` | vCenter/ESXi password. | `********` |
|
||||
| `ansible_vmware_validate_certs` | Validate vCenter/ESXi TLS certs. | `false` |
|
||||
|
||||
### 2.4 Disk Encryption (optional)
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| -------------------------- | ----------------------------------------------- | ------------------ |
|
||||
| `luks_enabled` | Enable LUKS encryption for the root volume. | `true`, `false` |
|
||||
| `luks_passphrase` | Passphrase used for initial LUKS format/unlock. | `1234` |
|
||||
| `luks_mapper_name` | Decrypted mapper name. | `SYSTEM_DECRYPTED` |
|
||||
| `luks_auto_decrypt` | Enable automatic unlock on boot. | `true`, `false` |
|
||||
| `luks_auto_decrypt_method` | Auto-unlock method. | `tpm2`, `keyfile` |
|
||||
| `luks_tpm2_device` | TPM2 device for enrollment. | `auto` |
|
||||
| `luks_tpm2_pcrs` | TPM2 PCR list (systemd-cryptenroll). | `7` |
|
||||
| `luks_keyfile_size` | Keyfile size in bytes for initramfs. | `64` |
|
||||
| `luks_options` | LUKS options passed to crypttab/kernel. | `discard,tries=3` |
|
||||
| `luks_type` | LUKS format type. | `luks2` |
|
||||
| `luks_cipher` | LUKS cipher. | `aes-xts-plain64` |
|
||||
| `luks_hash` | LUKS hash. | `sha512` |
|
||||
| `luks_iter_time` | LUKS iter time in milliseconds. | `4000` |
|
||||
| `luks_key_size` | LUKS key size in bits. | `512` |
|
||||
| `luks_pbkdf` | LUKS PBKDF algorithm. | `argon2id` |
|
||||
| `luks_use_urandom` | Reserved; module uses cryptsetup defaults. | `true` |
|
||||
| `luks_verify_passphrase` | Reserved; module uses cryptsetup defaults. | `true` |
|
||||
|
||||
To protect sensitive information, such as passwords, API keys, and other confidential variables (e.g., `hypervisor_password`), **it is recommended to use Ansible Vault**.
|
||||
|
||||
@@ -74,30 +124,51 @@ To protect sensitive information, such as passwords, API keys, and other confide
|
||||
|
||||
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.
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| --------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `cis` (optional) | Adjusts the installation to be CIS level 3 conformant. | `true`, `false` |
|
||||
| `selinux` (optional) | Toggle SELinux, `false` means it should be disabled.` | `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`, `rhel10`, `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` |
|
||||
| `user_public_key` | SSH Key for the user account within the VM. | `ssh-ed25519 AAAAC` |
|
||||
| `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_dns_search` | DNS search zone for the virtual machine's network configuration. | `example.com` |
|
||||
| `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` |
|
||||
### 3.1 System Identity and OS
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ------------ | -------------------------------------- | ---------------------- |
|
||||
| `os` | Operating system to be installed. | `ubuntu-lts` |
|
||||
| `filesystem` | Filesystem type for the root volume. | `btrfs`, `ext4`, `xfs` |
|
||||
| `hostname` | The hostname assigned to the system. | `vm01` |
|
||||
|
||||
### 3.2 Credentials and Access
|
||||
|
||||
These are prompted by default via `vars_prompt` in `main.yml`, but can be supplied via inventory/vars/`-e` for non-interactive runs.
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ----------------- | ---------------------------------- | ----------------- |
|
||||
| `root_password` | Root password (vault recommended). | `SecurePass123` |
|
||||
| `user_name` | Username for a user account. | `adminuser` |
|
||||
| `user_password` | Password for the user account. | `UserPass123` |
|
||||
| `user_public_key` | SSH Key for the user account. | `ssh-ed25519 AAAA` |
|
||||
|
||||
### 3.3 Networking
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| --------------- | -------------------------------------------------------------- | ----------------- |
|
||||
| `vm_ip` | IP address assigned to the system (omit to use DHCP). | `192.168.0.10` |
|
||||
| `vm_nms` | Netmask bits for static addressing. | `24` |
|
||||
| `vm_gw` | Default gateway IP address (static only). | `192.168.0.1` |
|
||||
| `vm_dns` | DNS server IP address(es). | `1.0.0.1,1.1.1.1` |
|
||||
| `vm_dns_search` | DNS search zone(s) for the network configuration. | `example.com` |
|
||||
| `vm_nif` | Network interface/bridge for the VM's network connection. | `vmbr0` |
|
||||
|
||||
### 3.4 VM Sizing (virtual installs)
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ----------- | --------------------------------- | ------------- |
|
||||
| `vm_id` | Unique identifier for the VM. | `101` |
|
||||
| `vm_size` | Disk size allocated in GB. | `20` |
|
||||
| `vm_memory` | Amount of memory in MB. | `2048` |
|
||||
| `vm_cpus` | Number of CPU cores. | `4` |
|
||||
| `vm_ballo` | Ballooning memory size (optional).| `2048` |
|
||||
|
||||
### 3.5 Post-install Packages
|
||||
|
||||
| Variable | Description | Example Value |
|
||||
| ------------------------ | --------------------------------------------------------------------- | ------------------ |
|
||||
| `extra_packages` (optional) | Additional packages installed after the first boot into the installed OS. | `["git", "jq"]` |
|
||||
|
||||
## 4. How to Use the Playbook
|
||||
|
||||
@@ -118,3 +189,15 @@ ansible-playbook -i inventory.yml -e @vars.yml main.yml
|
||||
```
|
||||
|
||||
This command prompts Ansible to execute the `main.yml` playbook, applying configurations defined in both `vars.yml` and the inventory file.
|
||||
|
||||
Use `inventory_example.yml`, `vars_example.yml`, and the bare-metal examples as starting points for new inventories.
|
||||
|
||||
## Notes
|
||||
|
||||
- `vm_size`/`vm_memory` are required for virtual installs only, physical installs use the full disk.
|
||||
- `vm_dns` and `vm_dns_search` accept comma-separated strings or YAML lists.
|
||||
- `hypervisor` determines which backend-specific roles run.
|
||||
- Guest tools are installed based on `hypervisor`: `qemu-guest-agent` for `libvirt`/`proxmox`, `open-vm-tools` for `vmware`, otherwise none.
|
||||
- With `luks_auto_decrypt_method: tpm2` on virtual installs, the virtualization role enables a TPM2 device (libvirt/proxmox/vmware).
|
||||
- For VMware, `vmware_ssh: true` enables SSH on the guest and switches the connection to SSH for the remaining tasks.
|
||||
- For physical installs, set `ansible_user`/`ansible_password` for the installer environment when it differs from the prompted user credentials.
|
||||
|
||||
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
|
||||
9
inventory_baremetal_example.yml
Normal file
9
inventory_baremetal_example.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
all:
|
||||
hosts:
|
||||
baremetal01.example.com:
|
||||
ansible_host: 10.0.0.162
|
||||
ansible_user: root
|
||||
ansible_password: "1234"
|
||||
ansible_become_password: "1234"
|
||||
hostname: "baremetal01.example.com"
|
||||
@@ -1,35 +1,40 @@
|
||||
---
|
||||
all:
|
||||
vars:
|
||||
hypervisor: 'proxmox'
|
||||
install_drive: '/dev/sda'
|
||||
cis: true
|
||||
boot_iso: "local-btrfs:iso/archlinux-x86_64.iso"
|
||||
hypervisor: "proxmox"
|
||||
install_type: "virtual"
|
||||
install_drive: "/dev/sda"
|
||||
boot_iso: "local:iso/archlinux-x86_64.iso"
|
||||
vm_nif: "vmbr0"
|
||||
vm_gw: "10.0.0.1"
|
||||
vm_dns:
|
||||
- 1.1.1.1
|
||||
- 1.0.0.1
|
||||
vm_dns_search:
|
||||
- example.com
|
||||
children:
|
||||
promox-kvm:
|
||||
hosts:
|
||||
192.168.122.10:
|
||||
hostname: proxy
|
||||
vm_id: 100
|
||||
os: archlinux
|
||||
filesystem: btrfs
|
||||
vm_memory: "2048"
|
||||
vm_ballo: "1024"
|
||||
vm_cpus: "2"
|
||||
vm_size: "5"
|
||||
vm_nif: vmbr1
|
||||
vm_gw: 192.168.122.1
|
||||
vm_dns: 1.1.1.1
|
||||
vm_dns_search: "example.com"
|
||||
192.168.122.11:
|
||||
hostname: database
|
||||
vm_id: 101
|
||||
os: rhel9
|
||||
filesystem: xfs
|
||||
vm_memory: "6144"
|
||||
vm_ballo: "3072"
|
||||
vm_cpus: "4"
|
||||
vm_size: "40"
|
||||
vm_nif: vmbr1
|
||||
vm_gw: 192.168.122.1
|
||||
vm_dns: 1.1.1.1
|
||||
rhel_iso: "local-btrfs:iso/rhel-9.4-x86_64-dvd.iso"
|
||||
proxmox:
|
||||
hosts:
|
||||
proxy01.example.com:
|
||||
ansible_host: 10.0.0.10
|
||||
hostname: "proxy01.example.com"
|
||||
vm_id: 100
|
||||
os: "archlinux"
|
||||
filesystem: "btrfs"
|
||||
vm_memory: 4096
|
||||
vm_ballo: 2048
|
||||
vm_cpus: 2
|
||||
vm_size: 40
|
||||
vm_ip: 10.0.0.10
|
||||
database01.example.com:
|
||||
ansible_host: 10.0.0.11
|
||||
hostname: "database01.example.com"
|
||||
vm_id: 101
|
||||
os: "rhel9"
|
||||
filesystem: "xfs"
|
||||
vm_memory: 4096
|
||||
vm_ballo: 2048
|
||||
vm_cpus: 4
|
||||
vm_size: 60
|
||||
vm_ip: 10.0.0.11
|
||||
rhel_iso: "local:iso/rhel-9.4-x86_64-dvd.iso"
|
||||
|
||||
133
main.yml
133
main.yml
@@ -1,7 +1,7 @@
|
||||
---
|
||||
- name: Create and configure VMs
|
||||
hosts: all
|
||||
strategy: free
|
||||
strategy: free # noqa: run-once[play]
|
||||
gather_facts: false
|
||||
become: true
|
||||
vars_prompt:
|
||||
@@ -26,36 +26,65 @@
|
||||
confirm: true
|
||||
vars_files: vars.yml
|
||||
pre_tasks:
|
||||
- name: Set ansible_python_interpreter
|
||||
when: os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"]
|
||||
- name: Validate variables
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- install_type in ["virtual", "physical"]
|
||||
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
|
||||
- filesystem in ["btrfs", "ext4", "xfs"]
|
||||
- install_drive is defined
|
||||
- install_type == "physical" or vm_size is defined
|
||||
- install_type == "physical" or vm_memory is defined
|
||||
- os in ["archlinux", "almalinux", "debian11", "debian12", "debian13", "fedora", "rhel8", "rhel9", "rhel10", "rocky", "ubuntu", "ubuntu-lts"]
|
||||
- os not in ["rhel8", "rhel9", "rhel10"] or rhel_iso is defined
|
||||
- >-
|
||||
install_type == "physical"
|
||||
or (
|
||||
(filesystem == "btrfs" and (vm_size | default(0) | int) >= 10)
|
||||
or (filesystem != "btrfs" and (vm_size | default(0) | int) >= 20)
|
||||
)
|
||||
- >-
|
||||
install_type == "physical"
|
||||
or (
|
||||
(vm_size | default(0) | float)
|
||||
>= (
|
||||
(vm_memory | default(0) | float / 1024 >= 16.0)
|
||||
| ternary(
|
||||
(vm_memory | default(0) | float / 2048),
|
||||
[vm_memory | default(0) | float / 1024, 4.0] | max
|
||||
)
|
||||
+ 16
|
||||
)
|
||||
)
|
||||
fail_msg: Invalid input specified, please try again.
|
||||
|
||||
- name: Normalize optional flags
|
||||
ansible.builtin.set_fact:
|
||||
cis: "{{ cis | default(false) | bool }}"
|
||||
custom_iso: "{{ custom_iso | default(false) | bool }}"
|
||||
is_rhel: "{{ os | default('') | lower in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rhel10', 'rocky'] }}"
|
||||
is_debian: "{{ os | default('') | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts'] }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Set Python interpreter for RHEL-based installers
|
||||
when:
|
||||
- ansible_python_interpreter is not defined
|
||||
- os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"]
|
||||
ansible.builtin.set_fact:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
changed_when: false
|
||||
|
||||
- name: Set default variables
|
||||
ansible.builtin.set_fact:
|
||||
cis: false
|
||||
|
||||
- name: Set SSH Access
|
||||
when: hypervisor != "vmware"
|
||||
- name: Set SSH access
|
||||
when:
|
||||
- install_type == "virtual"
|
||||
- hypervisor != "vmware"
|
||||
ansible.builtin.set_fact:
|
||||
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
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
|
||||
- filesystem in ["btrfs", "ext4", "xfs"]
|
||||
- install_drive is defined
|
||||
- os in ["archlinux", "almalinux", "debian11", "debian12", "debian13", "fedora", "rhel8", "rhel9", "rhel10", "rocky", "ubuntu", "ubuntu-lts"]
|
||||
- os not in ["rhel8", "rhel9", "rhel10"] or rhel_iso is defined
|
||||
- (filesystem == "btrfs" and (vm_size | int) >= 10) or (filesystem != "btrfs" and (vm_size | int) >= 20)
|
||||
- (vm_size | float) >= ((vm_memory | float / 1024 >= 16.0) | ternary((vm_memory | float / 2048), [vm_memory | float / 1024, 4.0] | max) + 16)
|
||||
fail_msg: Invalid input specified, please try again.
|
||||
|
||||
- name: Set connection
|
||||
- name: Set connection for VMware
|
||||
when: hypervisor == "vmware"
|
||||
ansible.builtin.set_fact:
|
||||
ansible_connection: vmware_tools
|
||||
@@ -73,31 +102,65 @@
|
||||
|
||||
- role: partitioning
|
||||
vars:
|
||||
boot_partition_suffix: 1
|
||||
main_partition_suffix: 2
|
||||
partitioning_boot_partition_suffix: 1
|
||||
partitioning_main_partition_suffix: 2
|
||||
|
||||
- role: bootstrap
|
||||
|
||||
- role: configuration
|
||||
|
||||
- role: cis
|
||||
when: cis | bool
|
||||
when: cis | default(false) | bool
|
||||
|
||||
- role: cleanup
|
||||
when: install_type == "virtual"
|
||||
vars:
|
||||
ansible_connection: local
|
||||
when: install_type in ["virtual", "physical"]
|
||||
become: false
|
||||
|
||||
tasks:
|
||||
- name: Set final SSH Credentials
|
||||
when: hypervisor != 'vmware' or (hypervisor == 'vmware' and vmware_ssh | bool)
|
||||
post_tasks:
|
||||
- name: Set post-reboot connection flags
|
||||
ansible.builtin.set_fact:
|
||||
post_reboot_can_connect: >-
|
||||
{{
|
||||
(ansible_connection | default('ssh')) != 'ssh'
|
||||
or ((vm_ip | default('') | string | length) > 0)
|
||||
or (
|
||||
install_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 | default(false) | bool
|
||||
ansible.builtin.set_fact:
|
||||
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: Check if VM is back and running
|
||||
when: not (hypervisor == 'vmware' and cis | bool)
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 300
|
||||
- name: Install post-reboot extra packages
|
||||
when:
|
||||
- extra_packages is defined
|
||||
- post_reboot_can_connect | default(false) | bool
|
||||
block:
|
||||
- name: Normalize extra package list
|
||||
ansible.builtin.set_fact:
|
||||
post_install_extra_packages: >-
|
||||
{{
|
||||
(
|
||||
extra_packages
|
||||
if (extra_packages is iterable and extra_packages is not string)
|
||||
else (extra_packages | default('') | string).split(',')
|
||||
)
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Install extra packages
|
||||
when: post_install_extra_packages | length > 0
|
||||
ansible.builtin.package:
|
||||
name: "{{ post_install_extra_packages }}"
|
||||
state: present
|
||||
|
||||
25
roles/bootstrap/tasks/almalinux.yml
Normal file
25
roles/bootstrap/tasks/almalinux.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
- name: Bootstrap AlmaLinux 9
|
||||
vars:
|
||||
bootstrap_alma_extra: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
lookup('vars', bootstrap_var_key)
|
||||
| difference(bootstrap_guest_agent_remove_packages)
|
||||
)
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- >-
|
||||
dnf --releasever=9 --best --repo=alma-baseos --installroot=/mnt
|
||||
--setopt=install_weak_deps=False groupinstall -y base core
|
||||
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- >-
|
||||
arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False
|
||||
install -y {{ bootstrap_alma_extra }}
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
15
roles/bootstrap/tasks/archlinux.yml
Normal file
15
roles/bootstrap/tasks/archlinux.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: Bootstrap ArchLinux
|
||||
vars:
|
||||
bootstrap_archlinux_packages: >-
|
||||
{{
|
||||
(
|
||||
lookup('vars', bootstrap_var_key)
|
||||
| difference(bootstrap_guest_agent_remove_packages)
|
||||
)
|
||||
+ bootstrap_guest_agent_packages
|
||||
}}
|
||||
ansible.builtin.command: >-
|
||||
pacstrap /mnt {{ bootstrap_archlinux_packages | join(' ') }} --asexplicit
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
29
roles/bootstrap/tasks/debian.yml
Normal file
29
roles/bootstrap/tasks/debian.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
- name: Bootstrap Debian System
|
||||
vars:
|
||||
bootstrap_debian_release: >-
|
||||
{{
|
||||
'bullseye' if bootstrap_os_key == 'debian11'
|
||||
else 'bookworm' if bootstrap_os_key == 'debian12'
|
||||
else 'trixie'
|
||||
}}
|
||||
bootstrap_debian_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}"
|
||||
bootstrap_debian_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
|
||||
bootstrap_debian_base: "{{ (bootstrap_debian_base_list | difference(bootstrap_guest_agent_remove_packages)) | join(',') }}"
|
||||
bootstrap_debian_extra: >-
|
||||
{{
|
||||
(
|
||||
(bootstrap_debian_extra_list | difference(bootstrap_guest_agent_remove_packages))
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- >-
|
||||
debootstrap --include={{ bootstrap_debian_base }}
|
||||
{{ bootstrap_debian_release }} /mnt http://deb.debian.org/debian/
|
||||
- "arch-chroot /mnt apt install -y {{ bootstrap_debian_extra }}"
|
||||
- arch-chroot /mnt apt remove -y libcups2 libavahi-common3 libavahi-common-data
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
27
roles/bootstrap/tasks/fedora.yml
Normal file
27
roles/bootstrap/tasks/fedora.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Bootstrap Fedora 42
|
||||
vars:
|
||||
bootstrap_fedora_extra: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
lookup('vars', bootstrap_var_key)
|
||||
| difference(bootstrap_guest_agent_remove_packages)
|
||||
)
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- >-
|
||||
dnf --releasever=42 --best --repo=fedora --repo=fedora-updates
|
||||
--installroot=/mnt --setopt=install_weak_deps=False
|
||||
groupinstall -y critical-path-base core
|
||||
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- >-
|
||||
arch-chroot /mnt dnf --releasever=42 --setopt=install_weak_deps=False
|
||||
install -y {{ bootstrap_fedora_extra }}
|
||||
- arch-chroot /mnt dnf reinstall -y kernel-core
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
@@ -1,101 +1,43 @@
|
||||
---
|
||||
- name: Run OS-specific bootstrap process
|
||||
vars:
|
||||
bootstrap_os_key: "{{ os | lower }}"
|
||||
bootstrap_var_key: "{{ 'bootstrap_' + (os | lower | replace('-', '_')) }}"
|
||||
bootstrap_hypervisor_key: "{{ hypervisor | default('none') | lower }}"
|
||||
bootstrap_guest_agent_packages: >-
|
||||
{{
|
||||
['qemu-guest-agent'] if bootstrap_hypervisor_key in ['libvirt', 'proxmox']
|
||||
else ['open-vm-tools'] if bootstrap_hypervisor_key == 'vmware'
|
||||
else []
|
||||
}}
|
||||
bootstrap_guest_agent_remove_packages:
|
||||
- open-vm-tools
|
||||
- qemu-guest-agent
|
||||
block:
|
||||
- name: Bootstrap ArchLinux
|
||||
when: os | lower == 'archlinux'
|
||||
ansible.builtin.command: pacstrap /mnt {{ archlinux | join(' ') }} --asexplicit
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
- name: Include AlmaLinux bootstrap tasks
|
||||
when: bootstrap_os_key == 'almalinux'
|
||||
ansible.builtin.include_tasks: almalinux.yml
|
||||
|
||||
- name: Bootstrap Debian System
|
||||
when: os | lower in ['debian11', 'debian12', 'debian13']
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
with_items:
|
||||
- debootstrap --include={{ vars[os].base | join(',') }} {{ 'bullseye' if os == 'debian11' else 'bookworm' if os == 'debian12' else 'trixie' }}
|
||||
/mnt http://deb.debian.org/debian/
|
||||
- arch-chroot /mnt apt install -y {{ vars[os].extra | join(' ') }}
|
||||
- arch-chroot /mnt apt remove -y libcups2 libavahi-common3 libavahi-common-data
|
||||
- name: Include ArchLinux bootstrap tasks
|
||||
when: bootstrap_os_key == 'archlinux'
|
||||
ansible.builtin.include_tasks: archlinux.yml
|
||||
|
||||
- name: Bootstrap Ubuntu System
|
||||
when: os | lower in ['ubuntu', 'ubuntu-lts']
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
with_items:
|
||||
- debootstrap --include={{ vars[os].base | join(',') }} {{ 'plucky' if os == 'ubuntu' else 'noble' }}
|
||||
/mnt http://archive.ubuntu.com/ubuntu/
|
||||
- ln -sf /run/NetworkManager/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 {{ vars[os].extra | join(' ') }}
|
||||
- name: Include Debian bootstrap tasks
|
||||
when: bootstrap_os_key in ['debian11', 'debian12', 'debian13']
|
||||
ansible.builtin.include_tasks: debian.yml
|
||||
|
||||
- name: Bootstrap AlmaLinux 9
|
||||
when: os | lower == 'almalinux'
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
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/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False install -y {{ almalinux | join(' ') }}
|
||||
- name: Include Fedora bootstrap tasks
|
||||
when: bootstrap_os_key == 'fedora'
|
||||
ansible.builtin.include_tasks: fedora.yml
|
||||
|
||||
- name: Bootstrap Fedora 42
|
||||
when: os | lower == 'fedora'
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
with_items:
|
||||
- dnf --releasever=42 --best --repo=fedora --repo=fedora-updates
|
||||
--installroot=/mnt --setopt=install_weak_deps=False groupinstall -y critical-path-base core
|
||||
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- arch-chroot /mnt dnf --releasever=42 --setopt=install_weak_deps=False install -y {{ fedora | join(' ') }}
|
||||
- arch-chroot /mnt dnf reinstall -y kernel-core
|
||||
- name: Include Rocky bootstrap tasks
|
||||
when: bootstrap_os_key == 'rocky'
|
||||
ansible.builtin.include_tasks: rocky.yml
|
||||
|
||||
- name: Bootstrap RockyLinux 9
|
||||
when: os | lower == 'rocky'
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
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/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False install -y {{ rocky | join(' ') }}
|
||||
- name: Include RHEL bootstrap tasks
|
||||
when: bootstrap_os_key in ['rhel8', 'rhel9', 'rhel10']
|
||||
ansible.builtin.include_tasks: rhel.yml
|
||||
|
||||
- name: Bootstrap RHEL System
|
||||
when: os | lower in ['rhel8', 'rhel9', 'rhel10']
|
||||
block:
|
||||
- name: Install base packages in chroot environment
|
||||
ansible.builtin.command: >-
|
||||
dnf --releasever={{ os | lower | replace('rhel', '') }} --repo={{ os | lower }}-baseos
|
||||
--installroot=/mnt
|
||||
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
||||
groupinstall -y core base standard
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
|
||||
- name: Prepare chroot environment
|
||||
ansible.builtin.shell: |
|
||||
ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
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
|
||||
ansible.builtin.copy:
|
||||
src: /etc/yum.repos.d/{{ os | lower }}.repo
|
||||
dest: /mnt/etc/yum.repos.d/redhat.repo
|
||||
mode: "0644"
|
||||
remote_src: true
|
||||
|
||||
- name: Install additional packages in chroot
|
||||
ansible.builtin.command: >-
|
||||
arch-chroot /mnt dnf --releasever={{ os | lower | replace('rhel', '') }}
|
||||
--setopt=install_weak_deps=False install -y {{ vars[os] | join(' ') }}
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
- name: Include Ubuntu bootstrap tasks
|
||||
when: bootstrap_os_key in ['ubuntu', 'ubuntu-lts']
|
||||
ansible.builtin.include_tasks: ubuntu.yml
|
||||
|
||||
71
roles/bootstrap/tasks/rhel.yml
Normal file
71
roles/bootstrap/tasks/rhel.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
- 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
|
||||
force: true
|
||||
|
||||
- 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:
|
||||
argv:
|
||||
- arch-chroot
|
||||
- /mnt
|
||||
- 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)
|
||||
| difference(bootstrap_guest_agent_remove_packages)
|
||||
)
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: >-
|
||||
arch-chroot /mnt dnf --releasever={{ bootstrap_rhel_release }}
|
||||
--setopt=install_weak_deps=False install -y {{ bootstrap_rhel_extra }}
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
26
roles/bootstrap/tasks/rocky.yml
Normal file
26
roles/bootstrap/tasks/rocky.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Bootstrap RockyLinux 9
|
||||
vars:
|
||||
bootstrap_rocky_extra: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
lookup('vars', bootstrap_var_key)
|
||||
| difference(bootstrap_guest_agent_remove_packages)
|
||||
)
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- >-
|
||||
dnf --releasever=9 --best --repo=rocky-baseos --installroot=/mnt
|
||||
--setopt=install_weak_deps=False --setopt=optional_metadata_types=filelists
|
||||
groupinstall -y base core
|
||||
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- >-
|
||||
arch-chroot /mnt dnf --releasever=9 --setopt=install_weak_deps=False
|
||||
install -y {{ bootstrap_rocky_extra }}
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
27
roles/bootstrap/tasks/ubuntu.yml
Normal file
27
roles/bootstrap/tasks/ubuntu.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: Bootstrap Ubuntu System
|
||||
vars:
|
||||
bootstrap_ubuntu_release: >-
|
||||
{{ 'plucky' if bootstrap_os_key == 'ubuntu' else 'noble' }}
|
||||
bootstrap_ubuntu_base_list: "{{ lookup('vars', bootstrap_var_key).base | default([]) }}"
|
||||
bootstrap_ubuntu_extra_list: "{{ lookup('vars', bootstrap_var_key).extra | default([]) }}"
|
||||
bootstrap_ubuntu_base: "{{ (bootstrap_ubuntu_base_list | difference(bootstrap_guest_agent_remove_packages)) | join(',') }}"
|
||||
bootstrap_ubuntu_extra: >-
|
||||
{{
|
||||
(
|
||||
(bootstrap_ubuntu_extra_list | difference(bootstrap_guest_agent_remove_packages))
|
||||
+ bootstrap_guest_agent_packages
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- >-
|
||||
debootstrap --include={{ bootstrap_ubuntu_base }}
|
||||
{{ bootstrap_ubuntu_release }} /mnt http://archive.ubuntu.com/ubuntu/
|
||||
- ln -sf /run/NetworkManager/resolv.conf /mnt/etc/resolv.conf
|
||||
- arch-chroot /mnt sed -i '1s|$| universe|' /etc/apt/sources.list
|
||||
- arch-chroot /mnt apt update
|
||||
- "arch-chroot /mnt apt install -y {{ bootstrap_ubuntu_extra }}"
|
||||
register: bootstrap_result
|
||||
changed_when: bootstrap_result.rc == 0
|
||||
@@ -1,5 +1,7 @@
|
||||
almalinux:
|
||||
---
|
||||
bootstrap_almalinux:
|
||||
- bind-utils
|
||||
- cryptsetup
|
||||
- dbus-daemon
|
||||
- dhcp-client
|
||||
- efibootmgr
|
||||
@@ -13,19 +15,20 @@ almalinux:
|
||||
- nfs-utils
|
||||
- nfsv4-client-utils
|
||||
- mtr
|
||||
- open-vm-tools
|
||||
- ppp
|
||||
- shim
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- wget
|
||||
- zram-generator
|
||||
- zstd
|
||||
|
||||
archlinux:
|
||||
bootstrap_archlinux:
|
||||
- base
|
||||
- btrfs-progs
|
||||
- cronie
|
||||
- cryptsetup
|
||||
- dhcpcd
|
||||
- efibootmgr
|
||||
- fastfetch
|
||||
@@ -43,27 +46,28 @@ archlinux:
|
||||
- ncdu
|
||||
- networkmanager
|
||||
- nfs-utils
|
||||
- open-vm-tools
|
||||
- openssh
|
||||
- ppp
|
||||
- prometheus-node-exporter
|
||||
- python-psycopg2
|
||||
- qemu-guest-agent
|
||||
- reflector
|
||||
- rsync
|
||||
- sudo
|
||||
- tldr
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- wireguard-tools
|
||||
- zram-generator
|
||||
|
||||
debian11:
|
||||
bootstrap_debian11:
|
||||
base:
|
||||
- apparmor-utils
|
||||
- btrfs-progs
|
||||
- chrony
|
||||
- cron
|
||||
- cryptsetup
|
||||
- cryptsetup-initramfs
|
||||
- gnupg
|
||||
- grub-efi
|
||||
- grub-efi-amd64-signed
|
||||
@@ -93,7 +97,6 @@ debian11:
|
||||
- ncdu
|
||||
- neofetch
|
||||
- network-manager
|
||||
- open-vm-tools
|
||||
- python-is-python3
|
||||
- ripgrep
|
||||
- rsync
|
||||
@@ -102,14 +105,17 @@ debian11:
|
||||
- syslog-ng
|
||||
- tcpd
|
||||
- tldr
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- wget
|
||||
- zstd
|
||||
|
||||
debian12:
|
||||
bootstrap_debian12:
|
||||
base:
|
||||
- btrfs-progs
|
||||
- cron
|
||||
- cryptsetup
|
||||
- cryptsetup-initramfs
|
||||
- gnupg
|
||||
- grub-efi
|
||||
- grub-efi-amd64-signed
|
||||
@@ -140,7 +146,6 @@ debian12:
|
||||
- neofetch
|
||||
- net-tools
|
||||
- network-manager
|
||||
- open-vm-tools
|
||||
- openssh-server
|
||||
- python-is-python3
|
||||
- python3
|
||||
@@ -153,14 +158,17 @@ debian12:
|
||||
- systemd-zram-generator
|
||||
- tcpd
|
||||
- tldr
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- wget
|
||||
- zstd
|
||||
|
||||
debian13:
|
||||
bootstrap_debian13:
|
||||
base:
|
||||
- btrfs-progs
|
||||
- cron
|
||||
- cryptsetup
|
||||
- cryptsetup-initramfs
|
||||
- gnupg
|
||||
- grub-efi
|
||||
- grub-efi-amd64-signed
|
||||
@@ -191,7 +199,6 @@ debian13:
|
||||
- ncdu
|
||||
- net-tools
|
||||
- network-manager
|
||||
- open-vm-tools
|
||||
- openssh-server
|
||||
- python-is-python3
|
||||
- python3
|
||||
@@ -202,15 +209,17 @@ debian13:
|
||||
- syslog-ng
|
||||
- systemd-zram-generator
|
||||
- tcpd
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- wget
|
||||
- zstd
|
||||
|
||||
fedora:
|
||||
bootstrap_fedora:
|
||||
- bat
|
||||
- bind-utils
|
||||
- btrfs-progs
|
||||
- cronie
|
||||
- cryptsetup
|
||||
- dhcp-client
|
||||
- duf
|
||||
- efibootmgr
|
||||
@@ -229,20 +238,21 @@ fedora:
|
||||
- nc
|
||||
- nfs-utils
|
||||
- nfsv4-client-utils
|
||||
- open-vm-tools
|
||||
- polkit
|
||||
- ppp
|
||||
- ripgrep
|
||||
- shim
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim-default-editor
|
||||
- wget
|
||||
- zoxide
|
||||
- zram-generator
|
||||
- zstd
|
||||
|
||||
rhel8:
|
||||
bootstrap_rhel8:
|
||||
- bind-utils
|
||||
- cryptsetup
|
||||
- dhcp-client
|
||||
- efibootmgr
|
||||
- glibc-langpack-de
|
||||
@@ -255,16 +265,17 @@ rhel8:
|
||||
- mtr
|
||||
- ncurses-term
|
||||
- nfs-utils
|
||||
- open-vm-tools
|
||||
- policycoreutils-python-utils
|
||||
- python39
|
||||
- shim
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- zstd
|
||||
|
||||
rhel9:
|
||||
bootstrap_rhel9:
|
||||
- bind-utils
|
||||
- cryptsetup
|
||||
- dhcp-client
|
||||
- efibootmgr
|
||||
- glibc-langpack-de
|
||||
@@ -277,17 +288,18 @@ rhel9:
|
||||
- mtr
|
||||
- ncurses-term
|
||||
- nfs-utils
|
||||
- open-vm-tools
|
||||
- policycoreutils-python-utils
|
||||
- python
|
||||
- shim
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- zram-generator
|
||||
- zstd
|
||||
|
||||
rhel10:
|
||||
bootstrap_rhel10:
|
||||
- bind-utils
|
||||
- cryptsetup
|
||||
- efibootmgr
|
||||
- glibc-langpack-de
|
||||
- glibc-langpack-en
|
||||
@@ -299,17 +311,18 @@ rhel10:
|
||||
- mtr
|
||||
- ncurses-term
|
||||
- nfs-utils
|
||||
- open-vm-tools
|
||||
- policycoreutils-python-utils
|
||||
- python
|
||||
- shim
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- vim
|
||||
- zram-generator
|
||||
- zstd
|
||||
|
||||
rocky:
|
||||
bootstrap_rocky:
|
||||
- bind-utils
|
||||
- cryptsetup
|
||||
- dbus-daemon
|
||||
- dhcp-client
|
||||
- efibootmgr
|
||||
@@ -323,21 +336,23 @@ rocky:
|
||||
- nc
|
||||
- nfs-utils
|
||||
- nfsv4-client-utils
|
||||
- open-vm-tools
|
||||
- ppp
|
||||
- shim
|
||||
- telnet
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- util-linux-core
|
||||
- vim
|
||||
- wget
|
||||
- zram-generator
|
||||
- zstd
|
||||
|
||||
ubuntu:
|
||||
bootstrap_ubuntu:
|
||||
base:
|
||||
- btrfs-progs
|
||||
- cron
|
||||
- cryptsetup
|
||||
- cryptsetup-initramfs
|
||||
- gnupg
|
||||
- grub-efi
|
||||
- grub-efi-amd64-signed
|
||||
@@ -372,7 +387,6 @@ ubuntu:
|
||||
- ncurses-term
|
||||
- net-tools
|
||||
- network-manager
|
||||
- open-vm-tools
|
||||
- openssh-server
|
||||
- python-is-python3
|
||||
- python3
|
||||
@@ -386,6 +400,7 @@ ubuntu:
|
||||
- tcpd
|
||||
- tldr
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- traceroute
|
||||
- util-linux-extra
|
||||
- vim
|
||||
@@ -394,10 +409,12 @@ ubuntu:
|
||||
- zoxide
|
||||
- zstd
|
||||
|
||||
ubuntu-lts:
|
||||
bootstrap_ubuntu_lts:
|
||||
base:
|
||||
- btrfs-progs
|
||||
- cron
|
||||
- cryptsetup
|
||||
- cryptsetup-initramfs
|
||||
- gnupg
|
||||
- grub-efi
|
||||
- grub-efi-amd64-signed
|
||||
@@ -432,7 +449,6 @@ ubuntu-lts:
|
||||
- ncurses-term
|
||||
- net-tools
|
||||
- network-manager
|
||||
- open-vm-tools
|
||||
- openssh-server
|
||||
- python-is-python3
|
||||
- python3
|
||||
@@ -446,6 +462,7 @@ ubuntu-lts:
|
||||
- tcpd
|
||||
- tldr
|
||||
- tmux
|
||||
- tpm2-tools
|
||||
- traceroute
|
||||
- util-linux-extra
|
||||
- vim
|
||||
|
||||
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 in ["almalinux", "rhel9", "rhel10", "rocky"]
|
||||
ansible.builtin.command: arch-chroot /mnt /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: >
|
||||
arch-chroot /mnt systemctl mask nftables 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,250 +1,14 @@
|
||||
---
|
||||
- name: Configurationg System for CIS conformity
|
||||
block:
|
||||
- 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: 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
|
||||
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
|
||||
|
||||
# - 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 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: "nullok"
|
||||
loop:
|
||||
- /mnt/etc/pam.d/system-auth
|
||||
- /mnt/etc/pam.d/password-auth
|
||||
|
||||
- name: Configure System Cryptography Policy
|
||||
when: os in ["almalinux", "rhel9", "rhel10", "rocky"]
|
||||
ansible.builtin.command: arch-chroot /mnt /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1
|
||||
register: crypto_policy_result
|
||||
changed_when: "'Setting system-wide crypto-policies to' in crypto_policy_result.stdout"
|
||||
|
||||
- name: Mask Systemd Services
|
||||
ansible.builtin.command: >
|
||||
arch-chroot /mnt systemctl mask nftables bluetooth rpcbind
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
|
||||
- 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: touch
|
||||
mode: "0600"
|
||||
loop:
|
||||
- /mnt/etc/at.deny
|
||||
- /mnt/etc/cron.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", "rhel10", "rocky"] else "bash.bashrc" }}',
|
||||
content: umask 077,
|
||||
}
|
||||
- {
|
||||
path: '/mnt/etc/{{ "bashrc" if os in ["almalinux", "fedora", "rhel8", "rhel9", "rhel10", "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", "rhel10"] else None,
|
||||
{ "path": "/mnt/usr/bin/" + ("fusermount3" if os in ["almalinux", "archlinux", "debian12", "fedora", "rhel9", "rhel10", "rocky"]
|
||||
else "fusermount"), "mode": "755" },
|
||||
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" }
|
||||
] | reject("none") }}
|
||||
|
||||
- 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: "1" }
|
||||
- { 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 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
|
||||
- name: Include CIS hardening tasks
|
||||
ansible.builtin.include_tasks: "{{ cis_task }}"
|
||||
loop:
|
||||
- modules.yml
|
||||
- sysctl.yml
|
||||
- auth.yml
|
||||
- crypto.yml
|
||||
- files.yml
|
||||
- security_lines.yml
|
||||
- permissions.yml
|
||||
- sshd.yml
|
||||
loop_control:
|
||||
loop_var: cis_task
|
||||
|
||||
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 legacy 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"
|
||||
37
roles/cis/tasks/permissions.yml
Normal file
37
roles/cis/tasks/permissions.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
- name: Build CIS permission targets
|
||||
ansible.builtin.set_fact:
|
||||
cis_permission_targets: >-
|
||||
{{
|
||||
[
|
||||
{ "path": "/mnt/etc/ssh/sshd_config", "mode": "0600" },
|
||||
{ "path": "/mnt/etc/cron.hourly", "mode": "0700" },
|
||||
{ "path": "/mnt/etc/cron.daily", "mode": "0700" },
|
||||
{ "path": "/mnt/etc/cron.weekly", "mode": "0700" },
|
||||
{ "path": "/mnt/etc/cron.monthly", "mode": "0700" },
|
||||
{ "path": "/mnt/etc/cron.d", "mode": "0700" },
|
||||
{ "path": "/mnt/etc/crontab", "mode": "0600" },
|
||||
{ "path": "/mnt/etc/logrotate.conf", "mode": "0644" },
|
||||
{ "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os not in ["rhel8", "rhel9", "rhel10"] else None,
|
||||
{ "path": "/mnt/usr/bin/" + ("fusermount3" if os in ["archlinux", "debian12", "fedora", "rhel9",
|
||||
"rhel10", "rocky"] else "fusermount"), "mode": "755" },
|
||||
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian11" else "write"), "mode": "755" }
|
||||
] | reject("none")
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Check CIS permission targets
|
||||
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 | default(false) else "bash.bashrc" }}', content: umask 077}
|
||||
- {path: '/mnt/etc/{{ "bashrc" if is_rhel | default(false) 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"}
|
||||
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
|
||||
113
roles/cleanup/tasks/libvirt.yml
Normal file
113
roles/cleanup/tasks/libvirt.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
- name: Remove Archiso and cloud-init disks
|
||||
when: hypervisor == "libvirt"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
block:
|
||||
- name: Set libvirt image paths
|
||||
vars:
|
||||
cleanup_libvirt_image_dir_value: "{{ vm_path | default('/var/lib/libvirt/images') }}"
|
||||
ansible.builtin.set_fact:
|
||||
cleanup_libvirt_image_dir: "{{ cleanup_libvirt_image_dir_value }}"
|
||||
cleanup_libvirt_cloudinit_path: >-
|
||||
{{ [cleanup_libvirt_image_dir_value, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||
changed_when: false
|
||||
|
||||
- name: Read current VM XML definition
|
||||
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
|
||||
@@ -1,132 +1,8 @@
|
||||
---
|
||||
- name: Unmount Disks
|
||||
vars:
|
||||
ansible_connection: ssh
|
||||
block:
|
||||
- name: Disable Swap
|
||||
ansible.builtin.command: swapoff -a
|
||||
register: swapoff_result
|
||||
changed_when: swapoff_result.rc == 0
|
||||
- name: Cleanup physical install
|
||||
when: install_type == "physical"
|
||||
ansible.builtin.include_tasks: physical.yml
|
||||
|
||||
- name: Unmount /mnt if mounted
|
||||
ansible.builtin.command: umount -R /mnt
|
||||
register: unmount_result
|
||||
changed_when: unmount_result.rc == 0
|
||||
|
||||
- name: Verify /mnt is no longer mounted
|
||||
ansible.builtin.command: grep ' /mnt ' /proc/mounts
|
||||
register: verify_unmount
|
||||
retries: 5
|
||||
delay: 5
|
||||
until: verify_unmount.rc != 0
|
||||
when: unmount_result.rc == 0
|
||||
changed_when: false
|
||||
failed_when: verify_unmount.rc not in [0, 1]
|
||||
|
||||
- name: Shutdown the VM
|
||||
community.general.shutdown:
|
||||
vars:
|
||||
ansible_connection: ssh
|
||||
|
||||
- name: Setup Cleanup
|
||||
when: hypervisor == "proxmox"
|
||||
delegate_to: localhost
|
||||
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: Start the VM
|
||||
community.general.proxmox_kvm:
|
||||
api_host: "{{ hypervisor_url }}"
|
||||
api_user: "{{ hypervisor_username }}"
|
||||
api_password: "{{ hypervisor_password }}"
|
||||
node: "{{ hypervisor_node }}"
|
||||
vmid: "{{ vm_id }}"
|
||||
state: restarted
|
||||
|
||||
- name: Clean vCenter VM
|
||||
when: hypervisor == "vmware"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
block:
|
||||
- name: Remove CD-ROM from VM in vCenter
|
||||
when: hypervisor == "vmware"
|
||||
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: Start VM in vCenter
|
||||
when: hypervisor == "vmware"
|
||||
community.vmware.vmware_guest_powerstate:
|
||||
hostname: "{{ hypervisor_url }}"
|
||||
username: "{{ hypervisor_username }}"
|
||||
password: "{{ hypervisor_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_cluster }}"
|
||||
name: "{{ hostname }}"
|
||||
state: powered-on
|
||||
|
||||
- name: Remove Archiso and cloud-init disks
|
||||
when: hypervisor == "libvirt"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
block:
|
||||
- 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
|
||||
- name: Cleanup virtual install
|
||||
when: install_type == "virtual"
|
||||
ansible.builtin.include_tasks: virtual.yml
|
||||
|
||||
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 == "proxmox"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
block:
|
||||
- name: Cleanup Setup Disks
|
||||
community.proxmox.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: Start the VM
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ hypervisor_url }}"
|
||||
api_user: "{{ hypervisor_username }}"
|
||||
api_password: "{{ hypervisor_password }}"
|
||||
node: "{{ hypervisor_node }}"
|
||||
vmid: "{{ vm_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]
|
||||
15
roles/cleanup/tasks/virtual.yml
Normal file
15
roles/cleanup/tasks/virtual.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- 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
|
||||
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 == "vmware"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
block:
|
||||
- name: Remove CD-ROM from VM in vCenter
|
||||
when: hypervisor == "vmware"
|
||||
community.vmware.vmware_guest:
|
||||
hostname: "{{ hypervisor_url }}"
|
||||
username: "{{ hypervisor_username }}"
|
||||
password: "{{ hypervisor_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_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 | default(omit) }}"
|
||||
state: absent
|
||||
failed_when: false
|
||||
|
||||
- name: Start VM in vCenter
|
||||
when: hypervisor == "vmware"
|
||||
vmware.vmware.vm_powerstate:
|
||||
hostname: "{{ hypervisor_url }}"
|
||||
username: "{{ hypervisor_username }}"
|
||||
password: "{{ hypervisor_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_datacenter }}"
|
||||
name: "{{ hostname }}"
|
||||
state: powered-on
|
||||
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 | default(false) }}"
|
||||
configuration_efi_dir: >-
|
||||
{{ "/boot/efi" if os | lower in ["ubuntu", "ubuntu-lts"] else "/boot" }}
|
||||
configuration_bootloader_id: >-
|
||||
{{ "ubuntu" if os | lower in ["ubuntu", "ubuntu-lts"] else os }}
|
||||
configuration_efi_vendor: >-
|
||||
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }}
|
||||
configuration_efibootmgr_cmd: >-
|
||||
/usr/sbin/efibootmgr -c -L '{{ os }}' -d "{{ install_drive }}" -p 1
|
||||
-l '\efi\EFI\{{ configuration_efi_vendor }}\shimx64.efi'
|
||||
configuration_grub_cmd: >-
|
||||
/usr/sbin/grub-install --target=x86_64-efi
|
||||
--efi-directory={{ configuration_efi_dir }}
|
||||
--bootloader-id={{ configuration_bootloader_id }}
|
||||
configuration_bootloader_cmd: >-
|
||||
{{ configuration_efibootmgr_cmd if configuration_use_efibootmgr else configuration_grub_cmd }}
|
||||
ansible.builtin.command: "arch-chroot /mnt {{ configuration_bootloader_cmd }}"
|
||||
register: configuration_bootloader_result
|
||||
changed_when: configuration_bootloader_result.rc == 0
|
||||
|
||||
- name: Ensure lvm2 for non btrfs filesystems
|
||||
when: os | lower == "archlinux" and filesystem != "btrfs"
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/mkinitcpio.conf
|
||||
regexp: "^(HOOKS=.*block)(?!.*lvm2)(.*)"
|
||||
line: '\1 lvm2\2'
|
||||
backrefs: true
|
||||
|
||||
- name: Regenerate initramfs
|
||||
vars:
|
||||
configuration_initramfs_cmd: >-
|
||||
{{
|
||||
'/usr/sbin/mkinitcpio -P'
|
||||
if os | lower == "archlinux"
|
||||
else (
|
||||
'/usr/sbin/update-initramfs -u -k all'
|
||||
if is_debian | default(false)
|
||||
else '/usr/bin/dracut --regenerate-all --force'
|
||||
)
|
||||
}}
|
||||
ansible.builtin.command: "arch-chroot /mnt {{ configuration_initramfs_cmd }}"
|
||||
register: configuration_initramfs_result
|
||||
changed_when: configuration_initramfs_result.rc == 0
|
||||
|
||||
- name: Generate grub config
|
||||
vars:
|
||||
configuration_efi_vendor: >-
|
||||
{{ "redhat" if os | lower in ["rhel8", "rhel9", "rhel10"] else os | lower }}
|
||||
configuration_grub_cfg_cmd: >-
|
||||
{{ '/usr/sbin/grub2-mkconfig -o /boot/efi/EFI/' + configuration_efi_vendor + '/grub.cfg'
|
||||
if is_rhel | default(false)
|
||||
else '/usr/sbin/grub-mkconfig -o ' + (
|
||||
'/boot/efi/EFI/ubuntu/grub.cfg'
|
||||
if os | lower in ["ubuntu", "ubuntu-lts"]
|
||||
else '/boot/grub/grub.cfg'
|
||||
)
|
||||
}}
|
||||
ansible.builtin.command: "arch-chroot /mnt {{ configuration_grub_cfg_cmd }}"
|
||||
register: configuration_grub_result
|
||||
changed_when: configuration_grub_result.rc == 0
|
||||
365
roles/configuration/tasks/encryption.yml
Normal file
365
roles/configuration/tasks/encryption.yml
Normal file
@@ -0,0 +1,365 @@
|
||||
---
|
||||
- name: Configure disk encryption
|
||||
when: partitioning_luks_enabled | default(luks_enabled | default(false)) | bool
|
||||
vars:
|
||||
configuration_luks_passphrase_effective: >-
|
||||
{{ (partitioning_luks_passphrase | default(luks_passphrase | default(''))) | string }}
|
||||
block:
|
||||
- name: Set LUKS configuration facts
|
||||
vars:
|
||||
configuration_luks_mapper_name_value: >-
|
||||
{{
|
||||
partitioning_luks_mapper_name
|
||||
| default(luks_mapper_name | default('SYSTEM_DECRYPTED'))
|
||||
}}
|
||||
configuration_luks_device_value: >-
|
||||
{{
|
||||
partitioning_luks_device
|
||||
| default(
|
||||
install_drive
|
||||
~ (partitioning_main_partition_suffix | default(2) | string)
|
||||
)
|
||||
}}
|
||||
configuration_luks_tpm2_pcrs_raw: >-
|
||||
{{ partitioning_luks_tpm2_pcrs | default(luks_tpm2_pcrs | default('')) }}
|
||||
configuration_luks_tpm2_pcrs_effective_value: >-
|
||||
{{
|
||||
(
|
||||
configuration_luks_tpm2_pcrs_raw
|
||||
if configuration_luks_tpm2_pcrs_raw is string
|
||||
else (configuration_luks_tpm2_pcrs_raw | map('string') | join('+'))
|
||||
)
|
||||
| string
|
||||
| replace(',', '+')
|
||||
| regex_replace('\\s+', '')
|
||||
| regex_replace('^\\+|\\+$', '')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_luks_mapper_name: "{{ configuration_luks_mapper_name_value }}"
|
||||
configuration_luks_uuid: "{{ partitioning_luks_uuid | default('') }}"
|
||||
configuration_luks_device: "{{ configuration_luks_device_value }}"
|
||||
configuration_luks_options: >-
|
||||
{{ partitioning_luks_options | default(luks_options | default('discard,tries=3')) }}
|
||||
configuration_luks_auto_method: >-
|
||||
{{
|
||||
(partitioning_luks_auto_decrypt | default(luks_auto_decrypt | default(true)) | bool)
|
||||
| ternary(
|
||||
partitioning_luks_auto_decrypt_method | default(luks_auto_decrypt_method | default('tpm2')),
|
||||
'manual'
|
||||
)
|
||||
}}
|
||||
configuration_luks_tpm2_device: >-
|
||||
{{ partitioning_luks_tpm2_device | default(luks_tpm2_device | default('auto')) }}
|
||||
configuration_luks_tpm2_pcrs: "{{ configuration_luks_tpm2_pcrs_raw }}"
|
||||
configuration_luks_tpm2_pcrs_effective: "{{ configuration_luks_tpm2_pcrs_effective_value }}"
|
||||
configuration_luks_keyfile_path: >-
|
||||
/etc/cryptsetup-keys.d/{{ configuration_luks_mapper_name_value }}.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_effective | length > 0
|
||||
fail_msg: luks_passphrase (or partitioning_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:
|
||||
configuration_luks_keyfile_in_use_value: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
||||
configuration_luks_option_list_value: >-
|
||||
{{
|
||||
(configuration_luks_options | trim).split(',')
|
||||
if configuration_luks_options | trim | length > 0
|
||||
else []
|
||||
}}
|
||||
configuration_luks_tpm2_option_list_value: >-
|
||||
{{
|
||||
(configuration_luks_auto_method == 'tpm2')
|
||||
| ternary(
|
||||
['tpm2-device=' + configuration_luks_tpm2_device]
|
||||
+ (['tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective]
|
||||
if configuration_luks_tpm2_pcrs_effective | length > 0 else []),
|
||||
[]
|
||||
)
|
||||
}}
|
||||
configuration_luks_crypttab_keyfile_value: >-
|
||||
{{ configuration_luks_keyfile_path if configuration_luks_keyfile_in_use_value else 'none' }}
|
||||
configuration_luks_crypttab_options_value: >-
|
||||
{{
|
||||
(['luks'] + configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value)
|
||||
| join(',')
|
||||
}}
|
||||
configuration_luks_rd_options_value: >-
|
||||
{{ (configuration_luks_option_list_value + configuration_luks_tpm2_option_list_value) | join(',') }}
|
||||
configuration_luks_kernel_args_value: >-
|
||||
{{
|
||||
(
|
||||
['rd.luks.name=' + configuration_luks_uuid + '=' + configuration_luks_mapper_name]
|
||||
+ (
|
||||
['rd.luks.options=' + configuration_luks_uuid + '=' + configuration_luks_rd_options_value]
|
||||
if configuration_luks_rd_options_value | length > 0 else []
|
||||
)
|
||||
+ (
|
||||
['rd.luks.key=' + configuration_luks_uuid + '=' + configuration_luks_keyfile_path]
|
||||
if configuration_luks_keyfile_in_use_value else []
|
||||
)
|
||||
) | join(' ')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_luks_keyfile_in_use: "{{ configuration_luks_keyfile_in_use_value }}"
|
||||
configuration_luks_option_list: "{{ configuration_luks_option_list_value }}"
|
||||
configuration_luks_tpm2_option_list: "{{ configuration_luks_tpm2_option_list_value }}"
|
||||
configuration_luks_crypttab_keyfile: "{{ configuration_luks_crypttab_keyfile_value }}"
|
||||
configuration_luks_crypttab_options: "{{ configuration_luks_crypttab_options_value }}"
|
||||
configuration_luks_rd_options: "{{ configuration_luks_rd_options_value }}"
|
||||
configuration_luks_kernel_args: "{{ configuration_luks_kernel_args_value }}"
|
||||
|
||||
- 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 | default(false)
|
||||
- configuration_luks_keyfile_in_use
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/cryptsetup-initramfs/conf-hook
|
||||
regexp: '^KEYFILE_PATTERN='
|
||||
line: 'KEYFILE_PATTERN=/etc/cryptsetup-keys.d/*.key'
|
||||
create: true
|
||||
mode: "0644"
|
||||
|
||||
- name: Configure mkinitcpio hooks for LUKS
|
||||
when: os | lower == 'archlinux'
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/mkinitcpio.conf
|
||||
regexp: '^HOOKS='
|
||||
line: >-
|
||||
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
|
||||
block sd-encrypt lvm2 filesystems fsck)
|
||||
|
||||
- name: Read mkinitcpio configuration
|
||||
when: os | lower == 'archlinux'
|
||||
ansible.builtin.slurp:
|
||||
src: /mnt/etc/mkinitcpio.conf
|
||||
register: configuration_mkinitcpio_slurp
|
||||
|
||||
- name: Build mkinitcpio FILES list
|
||||
when: os | lower == 'archlinux'
|
||||
vars:
|
||||
configuration_mkinitcpio_files_list_value: >-
|
||||
{{
|
||||
(
|
||||
configuration_mkinitcpio_slurp.content | b64decode
|
||||
| regex_findall('^FILES=\\(([^)]*)\\)', multiline=True)
|
||||
| default([])
|
||||
| first
|
||||
| default('')
|
||||
).split()
|
||||
}}
|
||||
configuration_mkinitcpio_files_list_new_value: >-
|
||||
{{
|
||||
(
|
||||
(configuration_mkinitcpio_files_list_value + [configuration_luks_keyfile_path])
|
||||
if configuration_luks_keyfile_in_use
|
||||
else (
|
||||
configuration_mkinitcpio_files_list_value
|
||||
| reject('equalto', configuration_luks_keyfile_path)
|
||||
| list
|
||||
)
|
||||
)
|
||||
| unique
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_mkinitcpio_files_list_new: "{{ configuration_mkinitcpio_files_list_new_value }}"
|
||||
|
||||
- name: Configure mkinitcpio FILES list
|
||||
when: os | lower == 'archlinux'
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/mkinitcpio.conf
|
||||
regexp: '^FILES='
|
||||
line: >-
|
||||
FILES=({{
|
||||
configuration_mkinitcpio_files_list_new | join(' ')
|
||||
}})
|
||||
|
||||
- name: Ensure dracut config directory exists
|
||||
when: is_rhel | default(false)
|
||||
ansible.builtin.file:
|
||||
path: /mnt/etc/dracut.conf.d
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure dracut for LUKS
|
||||
when: is_rhel | default(false)
|
||||
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 | default(false)
|
||||
ansible.builtin.slurp:
|
||||
src: /mnt/etc/kernel/cmdline
|
||||
register: configuration_kernel_cmdline_slurp
|
||||
|
||||
- name: Build kernel cmdline with LUKS args
|
||||
when: is_rhel | default(false)
|
||||
vars:
|
||||
configuration_kernel_cmdline_current_value: >-
|
||||
{{ configuration_kernel_cmdline_slurp.content | b64decode | trim }}
|
||||
configuration_kernel_cmdline_list_value: >-
|
||||
{{
|
||||
configuration_kernel_cmdline_current_value.split()
|
||||
if configuration_kernel_cmdline_current_value | length > 0 else []
|
||||
}}
|
||||
configuration_kernel_cmdline_filtered_value: >-
|
||||
{{
|
||||
configuration_kernel_cmdline_list_value
|
||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||
| list
|
||||
}}
|
||||
configuration_kernel_cmdline_new_value: >-
|
||||
{{
|
||||
(configuration_kernel_cmdline_filtered_value + configuration_luks_kernel_args.split())
|
||||
| unique
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_kernel_cmdline_new: "{{ configuration_kernel_cmdline_new_value }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Write kernel cmdline with LUKS args
|
||||
when: is_rhel | default(false)
|
||||
ansible.builtin.copy:
|
||||
dest: /mnt/etc/kernel/cmdline
|
||||
mode: "0644"
|
||||
content: "{{ configuration_kernel_cmdline_new }}\n"
|
||||
|
||||
- name: Find BLS entries
|
||||
when: is_rhel | default(false)
|
||||
ansible.builtin.find:
|
||||
paths: /mnt/boot/loader/entries
|
||||
patterns: "*.conf"
|
||||
register: configuration_kernel_bls_entries
|
||||
changed_when: false
|
||||
|
||||
- name: Update BLS options with LUKS args
|
||||
when:
|
||||
- is_rhel | default(false)
|
||||
- configuration_kernel_bls_entries.files | length > 0
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ item.path }}"
|
||||
regexp: '^options '
|
||||
line: "options {{ configuration_kernel_cmdline_new }}"
|
||||
loop: "{{ configuration_kernel_bls_entries.files }}"
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
|
||||
- name: Read grub defaults
|
||||
when: not is_rhel | default(false)
|
||||
ansible.builtin.slurp:
|
||||
src: /mnt/etc/default/grub
|
||||
register: configuration_grub_slurp
|
||||
|
||||
- name: Build grub command lines with LUKS args
|
||||
when: not is_rhel | default(false)
|
||||
vars:
|
||||
configuration_grub_content_value: "{{ configuration_grub_slurp.content | b64decode }}"
|
||||
configuration_grub_cmdline_linux_value: >-
|
||||
{{
|
||||
configuration_grub_content_value
|
||||
| regex_findall('^GRUB_CMDLINE_LINUX=\"(.*)\"', multiline=True)
|
||||
| default([])
|
||||
| first
|
||||
| default('')
|
||||
}}
|
||||
configuration_grub_cmdline_default_value: >-
|
||||
{{
|
||||
configuration_grub_content_value
|
||||
| regex_findall('^GRUB_CMDLINE_LINUX_DEFAULT=\"(.*)\"', multiline=True)
|
||||
| default([])
|
||||
| first
|
||||
| default('')
|
||||
}}
|
||||
configuration_grub_cmdline_linux_list_value: >-
|
||||
{{
|
||||
configuration_grub_cmdline_linux_value.split()
|
||||
if configuration_grub_cmdline_linux_value | length > 0 else []
|
||||
}}
|
||||
configuration_grub_cmdline_default_list_value: >-
|
||||
{{
|
||||
configuration_grub_cmdline_default_value.split()
|
||||
if configuration_grub_cmdline_default_value | length > 0 else []
|
||||
}}
|
||||
configuration_luks_kernel_args_list_value: "{{ configuration_luks_kernel_args.split() }}"
|
||||
configuration_grub_cmdline_linux_new_value: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
configuration_grub_cmdline_linux_list_value
|
||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||
| list
|
||||
)
|
||||
+ configuration_luks_kernel_args_list_value
|
||||
)
|
||||
| unique
|
||||
| join(' ')
|
||||
}}
|
||||
configuration_grub_cmdline_default_new_value: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
configuration_grub_cmdline_default_list_value
|
||||
| reject('match', '^rd\\.luks\\.(name|options|key)=' ~ configuration_luks_uuid ~ '=')
|
||||
| list
|
||||
)
|
||||
+ configuration_luks_kernel_args_list_value
|
||||
)
|
||||
| unique
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_grub_content: "{{ configuration_grub_content_value }}"
|
||||
configuration_grub_cmdline_linux: "{{ configuration_grub_cmdline_linux_value }}"
|
||||
configuration_grub_cmdline_default: "{{ configuration_grub_cmdline_default_value }}"
|
||||
configuration_grub_cmdline_linux_new: "{{ configuration_grub_cmdline_linux_new_value }}"
|
||||
configuration_grub_cmdline_default_new: "{{ configuration_grub_cmdline_default_new_value }}"
|
||||
|
||||
- name: Update GRUB_CMDLINE_LINUX_DEFAULT for LUKS
|
||||
when: not is_rhel | default(false)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/default/grub
|
||||
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT='
|
||||
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ configuration_grub_cmdline_default_new }}"'
|
||||
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=(partitioning_luks_keyfile_size | default(luks_keyfile_size | default(64)) | 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_effective }}"
|
||||
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.changed | default(false) | 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=(partitioning_luks_keyfile_size | default(luks_keyfile_size | default(64)) | 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_effective }}"
|
||||
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_effective }}"
|
||||
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_effective]
|
||||
if configuration_luks_tpm2_pcrs_effective | length > 0 else [])
|
||||
+ [configuration_luks_device]
|
||||
}}
|
||||
configuration_luks_enroll_chroot_args: "{{ ['arch-chroot', '/mnt'] + configuration_luks_enroll_args }}"
|
||||
ansible.builtin.command:
|
||||
argv: "{{ configuration_luks_enroll_chroot_args }}"
|
||||
register: configuration_luks_tpm2_enroll_chroot
|
||||
changed_when: configuration_luks_tpm2_enroll_chroot.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Retry TPM2 enrollment in installer environment
|
||||
when:
|
||||
- (configuration_luks_tpm2_enroll_chroot.rc | default(1)) != 0
|
||||
vars:
|
||||
configuration_luks_enroll_args: >-
|
||||
{{
|
||||
[
|
||||
'/usr/bin/systemd-cryptenroll',
|
||||
'--tpm2-device=' + configuration_luks_tpm2_device,
|
||||
'--tpm2-with-pin=false',
|
||||
'--wipe-slot=tpm2',
|
||||
'--unlock-key-file=' + configuration_luks_tpm2_passphrase_tempfile.path
|
||||
]
|
||||
+ (['--tpm2-pcrs=' + configuration_luks_tpm2_pcrs_effective]
|
||||
if configuration_luks_tpm2_pcrs_effective | length > 0 else [])
|
||||
+ [configuration_luks_device]
|
||||
}}
|
||||
ansible.builtin.command:
|
||||
argv: "{{ configuration_luks_enroll_args }}"
|
||||
register: configuration_luks_tpm2_enroll_host
|
||||
changed_when: configuration_luks_tpm2_enroll_host.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Validate TPM2 enrollment succeeded
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- >-
|
||||
(configuration_luks_tpm2_enroll_chroot.rc | default(1)) == 0
|
||||
or (configuration_luks_tpm2_enroll_host.rc | default(1)) == 0
|
||||
fail_msg: >-
|
||||
TPM2 enrollment failed.
|
||||
chroot rc={{ configuration_luks_tpm2_enroll_chroot.rc | default('n/a') }},
|
||||
host rc={{ configuration_luks_tpm2_enroll_host.rc | default('n/a') }},
|
||||
chroot stderr={{ configuration_luks_tpm2_enroll_chroot.stderr | default('') }},
|
||||
host stderr={{ configuration_luks_tpm2_enroll_host.stderr | default('') }}
|
||||
rescue:
|
||||
- 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
|
||||
69
roles/configuration/tasks/extras.yml
Normal file
69
roles/configuration/tasks/extras.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
- name: Append vim configurations to vimrc
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ '/mnt/etc/vim/vimrc' if is_debian | default(false) 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 | lower not in ['debian11', 'rhel8']
|
||||
ansible.builtin.copy:
|
||||
dest: /mnt/etc/systemd/zram-generator.conf
|
||||
content: |
|
||||
[zram0]
|
||||
zram-size = ram / 2
|
||||
compression-algorithm = zstd
|
||||
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 | lower in ["rhel8", "rhel9", "rhel10"]
|
||||
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: Remove deprecated attr2 and disable large extent
|
||||
when: os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"] and 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 in ["rhel8", "rhel9", "rhel10"]
|
||||
vars:
|
||||
configuration_fstab_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'
|
||||
}}
|
||||
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 in ["rhel8", "rhel9", "rhel10"] and hypervisor == '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
|
||||
109
roles/configuration/tasks/grub.yml
Normal file
109
roles/configuration/tasks/grub.yml
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
- name: Configure grub
|
||||
when: not is_rhel | default(false)
|
||||
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: Ensure grub defaults file exists for RHEL-based systems
|
||||
when: is_rhel | default(false)
|
||||
block:
|
||||
- name: Build RHEL kernel command line defaults
|
||||
vars:
|
||||
configuration_grub_root_uuid_value: >-
|
||||
{{
|
||||
(
|
||||
partitioning_main_uuid.stdout
|
||||
if (filesystem | lower) == 'btrfs'
|
||||
else (partitioning_uuid_root | default([]) | first | default(''))
|
||||
)
|
||||
| default('')
|
||||
| trim
|
||||
}}
|
||||
configuration_grub_lvm_args_value: >-
|
||||
{{
|
||||
['resume=/dev/mapper/sys-swap', 'rd.lvm.lv=sys/root', 'rd.lvm.lv=sys/swap']
|
||||
if (filesystem | lower) != 'btrfs'
|
||||
else []
|
||||
}}
|
||||
configuration_grub_root_flags_value: >-
|
||||
{{ ['rootflags=subvol=@'] if (filesystem | lower) == 'btrfs' else [] }}
|
||||
configuration_grub_cmdline_linux_base_value: >-
|
||||
{{
|
||||
(['crashkernel=auto'] + configuration_grub_lvm_args_value)
|
||||
| join(' ')
|
||||
}}
|
||||
configuration_grub_kernel_cmdline_base_value: >-
|
||||
{{
|
||||
(
|
||||
(['root=UUID=' + configuration_grub_root_uuid_value]
|
||||
if configuration_grub_root_uuid_value | length > 0 else [])
|
||||
+ ['ro', 'crashkernel=auto']
|
||||
+ configuration_grub_lvm_args_value
|
||||
+ configuration_grub_root_flags_value
|
||||
)
|
||||
| join(' ')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_grub_cmdline_linux_base: "{{ configuration_grub_cmdline_linux_base_value }}"
|
||||
configuration_kernel_cmdline_base: "{{ configuration_grub_kernel_cmdline_base_value }}"
|
||||
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=5
|
||||
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 }}"
|
||||
88
roles/configuration/tasks/locales.yml
Normal file
88
roles/configuration/tasks/locales.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
- name: Set local timezone
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- systemctl daemon-reload
|
||||
- arch-chroot /mnt ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
|
||||
register: configuration_timezone_result
|
||||
changed_when: configuration_timezone_result.rc == 0
|
||||
|
||||
- name: Setup locales
|
||||
block:
|
||||
- name: Configure locale.gen
|
||||
when: not is_rhel | default(false)
|
||||
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 | default(false)
|
||||
ansible.builtin.command: arch-chroot /mnt /usr/sbin/locale-gen
|
||||
register: configuration_locale_result
|
||||
changed_when: configuration_locale_result.rc == 0
|
||||
|
||||
- name: Set hostname
|
||||
vars:
|
||||
configuration_hostname_fqdn: >-
|
||||
{{
|
||||
hostname
|
||||
if '.' in hostname
|
||||
else (
|
||||
hostname + '.' + vm_dns_search
|
||||
if vm_dns_search is defined and vm_dns_search | length
|
||||
else hostname
|
||||
)
|
||||
}}
|
||||
ansible.builtin.copy:
|
||||
content: "{{ configuration_hostname_fqdn }}"
|
||||
dest: /mnt/etc/hostname
|
||||
mode: "0644"
|
||||
|
||||
- name: Add host entry to /etc/hosts
|
||||
vars:
|
||||
configuration_hostname_fqdn: >-
|
||||
{{
|
||||
hostname
|
||||
if '.' in hostname
|
||||
else (
|
||||
hostname + '.' + vm_dns_search
|
||||
if vm_dns_search is defined and vm_dns_search | length
|
||||
else hostname
|
||||
)
|
||||
}}
|
||||
configuration_hostname_short: "{{ hostname.split('.')[0] }}"
|
||||
configuration_hostname_entries: >-
|
||||
{{ [configuration_hostname_fqdn, configuration_hostname_short] | unique | join(' ') }}
|
||||
configuration_hosts_line: >-
|
||||
{{ vm_ip | default(inventory_hostname) }} {{ 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,324 +1,17 @@
|
||||
---
|
||||
- name: Configuration
|
||||
block:
|
||||
- name: Generate fstab
|
||||
ansible.builtin.shell: genfstab -LU /mnt > /mnt/etc/fstab
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
|
||||
- name: Remove depricated attr2 and disable large extent
|
||||
when: os in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"] and 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 in ["rhel8", "rhel9", "rhel10"]
|
||||
ansible.builtin.lineinfile:
|
||||
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", "rhel10"] 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,nosuid,nodev,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', 'rhel10', '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', 'rhel10', '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 }}{% if vm_dns_search is defined and vm_dns_search | length %}.{{ vm_dns_search }}{% endif %}"
|
||||
dest: /mnt/etc/hostname
|
||||
mode: "0644"
|
||||
|
||||
- name: Add host entry to /etc/hosts
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/hosts
|
||||
line: "{{ ansible_host }} {{ hostname }}{% if vm_dns_search is defined and vm_dns_search | length %} {{ hostname }}.{{ vm_dns_search }}{% endif %}"
|
||||
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', 'debian13'] 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', 'rhel10', '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", "debian13", "ubuntu", "ubuntu-lts"] %} /usr/sbin/efibootmgr
|
||||
-c -L '{{ os }}' -d "{{ install_drive }}" -p 1
|
||||
-l '\efi\EFI\{% if os | lower in ["rhel8", "rhel9", "rhel10"] %}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: Ensure lvm2 for non btrfs filesystems
|
||||
when: os | lower == "archlinux" and 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 ["debian11", "debian12", "debian13", "ubuntu", "ubuntu-lts"]
|
||||
ansible.builtin.command: arch-chroot /mnt
|
||||
{% if os | lower == "archlinux" %} /usr/sbin/mkinitcpio -P
|
||||
{% else %} /usr/bin/dracut --regenerate-all --force
|
||||
{% 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", "debian13", "ubuntu", "ubuntu-lts"] %}
|
||||
/usr/sbin/grub2-mkconfig -o /boot/efi/EFI/{% if os | lower in ["rhel8", "rhel9", "rhel10"] %}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: Extra Configuration
|
||||
block:
|
||||
- name: Append vim configurations to vimrc
|
||||
failed_when: false
|
||||
ansible.builtin.blockinfile:
|
||||
path:
|
||||
"{{ '/mnt/etc/vim/vimrc' if os | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts']
|
||||
else '/mnt/etc/vimrc' }}"
|
||||
block: |
|
||||
set encoding=utf-8
|
||||
set number
|
||||
set autoindent
|
||||
set smartindent
|
||||
set mouse=a
|
||||
insertafter: EOF
|
||||
marker: ""
|
||||
|
||||
- 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 not in ['debian11', 'rhel8']
|
||||
ansible.builtin.copy:
|
||||
dest: /mnt/etc/systemd/zram-generator.conf
|
||||
content: |
|
||||
[zram0]
|
||||
zram-size = ram / 2
|
||||
compression-algorithm = zstd
|
||||
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
|
||||
- /etc/issue.net
|
||||
|
||||
- name: Remove motd files
|
||||
when: os | lower in ["rhel8", "rhel9", "rhel10"]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /etc/motd.d/cockpit
|
||||
- /etc/motd.d/insights-client
|
||||
|
||||
- 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: set -o pipefail && 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", "debian13", "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', 'debian13', '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
|
||||
when: os | lower in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rhel10', 'rocky']
|
||||
block:
|
||||
- name: Fix SELinux by pre-labeling the filesystem before first boot
|
||||
when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rhel10', 'rocky'] and (selinux | default(true) | bool)
|
||||
ansible.builtin.command: >
|
||||
arch-chroot /mnt /sbin/setfiles -v -F
|
||||
-e /dev -e /proc -e /sys -e /run
|
||||
/etc/selinux/targeted/contexts/files/file_contexts /
|
||||
register: setfiles_result
|
||||
changed_when: setfiles_result.rc == 0
|
||||
|
||||
- name: Disable SELinux
|
||||
when: os | lower == "fedora" or not (selinux | default(true) | bool)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/selinux/config
|
||||
regexp: ^SELINUX=
|
||||
line: SELINUX=permissive
|
||||
- name: Include configuration tasks
|
||||
ansible.builtin.include_tasks: "{{ configuration_task }}"
|
||||
loop:
|
||||
- fstab.yml
|
||||
- locales.yml
|
||||
- services.yml
|
||||
- grub.yml
|
||||
- encryption.yml
|
||||
- bootloader.yml
|
||||
- extras.yml
|
||||
- network.yml
|
||||
- users.yml
|
||||
- sudo.yml
|
||||
- selinux.yml
|
||||
loop_control:
|
||||
loop_var: configuration_task
|
||||
|
||||
96
roles/configuration/tasks/network.yml
Normal file
96
roles/configuration/tasks/network.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
- 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_effective: >-
|
||||
{{ configuration_net_inf_from_facts | default(configuration_net_inf_from_ip, true) }}
|
||||
configuration_net_inf_regex: "{{ configuration_net_inf_effective | ansible.builtin.regex_escape }}"
|
||||
configuration_net_mac_from_virtualization: "{{ virtualization_mac_address | default('') }}"
|
||||
configuration_net_mac_from_facts: >-
|
||||
{{
|
||||
(
|
||||
(ansible_facts | default({})).get(configuration_net_inf_effective, {}).get('macaddress', '')
|
||||
)
|
||||
| default(
|
||||
(ansible_facts | default({})).get('ansible_' + configuration_net_inf_effective, {}).get('macaddress', ''),
|
||||
true
|
||||
)
|
||||
}}
|
||||
configuration_net_mac_from_ip: >-
|
||||
{{
|
||||
(
|
||||
configuration_ip_link.stdout
|
||||
| default('')
|
||||
| regex_findall(
|
||||
'^\\d+: ' ~ configuration_net_inf_regex ~ ':.*?link/ether\\s+([0-9A-Fa-f:]{17})',
|
||||
multiline=True
|
||||
)
|
||||
| first
|
||||
)
|
||||
| default('')
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
configuration_net_inf: "{{ configuration_net_inf_effective }}"
|
||||
configuration_net_mac: >-
|
||||
{{
|
||||
(
|
||||
configuration_net_mac_from_virtualization
|
||||
| default(configuration_net_mac_from_facts, true)
|
||||
| default(configuration_net_mac_from_ip, true)
|
||||
)
|
||||
| upper
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Validate 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: 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"
|
||||
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 | default(false)
|
||||
block:
|
||||
- name: Fix SELinux by pre-labeling the filesystem before first boot
|
||||
when: os | lower in ['almalinux', 'rhel8', 'rhel9', 'rhel10', 'rocky'] and (selinux | default(true) | bool)
|
||||
ansible.builtin.command: >
|
||||
arch-chroot /mnt /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 (selinux | default(true) | bool)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /mnt/etc/selinux/config
|
||||
regexp: ^SELINUX=
|
||||
line: SELINUX=permissive
|
||||
14
roles/configuration/tasks/services.yml
Normal file
14
roles/configuration/tasks/services.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- 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', 'debian13'] 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
|
||||
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 | default(false) 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 | default(false) else "wheel" }}
|
||||
configuration_useradd_cmd: >-
|
||||
arch-chroot /mnt /usr/sbin/useradd --create-home --user-group
|
||||
--groups {{ configuration_user_group }} {{ user_name }}
|
||||
--password {{ user_password | password_hash('sha512') }} --shell /bin/bash
|
||||
configuration_root_cmd: >-
|
||||
arch-chroot /mnt /usr/sbin/usermod --password
|
||||
'{{ 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: user_public_key is defined
|
||||
ansible.builtin.file:
|
||||
path: /mnt/home/{{ user_name }}/.ssh
|
||||
state: directory
|
||||
owner: 1000
|
||||
group: 1000
|
||||
mode: "0700"
|
||||
|
||||
- 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
|
||||
@@ -1,18 +1,33 @@
|
||||
[connection]
|
||||
id=LAN
|
||||
uuid={{ net_uuid.stdout }}
|
||||
uuid={{ configuration_net_uuid }}
|
||||
type=ethernet
|
||||
|
||||
[ethernet]
|
||||
mac-address={{ net_mac.stdout }}
|
||||
mac-address={{ configuration_net_mac }}
|
||||
|
||||
[ipv4]
|
||||
address={{ vm_ip }}/{{ vm_nms | default (24) }},{{ vm_gw }}
|
||||
dns={{ vm_dns }}
|
||||
{% if vm_dns_search is defined %}
|
||||
dns-search={{ vm_dns_search }}
|
||||
{% endif %}
|
||||
{% set dns_value = vm_dns | default('') %}
|
||||
{% set dns_list_raw = dns_value if dns_value is iterable and dns_value is not string else dns_value.split(',') %}
|
||||
{% set dns_list = dns_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||
{% set search_value = vm_dns_search | default('') %}
|
||||
{% set search_list_raw = search_value if search_value is iterable and search_value is not string else search_value.split(',') %}
|
||||
{% set search_list = search_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||
{% if vm_ip is defined and vm_ip | length %}
|
||||
address1={{ vm_ip }}/{{ vm_nms | default(24) }}{{ (',' ~ vm_gw) if (vm_gw is defined and vm_gw | length) else '' }}
|
||||
method=manual
|
||||
{% else %}
|
||||
method=auto
|
||||
{% endif %}
|
||||
{% if dns_list %}
|
||||
dns={{ dns_list | join(';') }}
|
||||
{% endif %}
|
||||
{% if dns_list or search_list %}
|
||||
ignore-auto-dns=true
|
||||
{% endif %}
|
||||
{% if search_list %}
|
||||
dns-search={{ search_list | join(';') }}
|
||||
{% endif %}
|
||||
|
||||
[ipv6]
|
||||
addr-gen-mode=stable-privacy
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
- name: Configre work environment
|
||||
become: true
|
||||
- name: Configure work environment
|
||||
become: "{{ hypervisor != 'vmware' }}"
|
||||
block:
|
||||
- name: Wait for connection
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 60
|
||||
timeout: 180
|
||||
delay: 5
|
||||
|
||||
- name: Gather facts
|
||||
@@ -13,118 +13,144 @@
|
||||
- name: Check if host is booted from the Arch install media
|
||||
ansible.builtin.stat:
|
||||
path: /run/archiso
|
||||
register: archiso_stat
|
||||
register: environment_archiso_stat
|
||||
|
||||
- name: Abort if the host is not booted from the Arch install media
|
||||
when:
|
||||
- not (custom_iso | default(false) | bool)
|
||||
- not environment_archiso_stat.stat.exists
|
||||
ansible.builtin.fail:
|
||||
msg: This host is not booted from the Arch install media!
|
||||
when: not archiso_stat.stat.exists
|
||||
|
||||
- name: Register Network Interface
|
||||
- name: Select primary Network Interface
|
||||
when: hypervisor == "vmware"
|
||||
ansible.builtin.shell: "set -o pipefail && ip l | awk -F': ' '!/lo/{print $2; exit}'"
|
||||
changed_when: interface_name.rc == 0
|
||||
register: interface_name
|
||||
ansible.builtin.set_fact:
|
||||
environment_interface_name: >-
|
||||
{{
|
||||
(
|
||||
(ansible_facts.interfaces | default(ansible_facts['ansible_interfaces'] | default([])))
|
||||
| reject('equalto', 'lo')
|
||||
| list
|
||||
| first
|
||||
)
|
||||
| default('')
|
||||
}}
|
||||
changed_when: false
|
||||
|
||||
- name: Set IP-Address
|
||||
when: hypervisor == "vmware"
|
||||
ansible.builtin.command: "ip addr replace {{ ansible_host }}/{{ vm_nms | default(24) }} dev {{ interface_name.stdout }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
when:
|
||||
- hypervisor == "vmware"
|
||||
- vm_ip is defined
|
||||
- vm_ip | length
|
||||
ansible.builtin.command: >-
|
||||
ip addr replace {{ vm_ip }}/{{ vm_nms | default(24) }}
|
||||
dev {{ environment_interface_name }}
|
||||
register: environment_ip_result
|
||||
changed_when: environment_ip_result.rc == 0
|
||||
|
||||
- name: Set Default Gateway
|
||||
when: hypervisor == "vmware"
|
||||
when:
|
||||
- hypervisor == "vmware"
|
||||
- vm_gw is defined
|
||||
- vm_gw | length
|
||||
- vm_ip is defined
|
||||
- vm_ip | length
|
||||
ansible.builtin.command: "ip route replace default via {{ vm_gw }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
register: environment_gateway_result
|
||||
changed_when: environment_gateway_result.rc == 0
|
||||
|
||||
- name: Synchronize clock via NTP
|
||||
ansible.builtin.command: timedatectl set-ntp true
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
register: environment_ntp_result
|
||||
changed_when: false
|
||||
|
||||
- name: Configure SSH for root login
|
||||
when: hypervisor == "vmware" and (vmware_ssh is defined and vmware_ssh | bool)
|
||||
block:
|
||||
- name: Allow empty passwords temporarily
|
||||
- name: Allow login
|
||||
ansible.builtin.replace:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "^#?PermitEmptyPasswords.*"
|
||||
replace: "PermitEmptyPasswords yes"
|
||||
|
||||
- name: Allow root login
|
||||
ansible.builtin.replace:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "^#?PermitRootLogin.*"
|
||||
replace: "PermitRootLogin yes"
|
||||
regexp: "{{ item.regexp }}"
|
||||
replace: "{{ item.replace }}"
|
||||
loop:
|
||||
- regexp: "^#?PermitEmptyPasswords.*"
|
||||
replace: "PermitEmptyPasswords yes"
|
||||
- regexp: "^#?PermitRootLogin.*"
|
||||
replace: "PermitRootLogin yes"
|
||||
|
||||
- name: Reload SSH service to apply changes
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: reloaded
|
||||
|
||||
- name: Set connection back to SSH
|
||||
- name: Set SSH connection for VMware
|
||||
ansible.builtin.set_fact:
|
||||
ansible_connection: ssh
|
||||
ansible_user: "root"
|
||||
ansible_password: ""
|
||||
ansible_become_password: ""
|
||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||
ansible_user: root
|
||||
|
||||
- name: Speed-up Bootstrap process
|
||||
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, rhel8, rhel9, rhel10, rocky] }
|
||||
- { name: debootstrap, os: [debian11, debian12, debian13, ubuntu, ubuntu-lts] }
|
||||
- { name: debian-archive-keyring, os: [debian11, debian12, debian13] }
|
||||
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
||||
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", "rhel10"]
|
||||
- name: Prepare installer environment
|
||||
block:
|
||||
- name: Create /iso directory
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/install/redhat/dvd
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Speed-up Bootstrap process
|
||||
when: not (custom_iso | default(false) | bool)
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/pacman.conf
|
||||
regexp: ^#ParallelDownloads =
|
||||
line: ParallelDownloads = 20
|
||||
|
||||
- name: Mount RHEL ISO
|
||||
ansible.posix.mount:
|
||||
src: "{{ '/dev/sr1' if hypervisor == 'vmware' else '/dev/sr2' }}"
|
||||
path: /usr/local/install/redhat/dvd
|
||||
fstype: iso9660
|
||||
opts: "ro,loop"
|
||||
state: mounted
|
||||
- name: Wait for pacman lock to be released
|
||||
when: not (custom_iso | default(false) | bool)
|
||||
ansible.builtin.wait_for:
|
||||
path: /var/lib/pacman/db.lck
|
||||
state: absent
|
||||
timeout: 120
|
||||
changed_when: false
|
||||
|
||||
- name: Configure RHEL Repos for installation
|
||||
when: os | lower in ["almalinux", "fedora", "rhel8", "rhel9", "rhel10", "rocky"]
|
||||
block:
|
||||
- name: Create directories for repository files and RPM GPG keys
|
||||
ansible.builtin.file:
|
||||
path: /etc/yum.repos.d
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Setup Pacman
|
||||
when:
|
||||
- not (custom_iso | default(false) | bool)
|
||||
- "'os' not in item or os in item.os"
|
||||
community.general.pacman:
|
||||
update_cache: true
|
||||
force: true
|
||||
name: "{{ item.name }}"
|
||||
state: latest
|
||||
loop:
|
||||
- {name: glibc}
|
||||
- {name: dnf, os: [almalinux, fedora, rhel8, rhel9, rhel10, rocky]}
|
||||
- {name: debootstrap, os: [debian11, debian12, debian13, ubuntu, ubuntu-lts]}
|
||||
- {name: debian-archive-keyring, os: [debian11, debian12, debian13]}
|
||||
- {name: ubuntu-keyring, os: [ubuntu, ubuntu-lts]}
|
||||
retries: 4
|
||||
delay: 15
|
||||
|
||||
- name: Create RHEL repository file
|
||||
ansible.builtin.template:
|
||||
src: "{{ os | lower }}.repo.j2"
|
||||
dest: /etc/yum.repos.d/{{ os | lower }}.repo
|
||||
mode: "0644"
|
||||
- name: Prepare /iso mount and repository for RHEL-based systems
|
||||
when: os | lower in ["rhel8", "rhel9", "rhel10"]
|
||||
block:
|
||||
- name: Create /iso directory
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/install/redhat/dvd
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Mount RHEL ISO
|
||||
ansible.posix.mount:
|
||||
src: "{{ '/dev/sr1' if hypervisor == 'vmware' else '/dev/sr2' }}"
|
||||
path: /usr/local/install/redhat/dvd
|
||||
fstype: iso9660
|
||||
opts: "ro,loop"
|
||||
state: mounted
|
||||
|
||||
- name: Configure RHEL Repos for installation
|
||||
when: is_rhel | default(false)
|
||||
block:
|
||||
- name: Create directories for repository files and RPM GPG keys
|
||||
ansible.builtin.file:
|
||||
path: /etc/yum.repos.d
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create RHEL repository file
|
||||
ansible.builtin.template:
|
||||
src: "{{ os | lower }}.repo.j2"
|
||||
dest: /etc/yum.repos.d/{{ os | lower }}.repo
|
||||
mode: "0644"
|
||||
|
||||
@@ -3,54 +3,63 @@
|
||||
block:
|
||||
- name: Create btrfs filesystem in main volume
|
||||
community.general.filesystem:
|
||||
dev: "{{ install_drive }}{{ main_partition_suffix }}"
|
||||
dev: "{{ partitioning_root_device }}"
|
||||
fstype: btrfs
|
||||
force: true
|
||||
opts: >-
|
||||
{{
|
||||
'-K'
|
||||
if (partitioning_luks_enabled | bool)
|
||||
and not ('discard' in (partitioning_luks_options | default('') | lower))
|
||||
else omit
|
||||
}}
|
||||
|
||||
- name: Prepare BTRFS Subvolume
|
||||
ansible.posix.mount:
|
||||
path: /mnt
|
||||
src: "{{ install_drive }}{{ main_partition_suffix }}"
|
||||
src: "{{ partitioning_root_device }}"
|
||||
fstype: btrfs
|
||||
opts: rw,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async
|
||||
state: mounted
|
||||
|
||||
- name: Enable quotas on Btrfs filesystem
|
||||
ansible.builtin.command: btrfs quota enable /mnt
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
register: partitioning_btrfs_quota_result
|
||||
changed_when: false
|
||||
|
||||
- name: Make root subvolumes
|
||||
when: cis | bool or item.subvol not in ['var_log_audit']
|
||||
ansible.builtin.command: btrfs su cr /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
args:
|
||||
creates: /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||
loop:
|
||||
- { subvol: root }
|
||||
- { subvol: swap }
|
||||
- { subvol: home }
|
||||
- { subvol: var }
|
||||
- { subvol: var_log }
|
||||
- { subvol: var_log_audit }
|
||||
- {subvol: root}
|
||||
- {subvol: swap}
|
||||
- {subvol: home}
|
||||
- {subvol: var}
|
||||
- {subvol: pkg}
|
||||
- {subvol: var_log}
|
||||
- {subvol: var_log_audit}
|
||||
register: partitioning_btrfs_subvol_result
|
||||
|
||||
- name: Set quotas for subvolumes
|
||||
when: cis | bool or item.subvol not in ['var_log_audit']
|
||||
when: cis | bool
|
||||
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
loop:
|
||||
- { subvol: home, quota: 2G }
|
||||
- {subvol: home, quota: 2G}
|
||||
register: partitioning_btrfs_qgroup_result
|
||||
changed_when: false
|
||||
|
||||
- name: Create a Btrfs swap file
|
||||
ansible.builtin.command: >-
|
||||
btrfs filesystem mkswapfile --size {{ ((vm_memory | float / 1024 >= 16.0) | ternary((vm_memory
|
||||
| float / 2048) | int, [vm_memory | float / 1024, 4.0] | max) | int) }}g --uuid clear /mnt/@swap/swapfile
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
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
|
||||
ansible.posix.mount:
|
||||
path: /mnt
|
||||
src: "{{ install_drive }}{{ main_partition_suffix }}"
|
||||
src: "{{ partitioning_root_device }}"
|
||||
fstype: btrfs
|
||||
state: unmounted
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
fstype: ext4
|
||||
force: true
|
||||
loop:
|
||||
- { lv: root }
|
||||
- { lv: home }
|
||||
- { lv: var }
|
||||
- { lv: var_log }
|
||||
- { lv: var_log_audit }
|
||||
- {lv: root}
|
||||
- {lv: home}
|
||||
- {lv: var}
|
||||
- {lv: var_log}
|
||||
- {lv: var_log_audit}
|
||||
|
||||
- name: Remove Unsupported features for older Systems
|
||||
when: (os | lower in ['almalinux', 'debian11', 'rhel8', 'rhel9', 'rocky']) and (cis | bool 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 }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
loop:
|
||||
- { lv: root }
|
||||
- { lv: home }
|
||||
- { lv: var }
|
||||
- { lv: var_log }
|
||||
- { lv: var_log_audit }
|
||||
- {lv: root}
|
||||
- {lv: home}
|
||||
- {lv: var}
|
||||
- {lv: var_log}
|
||||
- {lv: var_log_audit}
|
||||
register: partitioning_ext4_tune_result
|
||||
changed_when: partitioning_ext4_tune_result.rc == 0
|
||||
|
||||
@@ -1,33 +1,304 @@
|
||||
---
|
||||
- name: Set partitioning defaults
|
||||
ansible.builtin.set_fact:
|
||||
partitioning_luks_enabled: "{{ partitioning_luks_enabled | default(luks_enabled | default(false)) | bool }}"
|
||||
partitioning_luks_mapper_name: "{{ partitioning_luks_mapper_name | default(luks_mapper_name | default('SYSTEM_DECRYPTED')) }}"
|
||||
partitioning_luks_type: "{{ partitioning_luks_type | default(luks_type | default('luks2')) }}"
|
||||
partitioning_luks_cipher: "{{ partitioning_luks_cipher | default(luks_cipher | default('aes-xts-plain64')) }}"
|
||||
partitioning_luks_hash: "{{ partitioning_luks_hash | default(luks_hash | default('sha512')) }}"
|
||||
partitioning_luks_iter_time: "{{ partitioning_luks_iter_time | default(luks_iter_time | default(4000)) }}"
|
||||
partitioning_luks_key_size: "{{ partitioning_luks_key_size | default(luks_key_size | default(512)) }}"
|
||||
partitioning_luks_pbkdf: "{{ partitioning_luks_pbkdf | default(luks_pbkdf | default('argon2id')) }}"
|
||||
partitioning_luks_use_urandom: "{{ partitioning_luks_use_urandom | default(luks_use_urandom | default(true)) | bool }}"
|
||||
partitioning_luks_verify_passphrase: "{{ partitioning_luks_verify_passphrase | default(luks_verify_passphrase | default(true)) | bool }}"
|
||||
partitioning_luks_auto_decrypt: "{{ partitioning_luks_auto_decrypt | default(luks_auto_decrypt | default(true)) | bool }}"
|
||||
partitioning_luks_auto_decrypt_method: "{{ partitioning_luks_auto_decrypt_method | default(luks_auto_decrypt_method | default('tpm2')) }}"
|
||||
partitioning_luks_tpm2_device: "{{ partitioning_luks_tpm2_device | default(luks_tpm2_device | default('auto')) }}"
|
||||
partitioning_luks_tpm2_pcrs: "{{ partitioning_luks_tpm2_pcrs | default(luks_tpm2_pcrs | default('')) }}"
|
||||
partitioning_luks_keyfile_size: "{{ partitioning_luks_keyfile_size | default(luks_keyfile_size | default(64)) }}"
|
||||
partitioning_luks_options: "{{ partitioning_luks_options | default(luks_options | default('discard,tries=3')) }}"
|
||||
partitioning_luks_device: >-
|
||||
{{ install_drive ~ (partitioning_main_partition_suffix | string) }}
|
||||
|
||||
- name: Set partitioning root device
|
||||
ansible.builtin.set_fact:
|
||||
partitioning_root_device: >-
|
||||
{{
|
||||
'/dev/mapper/' + partitioning_luks_mapper_name
|
||||
if partitioning_luks_enabled | bool
|
||||
else install_drive ~ (partitioning_main_partition_suffix | string)
|
||||
}}
|
||||
|
||||
- name: Set partitioning vm_size from input
|
||||
when: vm_size is defined or partitioning_vm_size is defined
|
||||
ansible.builtin.set_fact:
|
||||
partitioning_vm_size: "{{ (partitioning_vm_size | default(vm_size)) | float }}"
|
||||
|
||||
- name: Set partitioning vm memory from input
|
||||
when: vm_memory is defined or partitioning_vm_memory is defined
|
||||
ansible.builtin.set_fact:
|
||||
partitioning_vm_memory: "{{ (partitioning_vm_memory | default(vm_memory)) | float }}"
|
||||
|
||||
- name: Detect system memory for swap sizing
|
||||
when: partitioning_vm_memory is not defined
|
||||
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: Calculate swap size
|
||||
ansible.builtin.set_fact:
|
||||
partitioning_swap_size_gb: >-
|
||||
{{
|
||||
((partitioning_vm_memory | float / 1024) >= 16.0)
|
||||
| ternary(
|
||||
(partitioning_vm_memory | float / 2048) | int,
|
||||
[partitioning_vm_memory | float / 1024, 4.0] | max | int
|
||||
)
|
||||
}}
|
||||
|
||||
- name: Set partitioning vm_size for physical installs
|
||||
when:
|
||||
- install_type == "physical"
|
||||
- partitioning_vm_size is not defined
|
||||
- install_drive is defined
|
||||
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
|
||||
block:
|
||||
- name: Prepare partitions
|
||||
failed_when: false
|
||||
ansible.builtin.command: "{{ item.cmd }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
loop:
|
||||
- { cmd: umount -l /mnt }
|
||||
- { cmd: vgremove -f sys }
|
||||
- {
|
||||
cmd: 'find /dev -wholename "{{ install_drive }}*" -exec wipefs --force --all {} \;',
|
||||
}
|
||||
loop_control:
|
||||
label: "{{ item.cmd }}"
|
||||
block:
|
||||
- name: Disable swap
|
||||
ansible.builtin.command: swapoff -a
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Find mounts under /mnt
|
||||
ansible.builtin.command: findmnt -R /mnt -n -o TARGET
|
||||
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: partitioning_luks_enabled | bool
|
||||
community.crypto.luks_device:
|
||||
name: "{{ partitioning_luks_mapper_name }}"
|
||||
state: closed
|
||||
failed_when: false
|
||||
|
||||
- name: Remove LUKS mapper device
|
||||
when: partitioning_luks_enabled | bool
|
||||
ansible.builtin.command: >-
|
||||
dmsetup remove --force --retry {{ partitioning_luks_mapper_name }}
|
||||
register: partitioning_dmsetup_remove
|
||||
changed_when: partitioning_dmsetup_remove.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Remove LUKS signatures
|
||||
when: partitioning_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
|
||||
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
|
||||
block:
|
||||
- name: Create 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:
|
||||
- {number: 1, part_end: 500MiB, name: boot, flags: [boot, esp]}
|
||||
- {number: 2, part_start: 500MiB, name: root}
|
||||
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:
|
||||
- {number: 1, part_end: 500MiB, name: boot, flags: [boot, esp]}
|
||||
- {number: 2, part_start: 500MiB, name: root}
|
||||
|
||||
- name: Settle partition table
|
||||
ansible.builtin.command: "{{ item }}"
|
||||
loop:
|
||||
- { number: 1, part_end: 500MiB, name: boot, flags: [boot, esp] }
|
||||
- { number: 2, part_start: 500MiB, name: root }
|
||||
- "partprobe {{ install_drive }}"
|
||||
- "udevadm settle"
|
||||
register: partitioning_partprobe_settle
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Configure LUKS encryption
|
||||
when: partitioning_luks_enabled | bool
|
||||
vars:
|
||||
partitioning_luks_passphrase_effective: >-
|
||||
{{ (partitioning_luks_passphrase | default(luks_passphrase | default(''))) | string }}
|
||||
block:
|
||||
- name: Validate LUKS passphrase
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- partitioning_luks_passphrase_effective | length > 0
|
||||
fail_msg: luks_passphrase (or partitioning_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: "{{ partitioning_luks_type }}"
|
||||
cipher: "{{ partitioning_luks_cipher }}"
|
||||
hash: "{{ partitioning_luks_hash }}"
|
||||
keysize: "{{ partitioning_luks_key_size }}"
|
||||
pbkdf:
|
||||
algorithm: "{{ partitioning_luks_pbkdf }}"
|
||||
iteration_time: "{{ (partitioning_luks_iter_time | float) / 1000 }}"
|
||||
passphrase: "{{ partitioning_luks_passphrase_effective }}"
|
||||
register: partitioning_luks_format_result
|
||||
no_log: true
|
||||
|
||||
- name: Force-close LUKS mapper
|
||||
community.crypto.luks_device:
|
||||
name: "{{ partitioning_luks_mapper_name }}"
|
||||
state: closed
|
||||
failed_when: false
|
||||
|
||||
- name: Force-remove LUKS mapper device
|
||||
ansible.builtin.command: >-
|
||||
dmsetup remove --force --retry {{ partitioning_luks_mapper_name }}
|
||||
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: "{{ partitioning_luks_mapper_name }}"
|
||||
passphrase: "{{ partitioning_luks_passphrase_effective }}"
|
||||
allow_discards: "{{ 'discard' in (partitioning_luks_options | default('') | lower) }}"
|
||||
register: partitioning_luks_open_result
|
||||
no_log: true
|
||||
rescue:
|
||||
- name: Force-close stale LUKS mapper
|
||||
community.crypto.luks_device:
|
||||
name: "{{ partitioning_luks_mapper_name }}"
|
||||
state: closed
|
||||
failed_when: false
|
||||
|
||||
- name: Force-remove stale LUKS mapper device
|
||||
ansible.builtin.command: >-
|
||||
dmsetup remove --force --retry {{ partitioning_luks_mapper_name }}
|
||||
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: "{{ partitioning_luks_mapper_name }}"
|
||||
passphrase: "{{ partitioning_luks_passphrase_effective }}"
|
||||
allow_discards: "{{ 'discard' in (partitioning_luks_options | default('') | 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
|
||||
when: filesystem != 'btrfs'
|
||||
@@ -35,7 +306,7 @@
|
||||
- name: Create LVM volume group
|
||||
community.general.lvg:
|
||||
vg: sys
|
||||
pvs: "{{ install_drive }}{{ main_partition_suffix }}"
|
||||
pvs: "{{ partitioning_root_device }}"
|
||||
|
||||
- name: Create LVM logical volumes
|
||||
when: cis | bool or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||
@@ -47,31 +318,37 @@
|
||||
loop:
|
||||
- lv: root
|
||||
size: >-
|
||||
{{ [(((((vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0)) - (((vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((vm_memory | float / 2048) | int), (vm_memory | float / 1024)))) < 4)
|
||||
| ternary(4,((((vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0)) -
|
||||
(((vm_memory | float / 1024) > 16.0)| ternary(((vm_memory | float / 2048) | int), (vm_memory | float / 1024)))) > 12)
|
||||
| ternary(((vm_size | float) * 0.4) | round(0, 'ceil'),((vm_size | float) - 0.5 - ((cis | bool)
|
||||
| ternary(7.5, 0)) - (((vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((vm_memory | float / 2048) | int), (vm_memory | float / 1024))))))))), 4 ] | max | string + 'G' }}
|
||||
{{ [(((((partitioning_vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0)) - (((partitioning_vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((partitioning_vm_memory | float / 2048) | int), (partitioning_vm_memory | float / 1024)))) < 4)
|
||||
| ternary(4,((((partitioning_vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0)) -
|
||||
(((partitioning_vm_memory | float / 1024) > 16.0)
|
||||
| ternary(
|
||||
((partitioning_vm_memory | float / 2048) | int),
|
||||
(partitioning_vm_memory | float / 1024)
|
||||
)))
|
||||
> 12)
|
||||
| ternary(((partitioning_vm_size | float) * 0.4) | round(0, 'ceil'),((partitioning_vm_size | float) - 0.5 - ((cis | bool)
|
||||
| ternary(7.5, 0)) - (((partitioning_vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((partitioning_vm_memory | float / 2048) | int), (partitioning_vm_memory | float / 1024))))))))), 4 ] | max | string +
|
||||
'G' }}
|
||||
- lv: swap
|
||||
size: >-
|
||||
{{ ((((vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0))) - (((vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((vm_memory | float / 2048) | int), (vm_memory | float / 1024)))) < 4)
|
||||
| ternary((((vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0))) - 4), (((vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((vm_memory | float / 2048) | int), (vm_memory | float / 1024)))) | string + 'G' }}
|
||||
{{ ((((partitioning_vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0))) - (((partitioning_vm_memory | float / 1024) > 16.0)
|
||||
| ternary(((partitioning_vm_memory | float / 2048) | int), (partitioning_vm_memory | float / 1024)))) < 4)
|
||||
| ternary((((partitioning_vm_size | float) - 0.5 - ((cis | bool) | ternary(7.5, 0))) - 4), (((partitioning_vm_memory | float / 1024)
|
||||
> 16.0)
|
||||
| ternary(((partitioning_vm_memory | float / 2048) | int), (partitioning_vm_memory | float / 1024)))) | string + 'G' }}
|
||||
- lv: home
|
||||
size: "{{ ([([(((vm_size | float) - 20) * 0.1), 2] | max), 20] | min) | string + 'G' }}"
|
||||
|
||||
- { lv: var, size: "2G" }
|
||||
- { lv: var_log, size: "2G" }
|
||||
- { lv: var_log_audit, size: "1.5G" }
|
||||
size: "{{ ([([(((partitioning_vm_size | float) - 20) * 0.1), 2] | max), 20] | min) | string + 'G' }}"
|
||||
- {lv: var, size: "2G"}
|
||||
- {lv: var_log, size: "2G"}
|
||||
- {lv: var_log_audit, size: "1.5G"}
|
||||
|
||||
- name: Create filesystems
|
||||
block:
|
||||
- name: Create FAT32 filesystem in boot partition
|
||||
community.general.filesystem:
|
||||
dev: "{{ install_drive }}{{ boot_partition_suffix }}"
|
||||
dev: "{{ install_drive }}{{ partitioning_boot_partition_suffix }}"
|
||||
fstype: vfat
|
||||
opts: -F32 -n BOOT
|
||||
force: true
|
||||
@@ -86,20 +363,18 @@
|
||||
ansible.builtin.include_tasks: "{{ filesystem }}.yml"
|
||||
|
||||
- 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
|
||||
register: boot_uuid
|
||||
|
||||
- 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
|
||||
register: main_uuid
|
||||
|
||||
- name: Get UUIDs for LVM filesystems
|
||||
when: filesystem != 'btrfs' and (cis | bool or item not in ['home', 'var', 'var_log', 'var_log_audit'])
|
||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/{{ item }}
|
||||
changed_when: false
|
||||
register: uuid_result
|
||||
loop:
|
||||
- root
|
||||
- swap
|
||||
@@ -107,72 +382,125 @@
|
||||
- var
|
||||
- var_log
|
||||
- var_log_audit
|
||||
register: partitioning_uuid_result
|
||||
changed_when: false
|
||||
|
||||
- name: Assign UUIDs to Variables
|
||||
when: filesystem != 'btrfs'
|
||||
ansible.builtin.set_fact:
|
||||
uuid_root: "{{ uuid_result.results[0].stdout_lines }}"
|
||||
uuid_swap: "{{ uuid_result.results[1].stdout_lines }}"
|
||||
uuid_home: "{{ uuid_result.results[2].stdout_lines if cis | bool else '' }}"
|
||||
uuid_var: "{{ uuid_result.results[3].stdout_lines if cis | bool else '' }}"
|
||||
uuid_var_log: "{{ uuid_result.results[4].stdout_lines if cis | bool else '' }}"
|
||||
uuid_var_log_audit: "{{ uuid_result.results[5].stdout_lines if cis | bool else '' }}"
|
||||
partitioning_uuid_root: "{{ partitioning_uuid_result.results[0].stdout_lines }}"
|
||||
partitioning_uuid_swap: "{{ partitioning_uuid_result.results[1].stdout_lines }}"
|
||||
partitioning_uuid_home: "{{ partitioning_uuid_result.results[2].stdout_lines if cis | bool else '' }}"
|
||||
partitioning_uuid_var: "{{ partitioning_uuid_result.results[3].stdout_lines if cis | bool else '' }}"
|
||||
partitioning_uuid_var_log: "{{ partitioning_uuid_result.results[4].stdout_lines if cis | bool else '' }}"
|
||||
partitioning_uuid_var_log_audit: "{{ partitioning_uuid_result.results[5].stdout_lines if cis | bool else '' }}"
|
||||
|
||||
- name: Mount filesystems
|
||||
block:
|
||||
- name: Mount filesystems and subvolumes
|
||||
when:
|
||||
- cis | bool or (not cis and (item.path == '/var/log' and filesystem == 'btrfs')
|
||||
or (item.path not in ['/home', '/var', '/var/log', '/var/log/audit']))
|
||||
- not (item.path == '/swap' and filesystem != 'btrfs')
|
||||
- >-
|
||||
cis | bool or (
|
||||
not cis and (
|
||||
(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 filesystem != 'btrfs')
|
||||
ansible.posix.mount:
|
||||
path: /mnt{{ item.path }}
|
||||
src: "{{ 'UUID=' + (main_uuid.stdout if filesystem == 'btrfs' else item.uuid) }}"
|
||||
src: "{{ 'UUID=' + (partitioning_main_uuid.stdout if filesystem == 'btrfs' else item.uuid) }}"
|
||||
fstype: "{{ filesystem }}"
|
||||
opts: "{{ item.opts }}"
|
||||
state: mounted
|
||||
loop:
|
||||
- path: ""
|
||||
uuid: "{{ uuid_root[0] | default(omit) }}"
|
||||
opts: "{{ 'defaults' if filesystem != 'btrfs' else 'rw,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@' }}"
|
||||
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=@'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /swap
|
||||
opts: "rw,nosuid,nodev,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@swap"
|
||||
opts: >-
|
||||
{{
|
||||
[
|
||||
'rw', 'nosuid', 'nodev', 'relatime', 'compress=zstd:15', 'ssd',
|
||||
'space_cache=v2', 'discard=async', 'subvol=@swap'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /home
|
||||
uuid: "{{ uuid_home[0] | default(omit) }}"
|
||||
opts: "{{ 'defaults,nosuid,nodev' if filesystem != 'btrfs'
|
||||
else 'rw,nosuid,nodev,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@home' }}"
|
||||
uuid: "{{ partitioning_uuid_home[0] | default(omit) }}"
|
||||
opts: >-
|
||||
{{
|
||||
'defaults,nosuid,nodev'
|
||||
if filesystem != 'btrfs'
|
||||
else [
|
||||
'rw', 'nosuid', 'nodev', 'relatime', 'compress=zstd:15', 'ssd',
|
||||
'space_cache=v2', 'discard=async', 'subvol=@home'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /var
|
||||
uuid: "{{ uuid_var[0] | default(omit) }}"
|
||||
opts: "{{ 'defaults,nosuid,nodev' if filesystem != 'btrfs'
|
||||
else 'rw,nosuid,nodev,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var' }}"
|
||||
uuid: "{{ partitioning_uuid_var[0] | default(omit) }}"
|
||||
opts: >-
|
||||
{{
|
||||
'defaults,nosuid,nodev'
|
||||
if filesystem != 'btrfs'
|
||||
else [
|
||||
'rw', 'nosuid', 'nodev', 'relatime', 'compress=zstd:15', 'ssd',
|
||||
'space_cache=v2', 'discard=async', 'subvol=@var'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /var/log
|
||||
uuid: "{{ uuid_var_log[0] | default(omit) }}"
|
||||
opts: "{{ 'defaults,nosuid,nodev,noexec' if filesystem != 'btrfs'
|
||||
else 'rw,nosuid,nodev,noexec,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var_log' }}"
|
||||
uuid: "{{ partitioning_uuid_var_log[0] | default(omit) }}"
|
||||
opts: >-
|
||||
{{
|
||||
'defaults,nosuid,nodev,noexec'
|
||||
if filesystem != 'btrfs'
|
||||
else [
|
||||
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', 'compress=zstd:15',
|
||||
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /var/cache/pacman/pkg
|
||||
uuid: "{{ partitioning_uuid_root | default([]) | first | default(omit) }}"
|
||||
opts: >-
|
||||
{{
|
||||
'defaults,nosuid,nodev,noexec'
|
||||
if filesystem != 'btrfs'
|
||||
else [
|
||||
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', 'compress=zstd:15',
|
||||
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@pkg'
|
||||
] | join(',')
|
||||
}}
|
||||
- path: /var/log/audit
|
||||
uuid: "{{ uuid_var_log_audit[0] | default(omit) }}"
|
||||
opts: "{{ 'defaults,nosuid,nodev,noexec' if filesystem != 'btrfs'
|
||||
else 'rw,nosuid,nodev,noexec,relatime,compress=zstd:15,ssd,space_cache=v2,discard=async,subvol=@var_log_audit' }}"
|
||||
|
||||
- name: Mount tmp and var_tmp filesystems
|
||||
ansible.posix.mount:
|
||||
path: /mnt{{ item.path }}
|
||||
src: tmpfs
|
||||
fstype: tmpfs
|
||||
opts: defaults,nosuid,nodev,noexec
|
||||
state: mounted
|
||||
loop:
|
||||
- { path: /tmp }
|
||||
- { path: /var/tmp }
|
||||
uuid: "{{ partitioning_uuid_var_log_audit[0] | default(omit) }}"
|
||||
opts: >-
|
||||
{{
|
||||
'defaults,nosuid,nodev,noexec'
|
||||
if filesystem != 'btrfs'
|
||||
else [
|
||||
'rw', 'nosuid', 'nodev', 'noexec', 'relatime', 'compress=zstd:15',
|
||||
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit'
|
||||
] | join(',')
|
||||
}}
|
||||
|
||||
- name: Mount boot filesystem
|
||||
ansible.posix.mount:
|
||||
path: "{{ '/mnt/boot/efi' if os | lower in ['rhel8', 'ubuntu', 'ubuntu-lts'] else '/mnt/boot' }}"
|
||||
src: UUID={{ boot_uuid.stdout }}
|
||||
src: UUID={{ partitioning_boot_uuid.stdout }}
|
||||
fstype: vfat
|
||||
state: mounted
|
||||
|
||||
- name: Activate swap
|
||||
ansible.builtin.command: "{{ 'swapon /mnt/swap/swapfile' if filesystem == 'btrfs' else 'swapon -U ' + uuid_swap[0] }}"
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
vars:
|
||||
partitioning_swap_cmd: >-
|
||||
{{ 'swapon /mnt/swap/swapfile' if 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
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
fstype: xfs
|
||||
force: true
|
||||
loop:
|
||||
- { lv: root }
|
||||
- { lv: home }
|
||||
- { lv: var }
|
||||
- { lv: var_log }
|
||||
- { lv: var_log_audit }
|
||||
- {lv: root}
|
||||
- {lv: home}
|
||||
- {lv: var}
|
||||
- {lv: var_log}
|
||||
- {lv: var_log_audit}
|
||||
|
||||
11
roles/virtualization/defaults/main.yml
Normal file
11
roles/virtualization/defaults/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
virtualization_tpm2_enabled: >-
|
||||
{{
|
||||
(partitioning_luks_enabled | default(luks_enabled | default(false)) | bool)
|
||||
and (partitioning_luks_auto_decrypt | default(luks_auto_decrypt | default(true)) | bool)
|
||||
and (
|
||||
(partitioning_luks_auto_decrypt_method | default(luks_auto_decrypt_method | default('tpm2')))
|
||||
| lower
|
||||
== 'tpm2'
|
||||
)
|
||||
}}
|
||||
@@ -1,22 +1,34 @@
|
||||
---
|
||||
- name: Check if VM disk exists
|
||||
- name: Set libvirt image paths
|
||||
delegate_to: localhost
|
||||
ansible.builtin.stat:
|
||||
path: "{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}.qcow2"
|
||||
register: vm_disk_stat
|
||||
vars:
|
||||
virtualization_libvirt_image_dir_value: "{{ vm_path | default('/var/lib/libvirt/images') }}"
|
||||
ansible.builtin.set_fact:
|
||||
virtualization_libvirt_image_dir: "{{ virtualization_libvirt_image_dir_value }}"
|
||||
virtualization_libvirt_disk_path: >-
|
||||
{{ [virtualization_libvirt_image_dir_value, hostname ~ '.qcow2'] | ansible.builtin.path_join }}
|
||||
virtualization_libvirt_cloudinit_path: >-
|
||||
{{ [virtualization_libvirt_image_dir_value, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||
changed_when: false
|
||||
|
||||
- name: Create VM disk
|
||||
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
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- qemu-img
|
||||
- create
|
||||
- -f
|
||||
- qcow2
|
||||
- "{{ virtualization_libvirt_disk_path }}"
|
||||
- "{{ vm_size }}G"
|
||||
creates: "{{ virtualization_libvirt_disk_path }}"
|
||||
|
||||
- name: Generate Random MAC Address
|
||||
- name: Generate VM MAC address
|
||||
delegate_to: localhost
|
||||
ansible.builtin.shell: set -o pipefail && openssl rand -hex 5 | sed 's/\(..\)/\1:/g; s/.$//' | sed 's/^/02:/'
|
||||
ansible.builtin.set_fact:
|
||||
virtualization_mac_address: >-
|
||||
{{ '52:54:00' | community.general.random_mac(seed=hostname) }}
|
||||
changed_when: false
|
||||
register: mac_address_output
|
||||
|
||||
- name: Render cloud config templates
|
||||
delegate_to: localhost
|
||||
@@ -25,17 +37,19 @@
|
||||
dest: /tmp/{{ item.dest_prefix }}-{{ hostname }}.yml
|
||||
mode: '0644'
|
||||
loop:
|
||||
- { src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data }
|
||||
- { src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config }
|
||||
- {src: cloud-user-data.yml.j2, dest_prefix: cloud-user-data}
|
||||
- {src: cloud-network-config.yml.j2, dest_prefix: cloud-network-config}
|
||||
|
||||
- name: Create cloud-init disk
|
||||
delegate_to: localhost
|
||||
ansible.builtin.command: >
|
||||
cloud-localds {{ vm_path | default('/var/lib/libvirt/images/') }}/{{ hostname }}-cloudinit.iso
|
||||
/tmp/cloud-user-data-{{ hostname }}.yml
|
||||
-N /tmp/cloud-network-config-{{ hostname }}.yml
|
||||
changed_when: result.rc == 0
|
||||
register: result
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- cloud-localds
|
||||
- "{{ virtualization_libvirt_cloudinit_path }}"
|
||||
- "/tmp/cloud-user-data-{{ hostname }}.yml"
|
||||
- -N
|
||||
- "/tmp/cloud-network-config-{{ hostname }}.yml"
|
||||
creates: "{{ virtualization_libvirt_cloudinit_path }}"
|
||||
|
||||
- name: Create VM using libvirt
|
||||
delegate_to: localhost
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
---
|
||||
- name: Deploy VM on Proxmox
|
||||
delegate_to: localhost
|
||||
community.general.proxmox_kvm:
|
||||
vars:
|
||||
virtualization_dns_value: "{{ vm_dns | default('') }}"
|
||||
virtualization_dns_list_raw: >-
|
||||
{{
|
||||
virtualization_dns_value
|
||||
if virtualization_dns_value is iterable and virtualization_dns_value is not string
|
||||
else virtualization_dns_value.split(',')
|
||||
}}
|
||||
virtualization_dns_list: >-
|
||||
{{ virtualization_dns_list_raw | map('trim') | reject('equalto', '') | list }}
|
||||
virtualization_search_value: "{{ vm_dns_search | default('') }}"
|
||||
virtualization_search_list_raw: >-
|
||||
{{
|
||||
virtualization_search_value
|
||||
if virtualization_search_value is iterable and virtualization_search_value is not string
|
||||
else virtualization_search_value.split(',')
|
||||
}}
|
||||
virtualization_search_list: >-
|
||||
{{ virtualization_search_list_raw | map('trim') | reject('equalto', '') | list }}
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ hypervisor_url }}"
|
||||
api_user: "{{ hypervisor_username }}"
|
||||
api_password: "{{ hypervisor_password }}"
|
||||
@@ -17,7 +36,10 @@
|
||||
balloon: "{{ vm_ballo | default(omit) }}"
|
||||
numa_enabled: true
|
||||
hotplug: network,disk
|
||||
update: "{{ virtualization_tpm2_enabled | bool }}"
|
||||
update_unsafe: "{{ virtualization_tpm2_enabled | bool }}"
|
||||
bios: ovmf
|
||||
machine: "{{ 'q35' if virtualization_tpm2_enabled | bool else omit }}"
|
||||
boot: ac
|
||||
scsihw: virtio-scsi-single
|
||||
scsi:
|
||||
@@ -27,6 +49,12 @@
|
||||
format: raw
|
||||
pre_enrolled_keys: false
|
||||
storage: "{{ hypervisor_storage }}"
|
||||
tpmstate0: >-
|
||||
{{
|
||||
{'storage': hypervisor_storage, 'version': '2.0'}
|
||||
if virtualization_tpm2_enabled | bool
|
||||
else omit
|
||||
}}
|
||||
ide:
|
||||
ide0: "{{ boot_iso }},media=cdrom"
|
||||
ide1: "{{ rhel_iso + ',media=cdrom' if rhel_iso is defined else omit }}"
|
||||
@@ -34,14 +62,21 @@
|
||||
net:
|
||||
net0: virtio,bridge={{ vm_nif }}{% if vlan_name is defined and vlan_name %},tag={{ vlan_name }}{% endif %}
|
||||
ipconfig:
|
||||
ipconfig0: ip={{ vm_ip }}/{{ vm_nms | default(24) }},gw={{ vm_gw }}
|
||||
nameservers: "{{ vm_dns }}"
|
||||
ipconfig0: >-
|
||||
{{
|
||||
'ip=' ~ vm_ip ~ '/' ~ (vm_nms | default(24))
|
||||
~ (',gw=' ~ vm_gw if vm_gw is defined and vm_gw | length else '')
|
||||
if vm_ip is defined and vm_ip | length
|
||||
else 'ip=dhcp'
|
||||
}}
|
||||
nameservers: "{{ virtualization_dns_list if virtualization_dns_list | length else omit }}"
|
||||
searchdomains: "{{ virtualization_search_list if virtualization_search_list | length else omit }}"
|
||||
onboot: true
|
||||
state: present
|
||||
|
||||
- name: Start VM on Proxmox
|
||||
delegate_to: localhost
|
||||
community.general.proxmox_kvm:
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ hypervisor_url }}"
|
||||
api_user: "{{ hypervisor_username }}"
|
||||
api_password: "{{ hypervisor_password }}"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
- name: Create VM in vCenter
|
||||
delegate_to: localhost
|
||||
community.vmware.vmware_guest:
|
||||
@@ -7,12 +8,12 @@
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_datacenter }}"
|
||||
cluster: "{{ hypervisor_cluster }}"
|
||||
folder: "{{ vm_path }}"
|
||||
folder: "{{ vm_path | default(omit) }}"
|
||||
name: "{{ hostname }}"
|
||||
guest_id: otherLinux64Guest
|
||||
annotation: |
|
||||
{{ note | default('') }}
|
||||
state: poweredon
|
||||
state: "{{ 'poweredoff' if virtualization_tpm2_enabled | bool else 'poweredon' }}"
|
||||
disk:
|
||||
- size_gb: "{{ vm_size }}"
|
||||
type: thin
|
||||
@@ -46,9 +47,28 @@
|
||||
- name: "{{ vm_nif }}"
|
||||
type: dhcp
|
||||
vlan: "{{ vlan_name | default(omit) }}"
|
||||
register: vmware_guest_result
|
||||
failed_when:
|
||||
- vmware_guest_result.failed is defined and vmware_guest_result.failed
|
||||
- "'error' in vmware_guest_result"
|
||||
- "'failed' in vmware_guest_result"
|
||||
- vmware_guest_result.rc is defined and vmware_guest_result.rc != 0
|
||||
|
||||
- name: Ensure vTPM2 is enabled when required
|
||||
when: virtualization_tpm2_enabled | bool
|
||||
delegate_to: localhost
|
||||
community.vmware.vmware_guest_tpm:
|
||||
hostname: "{{ hypervisor_url }}"
|
||||
username: "{{ hypervisor_username }}"
|
||||
password: "{{ hypervisor_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_datacenter }}"
|
||||
folder: "{{ vm_path | default(omit) }}"
|
||||
name: "{{ hostname }}"
|
||||
state: present
|
||||
|
||||
- name: Start VM in vCenter
|
||||
when: virtualization_tpm2_enabled | bool
|
||||
delegate_to: localhost
|
||||
vmware.vmware.vm_powerstate:
|
||||
hostname: "{{ hypervisor_url }}"
|
||||
username: "{{ hypervisor_username }}"
|
||||
password: "{{ hypervisor_password }}"
|
||||
validate_certs: false
|
||||
datacenter: "{{ hypervisor_datacenter }}"
|
||||
name: "{{ hostname }}"
|
||||
state: powered-on
|
||||
|
||||
@@ -3,9 +3,44 @@ network:
|
||||
ethernets:
|
||||
id0:
|
||||
match:
|
||||
macaddress: "{{ mac_address_output.stdout }}"
|
||||
macaddress: "{{ virtualization_mac_address }}"
|
||||
{% set has_static = vm_ip is defined and vm_ip | length %}
|
||||
{% set dns_value = vm_dns | default('') %}
|
||||
{% set dns_list_raw = dns_value if dns_value is iterable and dns_value is not string else dns_value.split(',') %}
|
||||
{% set dns_list = dns_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||
{% set search_value = vm_dns_search | default('') %}
|
||||
{% set search_list_raw = search_value if search_value is iterable and search_value is not string else search_value.split(',') %}
|
||||
{% set search_list = search_list_raw | map('trim') | reject('equalto', '') | list %}
|
||||
{% if has_static %}
|
||||
addresses:
|
||||
- "{{ vm_ip }}"
|
||||
- "{{ vm_ip }}/{{ vm_nms | default(24) }}"
|
||||
{% if vm_gw is defined and vm_gw | length %}
|
||||
gateway4: "{{ vm_gw }}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
dhcp4: true
|
||||
{% if (vm_dns is defined and vm_dns | length) or (vm_dns_search is defined and vm_dns_search | length) %}
|
||||
dhcp4-overrides:
|
||||
{% if vm_dns is defined and vm_dns | length %}
|
||||
use-dns: false
|
||||
{% endif %}
|
||||
{% if vm_dns_search is defined and vm_dns_search | length %}
|
||||
use-domains: false
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if dns_list or search_list %}
|
||||
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 %}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<devices>
|
||||
<disk type='file' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source file='{{ vm_path | default('/var/lib/libvirt/images/') }}{{ hostname }}.qcow2'/>
|
||||
<source file='{{ virtualization_libvirt_disk_path }}'/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
</disk>
|
||||
<disk type="file" device="cdrom">
|
||||
@@ -34,7 +34,7 @@
|
||||
</disk>
|
||||
<disk type="file" device="cdrom">
|
||||
<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"/>
|
||||
</disk>
|
||||
{% if rhel_iso is defined %}
|
||||
@@ -45,10 +45,15 @@
|
||||
</disk>
|
||||
{% endif %}
|
||||
<interface type='network'>
|
||||
<mac address="{{ mac_address_output.stdout }}"/>
|
||||
<mac address="{{ virtualization_mac_address }}"/>
|
||||
<source network='default'/>
|
||||
<model type='virtio'/>
|
||||
</interface>
|
||||
{% if virtualization_tpm2_enabled | default(false) %}
|
||||
<tpm model='tpm-crb'>
|
||||
<backend type='emulator' version='2.0'/>
|
||||
</tpm>
|
||||
{% endif %}
|
||||
<input type="tablet" bus="usb"/>
|
||||
<input type="mouse" bus="ps2"/>
|
||||
<input type="keyboard" bus="ps2"/>
|
||||
|
||||
15
vars_baremetal_example.yml
Normal file
15
vars_baremetal_example.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
hypervisor: "none"
|
||||
install_type: "physical"
|
||||
install_drive: "/dev/sda"
|
||||
|
||||
os: "archlinux"
|
||||
filesystem: "btrfs"
|
||||
|
||||
luks_enabled: true
|
||||
luks_passphrase: "1234"
|
||||
luks_mapper_name: "SYSTEM_DECRYPTED"
|
||||
luks_auto_decrypt: true
|
||||
luks_auto_decrypt_method: "tpm2"
|
||||
luks_tpm2_device: "auto"
|
||||
luks_tpm2_pcrs: "7"
|
||||
@@ -1,18 +1,39 @@
|
||||
---
|
||||
# Set vm_ip for static addressing. Remove vm_ip to use DHCP.
|
||||
vm_ip: "{{ inventory_hostname }}"
|
||||
|
||||
install_type: "virtual"
|
||||
install_drive: "/dev/sda" # Use /dev/vda for virtio/libvirt.
|
||||
custom_iso: false # Set true to skip ArchISO-specific validation and pacman setup.
|
||||
|
||||
hypervisor_url: "192.168.0.2"
|
||||
hypervisor_url: "pve01.example.com"
|
||||
hypervisor_username: "root@pam"
|
||||
hypervisor_password: "SomePassword"
|
||||
hypervisor_node: "NodeName"
|
||||
hypervisor_storage: "local-btrfs"
|
||||
hypervisor_password: "CHANGE_ME"
|
||||
hypervisor_node: "pve01"
|
||||
hypervisor_storage: "local-lvm"
|
||||
hypervisor_datacenter: "dc01"
|
||||
hypervisor_cluster: "cluster01"
|
||||
|
||||
# For VMware-Tools
|
||||
ansible_vmware_host: "{{ hypervisor_url }}"
|
||||
ansible_vmware_user: "{{ hypervisor_username }}"
|
||||
ansible_vmware_password: "{{ hypervisor_password }}"
|
||||
ansible_vmware_guest_path: "/{{ hypervisor_cluster }}/vm{{ vm_path }}/{{ hostname }}"
|
||||
ansible_vmware_validate_certs: no
|
||||
ansible_vmware_tools_user: "root"
|
||||
ansible_vmware_tools_password: ""
|
||||
# VMware (only needed when hypervisor: vmware)
|
||||
# vm_path: "/Folder" # Optional folder path segment in vCenter.
|
||||
vmware_ssh: true
|
||||
|
||||
# LUKS disk encryption (optional)
|
||||
# These map to partitioning_luks_* internally.
|
||||
luks_enabled: false
|
||||
luks_passphrase: "CHANGE_ME"
|
||||
luks_mapper_name: "SYSTEM_DECRYPTED"
|
||||
luks_auto_decrypt: true
|
||||
luks_auto_decrypt_method: "tpm2"
|
||||
luks_tpm2_device: "auto"
|
||||
luks_tpm2_pcrs: "7"
|
||||
luks_keyfile_size: 64
|
||||
luks_options: "discard,tries=3"
|
||||
luks_type: "luks2"
|
||||
luks_cipher: "aes-xts-plain64"
|
||||
luks_hash: "sha512"
|
||||
luks_iter_time: 4000
|
||||
luks_key_size: 512
|
||||
luks_pbkdf: "argon2id"
|
||||
luks_use_urandom: true
|
||||
luks_verify_passphrase: true
|
||||
|
||||
Reference in New Issue
Block a user