Compare commits
56 Commits
09b3ed44ba
...
07492b5b57
| Author | SHA1 | Date | |
|---|---|---|---|
| 07492b5b57 | |||
| 14913bcd3d | |||
| 041650c287 | |||
| a63ffbc731 | |||
| 9d2f1cc5bd | |||
| f72f9feb9a | |||
| 417737f904 | |||
| a06c2ebdcf | |||
| e174ecda42 | |||
| 5246a905bb | |||
| d00d84b69c | |||
| 4dafa8c596 | |||
| 53584b8730 | |||
| ce40468b77 | |||
| 4b4fab3c33 | |||
| db2fab5e7d | |||
| 42be0a5919 | |||
| 17400fa6ff | |||
| deb14d2c94 | |||
| 65c5b1029b | |||
| a1fbb7c21d | |||
| d076ac8fef | |||
| c82e4afc4d | |||
| ac72fdc4a6 | |||
| b2e050c467 | |||
| 914d7dd9d1 | |||
| 21bf8f79e2 | |||
| 38feff4369 | |||
| 404529e8a4 | |||
| 3db18858c3 | |||
| 72a9576abe | |||
| 462c2c7dfe | |||
| ef8bfeaf84 | |||
| ba6be037ac | |||
| 5ca1c7f570 | |||
| cd8e477534 | |||
| c439e9741e | |||
| 0a5c70e49f | |||
| 19f2c9efe2 | |||
| 230c74fd9b | |||
| a2c19e2e49 | |||
| 9f9a4b38b8 | |||
| 524356cf8d | |||
| a2993212ca | |||
| fba2e5fc94 | |||
| cf68a93b45 | |||
| 3000268a0e | |||
| 196c5be67a | |||
| 33bad193b4 | |||
| d5277802f7 | |||
| 28e6cf50d1 | |||
| 42cb5071c2 | |||
| 23a798a63a | |||
| 5dd84c6b39 | |||
| d0ae20911b | |||
| b6d06dd96d |
19
.yamllint
Normal file
19
.yamllint
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
extends: default
|
||||||
|
|
||||||
|
rules:
|
||||||
|
document-start: disable
|
||||||
|
line-length:
|
||||||
|
max: 200
|
||||||
|
allow-non-breakable-words: true
|
||||||
|
allow-non-breakable-inline-mappings: true
|
||||||
|
truthy:
|
||||||
|
allowed-values: ["true", "false"]
|
||||||
|
check-keys: false
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
|
comments-indentation: disable
|
||||||
|
braces:
|
||||||
|
max-spaces-inside: 1
|
||||||
|
octal-values:
|
||||||
|
forbid-implicit-octal: true
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
[defaults]
|
[defaults]
|
||||||
hash_behaviour = merge
|
hash_behaviour = merge
|
||||||
|
interpreter_python = auto_silent
|
||||||
|
deprecation_warnings = False
|
||||||
|
host_key_checking = False
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
register: bootstrap_almalinux_base_result
|
register: bootstrap_almalinux_base_result
|
||||||
changed_when: bootstrap_almalinux_base_result.rc == 0
|
changed_when: bootstrap_almalinux_base_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
|
|
||||||
- name: Install extra packages
|
- name: Install extra packages
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
|||||||
@@ -6,13 +6,6 @@
|
|||||||
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
||||||
}}
|
}}
|
||||||
block:
|
block:
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
|
|
||||||
- name: Install Alpine Linux packages
|
- name: Install Alpine Linux packages
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
apk --root /mnt --no-cache add alpine-base
|
apk --root /mnt --no-cache add alpine-base
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
register: bootstrap_fedora_base_result
|
register: bootstrap_fedora_base_result
|
||||||
changed_when: bootstrap_fedora_base_result.rc == 0
|
changed_when: bootstrap_fedora_base_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
|
|
||||||
- name: Install extra packages
|
- name: Install extra packages
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
- { src: proc, path: proc, fstype: proc }
|
- { src: proc, path: proc, fstype: proc }
|
||||||
- { src: sysfs, path: sys, fstype: sysfs }
|
- { src: sysfs, path: sys, fstype: sysfs }
|
||||||
- { src: /dev, path: dev, fstype: none, opts: bind }
|
- { src: /dev, path: dev, fstype: none, opts: bind }
|
||||||
|
- { src: devpts, path: dev/pts, fstype: devpts, opts: "gid=5,mode=620" }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Run OS-specific bootstrap process
|
- name: Run OS-specific bootstrap process
|
||||||
vars:
|
vars:
|
||||||
@@ -39,3 +42,10 @@
|
|||||||
void: void.yml
|
void: void.yml
|
||||||
bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}"
|
bootstrap_var_key: "{{ 'bootstrap_' + (os | replace('-lts', '') | replace('-', '_')) }}"
|
||||||
ansible.builtin.include_tasks: "{{ bootstrap_os_task_map[os] }}"
|
ansible.builtin.include_tasks: "{{ bootstrap_os_task_map[os] }}"
|
||||||
|
|
||||||
|
- name: Ensure chroot uses live environment DNS
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: /run/NetworkManager/resolv.conf
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
state: link
|
||||||
|
force: true
|
||||||
|
|||||||
@@ -6,13 +6,6 @@
|
|||||||
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
||||||
}}
|
}}
|
||||||
block:
|
block:
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
|
|
||||||
- name: Install openSUSE base packages
|
- name: Install openSUSE base packages
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
zypper --root /mnt --non-interactive install -t pattern patterns-base-base
|
zypper --root /mnt --non-interactive install -t pattern patterns-base-base
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
- bootstrap_result.rc != 0
|
- bootstrap_result.rc != 0
|
||||||
- "'grub2-common' not in (bootstrap_result.stderr | default(''))"
|
- "'grub2-common' not in (bootstrap_result.stderr | default(''))"
|
||||||
|
|
||||||
- name: Write resolv.conf into chroot
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
{% for dns in system_cfg.network.dns.servers %}
|
|
||||||
nameserver {{ dns }}
|
|
||||||
{% endfor %}
|
|
||||||
{% if system_cfg.network.dns.search | default([]) | length > 0 %}
|
|
||||||
search {{ system_cfg.network.dns.search | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
- name: Ensure chroot RHEL DVD directory exists
|
- name: Ensure chroot RHEL DVD directory exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /mnt/usr/local/install/redhat/dvd
|
path: /mnt/usr/local/install/redhat/dvd
|
||||||
@@ -37,7 +25,7 @@
|
|||||||
path: /mnt/usr/local/install/redhat/dvd
|
path: /mnt/usr/local/install/redhat/dvd
|
||||||
fstype: none
|
fstype: none
|
||||||
opts: bind
|
opts: bind
|
||||||
state: mounted
|
state: ephemeral
|
||||||
|
|
||||||
- name: Rebuild RPM database inside chroot
|
- name: Rebuild RPM database inside chroot
|
||||||
ansible.builtin.command: "{{ chroot_command }} rpm --rebuilddb"
|
ansible.builtin.command: "{{ chroot_command }} rpm --rebuilddb"
|
||||||
|
|||||||
@@ -16,12 +16,6 @@
|
|||||||
register: bootstrap_rocky_base_result
|
register: bootstrap_rocky_base_result
|
||||||
changed_when: bootstrap_rocky_base_result.rc == 0
|
changed_when: bootstrap_rocky_base_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
|
|
||||||
- name: Install extra packages
|
- name: Install extra packages
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
{{ chroot_command }} dnf --releasever={{ os_version }} --setopt=install_weak_deps=False
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
---
|
---
|
||||||
- name: Bootstrap Ubuntu System
|
- name: Bootstrap Ubuntu System
|
||||||
vars:
|
vars:
|
||||||
bootstrap_ubuntu_release: >-
|
# ubuntu = latest non-LTS, ubuntu-lts = latest LTS
|
||||||
{{ 'plucky' if os == 'ubuntu' else 'noble' }}
|
bootstrap_ubuntu_release_map:
|
||||||
|
ubuntu: plucky
|
||||||
|
ubuntu-lts: noble
|
||||||
|
bootstrap_ubuntu_release: "{{ bootstrap_ubuntu_release_map[os] | default('noble') }}"
|
||||||
bootstrap_ubuntu_package_config: >-
|
bootstrap_ubuntu_package_config: >-
|
||||||
{{
|
{{
|
||||||
lookup('vars', bootstrap_var_key)
|
lookup('vars', bootstrap_var_key)
|
||||||
@@ -47,13 +50,6 @@
|
|||||||
register: bootstrap_ubuntu_base_result
|
register: bootstrap_ubuntu_base_result
|
||||||
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
changed_when: bootstrap_ubuntu_base_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.copy:
|
|
||||||
src: /etc/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
remote_src: true
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Enable universe repository
|
- name: Enable universe repository
|
||||||
ansible.builtin.command: "{{ chroot_command }} sed -i '1s|$| universe|' /etc/apt/sources.list"
|
ansible.builtin.command: "{{ chroot_command }} sed -i '1s|$| universe|' /etc/apt/sources.list"
|
||||||
register: bootstrap_ubuntu_repo_result
|
register: bootstrap_ubuntu_repo_result
|
||||||
|
|||||||
@@ -6,13 +6,6 @@
|
|||||||
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
lookup('vars', bootstrap_var_key) | reject('equalto', '') | join(' ')
|
||||||
}}
|
}}
|
||||||
block:
|
block:
|
||||||
- name: Ensure chroot has resolv.conf
|
|
||||||
ansible.builtin.file:
|
|
||||||
src: /run/NetworkManager/resolv.conf
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
state: link
|
|
||||||
force: true
|
|
||||||
|
|
||||||
- name: Install Void Linux base packages
|
- name: Install Void Linux base packages
|
||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
xbps-install -Sy -r /mnt -R https://repo-default.voidlinux.org/current void-repo-nonfree base-system
|
xbps-install -Sy -r /mnt -R https://repo-default.voidlinux.org/current void-repo-nonfree base-system
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
---
|
---
|
||||||
# Common conditional packages shared across distributions.
|
# Common feature-gated packages. Built as a clean list (no empty strings).
|
||||||
# Arch overrides nftables with iptables-nft; SSH package names vary per distro.
|
# Arch overrides nftables → iptables-nft; SSH package names vary per distro.
|
||||||
bootstrap_common_conditional:
|
bootstrap_common_conditional: >-
|
||||||
- "{{ 'firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}"
|
{{
|
||||||
- "{{ 'ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}"
|
(
|
||||||
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
|
(['firewalld'] if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
+ (['iptables'] if system_cfg.features.firewall.toolkit == 'iptables' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
- "{{ 'tpm2-tools' if system_cfg.luks.enabled else '' }}"
|
+ (['nftables'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
- "{{ 'qemu-guest-agent' if hypervisor_type in ['libvirt', 'proxmox'] else '' }}"
|
+ (['cryptsetup', 'tpm2-tools'] if system_cfg.luks.enabled | bool else [])
|
||||||
- "{{ 'open-vm-tools' if hypervisor_type == 'vmware' else '' }}"
|
+ (['qemu-guest-agent'] if hypervisor_type in ['libvirt', 'proxmox'] else [])
|
||||||
|
+ (['open-vm-tools'] if hypervisor_type == 'vmware' else [])
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
bootstrap_rhel_base: >-
|
bootstrap_rhel_base: >-
|
||||||
{{
|
{{
|
||||||
@@ -20,13 +23,15 @@ bootstrap_rhel_base: >-
|
|||||||
+ bootstrap_common_conditional
|
+ bootstrap_common_conditional
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_rhel_versioned:
|
bootstrap_rhel_versioned: >-
|
||||||
- grub2
|
{{
|
||||||
- "{{ 'grub2-efi-x64' if os_version_major | default('') == '8' else 'grub2-efi' }}"
|
['grub2']
|
||||||
- "{{ 'grub2-tools-extra' if os_version_major | default('') in ['8', '9'] else '' }}"
|
+ (['grub2-efi-x64'] if os_version_major | default('') == '8' else ['grub2-efi'])
|
||||||
- "{{ 'python39' if os_version_major | default('') == '8' else 'python' }}"
|
+ (['grub2-tools-extra'] if os_version_major | default('') in ['8', '9'] else [])
|
||||||
- "{{ 'kernel' if os_version_major | default('') == '10' else '' }}"
|
+ (['python39'] if os_version_major | default('') == '8' else ['python'])
|
||||||
- "{{ 'zram-generator' if os_version_major | default('') in ['9', '10'] else '' }}"
|
+ (['kernel'] if os_version_major | default('') == '10' else [])
|
||||||
|
+ (['zram-generator'] if os_version_major | default('') in ['9', '10'] else [])
|
||||||
|
}}
|
||||||
|
|
||||||
bootstrap_rhel: "{{ bootstrap_rhel_base + bootstrap_rhel_versioned }}"
|
bootstrap_rhel: "{{ bootstrap_rhel_base + bootstrap_rhel_versioned }}"
|
||||||
|
|
||||||
@@ -34,14 +39,14 @@ bootstrap_almalinux: >-
|
|||||||
{{
|
{{
|
||||||
bootstrap_rhel_base
|
bootstrap_rhel_base
|
||||||
+ ['grub2', 'grub2-efi', 'dbus-daemon', 'lrzsz',
|
+ ['grub2', 'grub2-efi', 'dbus-daemon', 'lrzsz',
|
||||||
'nfsv4-client-utils', 'nc', 'ppp', 'zram-generator']
|
'nfsv4-client-utils', 'nc', 'ppp', 'python3', 'zram-generator']
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_rocky: >-
|
bootstrap_rocky: >-
|
||||||
{{
|
{{
|
||||||
bootstrap_rhel_base
|
bootstrap_rhel_base
|
||||||
+ ['grub2', 'grub2-efi', 'nfsv4-client-utils', 'nc', 'ppp',
|
+ ['grub2', 'grub2-efi', 'nfsv4-client-utils', 'nc', 'ppp',
|
||||||
'telnet', 'util-linux-core', 'wget', 'zram-generator']
|
'python3', 'telnet', 'util-linux-core', 'wget', 'zram-generator']
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_fedora: >-
|
bootstrap_fedora: >-
|
||||||
@@ -51,28 +56,18 @@ bootstrap_fedora: >-
|
|||||||
'glibc-langpack-de', 'glibc-langpack-en', 'grub2', 'grub2-efi',
|
'glibc-langpack-de', 'glibc-langpack-en', 'grub2', 'grub2-efi',
|
||||||
'htop', 'iperf3', 'logrotate', 'lrzsz', 'lvm2',
|
'htop', 'iperf3', 'logrotate', 'lrzsz', 'lvm2',
|
||||||
'nc', 'nfs-utils', 'nfsv4-client-utils', 'polkit', 'ppp',
|
'nc', 'nfs-utils', 'nfsv4-client-utils', 'polkit', 'ppp',
|
||||||
'ripgrep', 'shim', 'tmux', 'vim-default-editor',
|
'python3', 'ripgrep', 'shim', 'tmux', 'vim-default-editor',
|
||||||
'wget', 'zoxide', 'zram-generator', 'zstd']
|
'wget', 'zoxide', 'zram-generator', 'zstd']
|
||||||
+ bootstrap_common_conditional
|
+ bootstrap_common_conditional
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_debian_base_common:
|
bootstrap_debian_base_common: >-
|
||||||
- btrfs-progs
|
{{
|
||||||
- cron
|
['btrfs-progs', 'cron', 'gnupg', 'grub-efi', 'grub-efi-amd64-signed',
|
||||||
- gnupg
|
'grub2-common', 'locales', 'logrotate', 'lvm2', 'python3', 'xfsprogs']
|
||||||
- grub-efi
|
+ (['cryptsetup-initramfs'] if system_cfg.luks.enabled | bool else [])
|
||||||
- grub-efi-amd64-signed
|
+ (['openssh-server'] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
- grub2-common
|
}}
|
||||||
- "{{ 'cryptsetup' if system_cfg.luks.enabled else '' }}"
|
|
||||||
- "{{ 'cryptsetup-initramfs' if system_cfg.luks.enabled else '' }}"
|
|
||||||
- locales
|
|
||||||
- logrotate
|
|
||||||
- lvm2
|
|
||||||
- "{{ 'iptables' if system_cfg.features.firewall.toolkit == 'iptables' else '' }}"
|
|
||||||
- "{{ 'nftables' if system_cfg.features.firewall.toolkit == 'nftables' else '' }}"
|
|
||||||
- "{{ 'openssh-server' if system_cfg.features.ssh.enabled | bool else '' }}"
|
|
||||||
- python3
|
|
||||||
- xfsprogs
|
|
||||||
|
|
||||||
bootstrap_debian_extra_common:
|
bootstrap_debian_extra_common:
|
||||||
- apparmor-utils
|
- apparmor-utils
|
||||||
@@ -101,14 +96,16 @@ bootstrap_debian_extra_common:
|
|||||||
- wget
|
- wget
|
||||||
- zstd
|
- zstd
|
||||||
|
|
||||||
bootstrap_debian_extra_versioned:
|
bootstrap_debian_extra_versioned: >-
|
||||||
- linux-image-amd64
|
{{
|
||||||
- "{{ 'duf' if (os_version | string) not in ['10', '11'] else '' }}"
|
['linux-image-amd64']
|
||||||
- "{{ 'fastfetch' if (os_version | string) in ['13', 'unstable'] else '' }}"
|
+ (['duf'] if (os_version | string) not in ['10', '11'] else [])
|
||||||
- "{{ 'neofetch' if (os_version | string) == '12' else '' }}"
|
+ (['fastfetch'] if (os_version | string) in ['13', 'unstable'] else [])
|
||||||
- "{{ 'software-properties-common' if (os_version | string) not in ['13', 'unstable'] else '' }}"
|
+ (['neofetch'] if (os_version | string) == '12' else [])
|
||||||
- "{{ 'systemd-zram-generator' if (os_version | string) not in ['10', '11'] else '' }}"
|
+ (['software-properties-common'] if (os_version | string) not in ['13', 'unstable'] else [])
|
||||||
- "{{ 'tldr' if (os_version | string) not in ['13', 'unstable'] else '' }}"
|
+ (['systemd-zram-generator'] if (os_version | string) not in ['10', '11'] else [])
|
||||||
|
+ (['tldr'] if (os_version | string) not in ['13', 'unstable'] else [])
|
||||||
|
}}
|
||||||
|
|
||||||
bootstrap_debian:
|
bootstrap_debian:
|
||||||
base: "{{ bootstrap_debian_base_common }}"
|
base: "{{ bootstrap_debian_base_common }}"
|
||||||
@@ -138,30 +135,36 @@ bootstrap_archlinux: >-
|
|||||||
['base', 'btrfs-progs', 'cronie', 'dhcpcd', 'efibootmgr', 'fastfetch',
|
['base', 'btrfs-progs', 'cronie', 'dhcpcd', 'efibootmgr', 'fastfetch',
|
||||||
'fish', 'fzf', 'grub', 'htop', 'libpwquality', 'linux', 'logrotate',
|
'fish', 'fzf', 'grub', 'htop', 'libpwquality', 'linux', 'logrotate',
|
||||||
'lrzsz', 'lsof', 'lvm2', 'ncdu', 'networkmanager', 'nfs-utils',
|
'lrzsz', 'lsof', 'lvm2', 'ncdu', 'networkmanager', 'nfs-utils',
|
||||||
'ppp', 'prometheus-node-exporter', 'python-psycopg2', 'reflector',
|
'ppp', 'python', 'reflector',
|
||||||
'rsync', 'sudo', 'tldr', 'tmux', 'vim', 'wireguard-tools', 'zram-generator']
|
'rsync', 'sudo', 'tldr', 'tmux', 'vim', 'zram-generator']
|
||||||
+ [('openssh' if system_cfg.features.ssh.enabled | bool else '')]
|
+ (['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
+ [('iptables-nft' if system_cfg.features.firewall.toolkit == 'nftables' else '')]
|
+ (['iptables-nft'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
+ (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
|
+ (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_alpine: >-
|
bootstrap_alpine: >-
|
||||||
{{
|
{{
|
||||||
['alpine-base', 'vim']
|
['alpine-base', 'btrfs-progs', 'chrony', 'curl', 'e2fsprogs',
|
||||||
+ [('openssh' if system_cfg.features.ssh.enabled | bool else '')]
|
'linux-lts', 'logrotate', 'lvm2', 'python3', 'rsync', 'sudo',
|
||||||
|
'util-linux', 'vim', 'xfsprogs']
|
||||||
|
+ (['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
+ bootstrap_common_conditional
|
+ bootstrap_common_conditional
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_opensuse: >-
|
bootstrap_opensuse: >-
|
||||||
{{
|
{{
|
||||||
['vim']
|
['btrfs-progs', 'chrony', 'curl', 'e2fsprogs',
|
||||||
+ [('openssh' if system_cfg.features.ssh.enabled | bool else '')]
|
'glibc-locale', 'kernel-default', 'logrotate', 'lvm2', 'NetworkManager',
|
||||||
|
'python3', 'rsync', 'sudo', 'vim', 'xfsprogs']
|
||||||
|
+ (['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
+ bootstrap_common_conditional
|
+ bootstrap_common_conditional
|
||||||
}}
|
}}
|
||||||
|
|
||||||
bootstrap_void: >-
|
bootstrap_void: >-
|
||||||
{{
|
{{
|
||||||
['vim']
|
['btrfs-progs', 'chrony', 'curl', 'dhcpcd', 'e2fsprogs',
|
||||||
+ [('openssh' if system_cfg.features.ssh.enabled | bool else '')]
|
'logrotate', 'lvm2', 'python3', 'rsync', 'sudo',
|
||||||
|
'vim', 'xfsprogs']
|
||||||
|
+ (['openssh'] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
+ bootstrap_common_conditional
|
+ bootstrap_common_conditional
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
---
|
---
|
||||||
cis_permission_targets: >-
|
# Platform-specific binary names for CIS permission targets
|
||||||
{{
|
cis_fusermount_binary: "{{ 'fusermount3' if is_rhel | default(false) | bool else 'fusermount' }}"
|
||||||
[
|
cis_write_binary: "{{ 'write' if is_rhel | default(false) | bool else 'wall' }}"
|
||||||
{ "path": "/mnt/etc/ssh/sshd_config", "mode": "0600" },
|
|
||||||
{ "path": "/mnt/etc/cron.hourly", "mode": "0700" },
|
cis_permission_targets:
|
||||||
{ "path": "/mnt/etc/cron.daily", "mode": "0700" },
|
- { path: "/mnt/etc/ssh/sshd_config", mode: "0600" }
|
||||||
{ "path": "/mnt/etc/cron.weekly", "mode": "0700" },
|
- { path: "/mnt/etc/cron.hourly", mode: "0700" }
|
||||||
{ "path": "/mnt/etc/cron.monthly", "mode": "0700" },
|
- { path: "/mnt/etc/cron.daily", mode: "0700" }
|
||||||
{ "path": "/mnt/etc/cron.d", "mode": "0700" },
|
- { path: "/mnt/etc/cron.weekly", mode: "0700" }
|
||||||
{ "path": "/mnt/etc/crontab", "mode": "0600" },
|
- { path: "/mnt/etc/cron.monthly", mode: "0700" }
|
||||||
{ "path": "/mnt/etc/logrotate.conf", "mode": "0644" },
|
- { path: "/mnt/etc/cron.d", mode: "0700" }
|
||||||
{ "path": "/mnt/usr/sbin/pppd", "mode": "0754" } if os != "rhel" else None,
|
- { path: "/mnt/etc/crontab", mode: "0600" }
|
||||||
{
|
- { path: "/mnt/etc/logrotate.conf", mode: "0644" }
|
||||||
"path": "/mnt/usr/bin/"
|
- { path: "/mnt/usr/sbin/pppd", mode: "0754" }
|
||||||
+ ("fusermount3" if os in ["archlinux", "fedora", "rocky"] or os == "rhel" or (os == "debian" and (os_version | string) == "12") else "fusermount"),
|
- { path: "/mnt/usr/bin/{{ cis_fusermount_binary }}", mode: "0755" }
|
||||||
"mode": "755"
|
- { path: "/mnt/usr/bin/{{ cis_write_binary }}", mode: "0755" }
|
||||||
},
|
|
||||||
{ "path": "/mnt/usr/bin/" + ("write.ul" if os == "debian" and (os_version | string) == "11" else "write"), "mode": "755" }
|
|
||||||
] | reject("none")
|
|
||||||
}}
|
|
||||||
|
|||||||
@@ -5,11 +5,19 @@
|
|||||||
regexp: "^(\\s*)umask\\s+\\d+"
|
regexp: "^(\\s*)umask\\s+\\d+"
|
||||||
line: "umask 027"
|
line: "umask 027"
|
||||||
|
|
||||||
|
# Non-RHEL/non-Debian distros: loop evaluates to [] (intentional skip)
|
||||||
- name: Prevent Login to Accounts With Empty Password
|
- name: Prevent Login to Accounts With Empty Password
|
||||||
ansible.builtin.replace:
|
ansible.builtin.replace:
|
||||||
dest: "{{ item }}"
|
dest: "{{ item }}"
|
||||||
regexp: "\\s*nullok"
|
regexp: "\\s*nullok"
|
||||||
replace: ""
|
replace: ""
|
||||||
loop:
|
loop: >-
|
||||||
- /mnt/etc/pam.d/system-auth
|
{{
|
||||||
- /mnt/etc/pam.d/password-auth
|
['/mnt/etc/pam.d/system-auth', '/mnt/etc/pam.d/password-auth']
|
||||||
|
if is_rhel | bool
|
||||||
|
else (
|
||||||
|
['/mnt/etc/pam.d/common-auth', '/mnt/etc/pam.d/common-password']
|
||||||
|
if is_debian | bool
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Configure System Cryptography Policy
|
- name: Configure System Cryptography Policy
|
||||||
when: os == "rhel" or os in ["almalinux", "rocky"]
|
when: os in (os_family_rhel | difference(['fedora']))
|
||||||
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
ansible.builtin.command: "{{ chroot_command }} /usr/bin/update-crypto-policies --set DEFAULT:NO-SHA1"
|
||||||
register: cis_crypto_policy_result
|
register: cis_crypto_policy_result
|
||||||
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
|
changed_when: "'Setting system-wide crypto-policies to' in cis_crypto_policy_result.stdout"
|
||||||
@@ -9,4 +9,4 @@
|
|||||||
ansible.builtin.command: >
|
ansible.builtin.command: >
|
||||||
{{ chroot_command }} systemctl mask {{ 'nftables' if system_cfg.features.firewall.toolkit == 'iptables' else 'iptables' }} bluetooth rpcbind
|
{{ chroot_command }} systemctl mask {{ 'nftables' if system_cfg.features.firewall.toolkit == 'iptables' else 'iptables' }} bluetooth rpcbind
|
||||||
register: cis_mask_services_result
|
register: cis_mask_services_result
|
||||||
changed_when: cis_mask_services_result.rc == 0
|
changed_when: "'Created symlink' in cis_mask_services_result.stderr"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
install hfs /bin/false
|
install hfs /bin/false
|
||||||
install hfsplus /bin/false
|
install hfsplus /bin/false
|
||||||
install cramfs /bin/false
|
install cramfs /bin/false
|
||||||
|
# Note: disabling squashfs breaks snap (Ubuntu). Remove for snap-dependent hosts.
|
||||||
install squashfs /bin/false
|
install squashfs /bin/false
|
||||||
install udf /bin/false
|
install udf /bin/false
|
||||||
install usb-storage /bin/false
|
install usb-storage /bin/false
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
loop: "{{ cis_permission_targets }}"
|
loop: "{{ cis_permission_targets }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
register: cis_permission_stats
|
register: cis_permission_stats
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
@@ -13,4 +15,6 @@
|
|||||||
group: "{{ item.item.group | default(omit) }}"
|
group: "{{ item.item.group | default(omit) }}"
|
||||||
mode: "{{ item.item.mode }}"
|
mode: "{{ item.item.mode }}"
|
||||||
loop: "{{ cis_permission_stats.results }}"
|
loop: "{{ cis_permission_stats.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item.path }}"
|
||||||
when: item.stat.exists
|
when: item.stat.exists
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
- { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, content: ocredit = -1 }
|
||||||
- { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 }
|
- { path: /mnt/etc/security/pwquality.conf, content: lcredit = -1 }
|
||||||
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: umask 077 }
|
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: umask 077 }
|
||||||
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: export TMOUT=3000 }
|
- { path: '/mnt/etc/{{ "bashrc" if is_rhel else "bash.bashrc" }}', content: export TMOUT=900 }
|
||||||
- { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if os == "fedora" else "etc/systemd/journald.conf" }}', content: Storage=persistent }
|
- { path: '/mnt/{{ "usr/lib/systemd/journald.conf" if is_rhel | bool else "etc/systemd/journald.conf" }}', content: Storage=persistent }
|
||||||
- { path: /mnt/etc/sudoers, content: Defaults logfile="/var/log/sudo.log" }
|
- { 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/su, content: auth required pam_wheel.so }
|
||||||
- path: >-
|
- path: >-
|
||||||
@@ -44,3 +44,5 @@
|
|||||||
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
|
password [success=1 default=ignore] pam_unix.so obscure sha512 remember=5
|
||||||
- { path: /mnt/etc/hosts.deny, content: "ALL: ALL" }
|
- { path: /mnt/etc/hosts.deny, content: "ALL: ALL" }
|
||||||
- { path: /mnt/etc/hosts.allow, content: "sshd: ALL" }
|
- { path: /mnt/etc/hosts.allow, content: "sshd: ALL" }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.content }}"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
- { option: GSSAPIAuthentication, value: "no" }
|
- { option: GSSAPIAuthentication, value: "no" }
|
||||||
- { option: AllowAgentForwarding, value: "no" }
|
- { option: AllowAgentForwarding, value: "no" }
|
||||||
- { option: AllowTcpForwarding, value: "no" }
|
- { option: AllowTcpForwarding, value: "no" }
|
||||||
- { option: ChallengeResponseAuthentication, value: "no" }
|
- { option: KbdInteractiveAuthentication, value: "no" }
|
||||||
- { option: GatewayPorts, value: "no" }
|
- { option: GatewayPorts, value: "no" }
|
||||||
- { option: X11Forwarding, value: "no" }
|
- { option: X11Forwarding, value: "no" }
|
||||||
- { option: PermitUserEnvironment, value: "no" }
|
- { option: PermitUserEnvironment, value: "no" }
|
||||||
@@ -29,17 +29,34 @@
|
|||||||
- { option: ClientAliveCountMax, value: "1" }
|
- { option: ClientAliveCountMax, value: "1" }
|
||||||
- { option: PermitTunnel, value: "no" }
|
- { option: PermitTunnel, value: "no" }
|
||||||
- { option: Banner, value: /etc/issue.net }
|
- { option: Banner, value: /etc/issue.net }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.option }}"
|
||||||
|
|
||||||
|
- name: Detect target OpenSSH version
|
||||||
|
ansible.builtin.shell: >-
|
||||||
|
set -o pipefail && {{ chroot_command }} ssh -V 2>&1 | grep -oP 'OpenSSH_\K[0-9]+\.[0-9]+'
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: cis_sshd_openssh_version
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
- name: Append CIS specific configurations to sshd_config
|
- name: Append CIS specific configurations to sshd_config
|
||||||
|
vars:
|
||||||
|
cis_sshd_has_mlkem: "{{ (cis_sshd_openssh_version.stdout | default('0.0') is version('9.9', '>=')) }}"
|
||||||
|
cis_sshd_kex: >-
|
||||||
|
{{
|
||||||
|
(['mlkem768x25519-sha256'] if cis_sshd_has_mlkem | bool else [])
|
||||||
|
+ ['curve25519-sha256@libssh.org', 'ecdh-sha2-nistp521', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256']
|
||||||
|
}}
|
||||||
ansible.builtin.blockinfile:
|
ansible.builtin.blockinfile:
|
||||||
path: /mnt/etc/ssh/sshd_config
|
path: /mnt/etc/ssh/sshd_config
|
||||||
marker: "# {mark} CIS SSH HARDENING"
|
marker: "# {mark} CIS SSH HARDENING"
|
||||||
block: |-
|
block: |-
|
||||||
## CIS Specific
|
## CIS Specific
|
||||||
Protocol 2
|
|
||||||
### Ciphers and keying ###
|
### Ciphers and keying ###
|
||||||
RekeyLimit 512M 6h
|
RekeyLimit 512M 6h
|
||||||
KexAlgorithms mlkem768x25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256
|
KexAlgorithms {{ cis_sshd_kex | join(',') }}
|
||||||
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
||||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
|
||||||
###########################
|
###########################
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: |
|
content: |
|
||||||
## CIS Sysctl configurations
|
## CIS Sysctl configurations
|
||||||
|
fs.suid_dumpable=0
|
||||||
|
kernel.dmesg_restrict=1
|
||||||
kernel.yama.ptrace_scope=1
|
kernel.yama.ptrace_scope=1
|
||||||
kernel.randomize_va_space=2
|
kernel.randomize_va_space=2
|
||||||
# Network
|
# Network
|
||||||
|
# Disable forwarding; override in inventory for routers/containers
|
||||||
net.ipv4.ip_forward=0
|
net.ipv4.ip_forward=0
|
||||||
net.ipv4.tcp_syncookies=1
|
net.ipv4.tcp_syncookies=1
|
||||||
net.ipv4.icmp_echo_ignore_broadcasts=1
|
net.ipv4.icmp_echo_ignore_broadcasts=1
|
||||||
@@ -24,6 +27,7 @@
|
|||||||
net.ipv4.conf.default.send_redirects=0
|
net.ipv4.conf.default.send_redirects=0
|
||||||
net.ipv4.conf.default.accept_redirects=0
|
net.ipv4.conf.default.accept_redirects=0
|
||||||
net.ipv6.conf.all.accept_redirects=0
|
net.ipv6.conf.all.accept_redirects=0
|
||||||
|
# Disable IPv6; override in inventory if IPv6 is needed
|
||||||
net.ipv6.conf.all.disable_ipv6=1
|
net.ipv6.conf.all.disable_ipv6=1
|
||||||
net.ipv6.conf.default.accept_redirects=0
|
net.ipv6.conf.default.accept_redirects=0
|
||||||
net.ipv6.conf.default.disable_ipv6=1
|
net.ipv6.conf.default.disable_ipv6=1
|
||||||
|
|||||||
21
roles/cis/vars/main.yml
Normal file
21
roles/cis/vars/main.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
# OS-specific binary names for CIS permission targets.
|
||||||
|
# fusermount3 is the modern name; older distros still use fusermount.
|
||||||
|
cis_fusermount_binary: >-
|
||||||
|
{{
|
||||||
|
'fusermount3'
|
||||||
|
if (
|
||||||
|
os in ['archlinux', 'fedora', 'rocky', 'rhel']
|
||||||
|
or (os == 'debian' and (os_version | string) not in ['10', '11'])
|
||||||
|
or (os == 'almalinux')
|
||||||
|
)
|
||||||
|
else 'fusermount'
|
||||||
|
}}
|
||||||
|
|
||||||
|
# write.ul is the Debian 11 name; all others use write.
|
||||||
|
cis_write_binary: >-
|
||||||
|
{{
|
||||||
|
'write.ul'
|
||||||
|
if (os == 'debian' and (os_version | string) == '11')
|
||||||
|
else 'write'
|
||||||
|
}}
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
---
|
---
|
||||||
|
# Post-reboot verification
|
||||||
|
cleanup_verify_boot: true
|
||||||
|
cleanup_boot_timeout: 300
|
||||||
|
cleanup_remove_on_failure: true
|
||||||
|
|
||||||
|
# Libvirt paths
|
||||||
cleanup_libvirt_image_dir: >-
|
cleanup_libvirt_image_dir: >-
|
||||||
{{
|
{{
|
||||||
system_cfg.path
|
system_cfg.path
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: running
|
state: running
|
||||||
|
|
||||||
|
# delegate_to inventory_hostname: overrides play-level localhost to run wait_for_connection against the VM
|
||||||
- name: Wait for VM to boot up
|
- name: Wait for VM to boot up
|
||||||
delegate_to: "{{ inventory_hostname }}"
|
delegate_to: "{{ inventory_hostname }}"
|
||||||
ansible.builtin.wait_for_connection:
|
ansible.builtin.wait_for_connection:
|
||||||
|
|||||||
@@ -3,25 +3,32 @@
|
|||||||
when: hypervisor_type == "proxmox"
|
when: hypervisor_type == "proxmox"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
block:
|
module_defaults:
|
||||||
- name: Cleanup Setup Disks
|
|
||||||
community.proxmox.proxmox_disk:
|
community.proxmox.proxmox_disk:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
name: "{{ hostname }}"
|
|
||||||
vmid: "{{ system_cfg.id }}"
|
|
||||||
disk: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- ide0
|
|
||||||
- ide2
|
|
||||||
|
|
||||||
- name: Start the VM
|
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
block:
|
||||||
|
- name: Cleanup Setup Disks
|
||||||
|
community.proxmox.proxmox_disk:
|
||||||
|
name: "{{ hostname }}"
|
||||||
|
vmid: "{{ system_cfg.id }}"
|
||||||
|
disk: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop: >-
|
||||||
|
{{
|
||||||
|
['ide0', 'ide2']
|
||||||
|
+ (['ide1'] if not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso') else [])
|
||||||
|
}}
|
||||||
|
failed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Start the VM
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -6,16 +6,7 @@
|
|||||||
ansible.builtin.include_tasks: shutdown.yml
|
ansible.builtin.include_tasks: shutdown.yml
|
||||||
|
|
||||||
- name: Cleanup hypervisor resources
|
- name: Cleanup hypervisor resources
|
||||||
ansible.builtin.include_tasks: proxmox.yml
|
ansible.builtin.include_tasks: "{{ hypervisor_type }}.yml"
|
||||||
|
|
||||||
- name: Cleanup vCenter resources
|
|
||||||
ansible.builtin.include_tasks: vmware.yml
|
|
||||||
|
|
||||||
- name: Cleanup libvirt resources
|
|
||||||
ansible.builtin.include_tasks: libvirt.yml
|
|
||||||
|
|
||||||
- name: Cleanup Xen resources
|
|
||||||
ansible.builtin.include_tasks: xen.yml
|
|
||||||
|
|
||||||
- name: Determine post-reboot connectivity
|
- name: Determine post-reboot connectivity
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
@@ -34,25 +25,27 @@
|
|||||||
)
|
)
|
||||||
) | bool
|
) | bool
|
||||||
}}
|
}}
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Check VM accessibility after reboot
|
- name: Check VM accessibility after reboot
|
||||||
when:
|
when:
|
||||||
|
- cleanup_verify_boot | bool
|
||||||
- system_cfg.type == "virtual"
|
- system_cfg.type == "virtual"
|
||||||
- cleanup_post_reboot_can_connect | bool
|
- cleanup_post_reboot_can_connect | bool
|
||||||
block:
|
block:
|
||||||
- name: Attempt to connect to VM
|
- name: Attempt to connect to VM
|
||||||
delegate_to: "{{ inventory_hostname }}"
|
delegate_to: "{{ inventory_hostname }}"
|
||||||
ansible.builtin.wait_for_connection:
|
ansible.builtin.wait_for_connection:
|
||||||
timeout: 300
|
timeout: "{{ cleanup_boot_timeout }}"
|
||||||
register: cleanup_vm_connection_check
|
register: cleanup_vm_connection_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: VM failed to boot - initiate cleanup
|
- name: VM failed to boot - initiate cleanup
|
||||||
when:
|
when:
|
||||||
|
- cleanup_remove_on_failure | bool
|
||||||
- cleanup_vm_connection_check is defined
|
- cleanup_vm_connection_check is defined
|
||||||
- cleanup_vm_connection_check.failed | bool
|
- cleanup_vm_connection_check.failed | bool
|
||||||
|
- virtualization_vm_created_in_run | default(false) | bool
|
||||||
block:
|
block:
|
||||||
- name: VM boot failure detected - removing VM
|
- name: VM boot failure detected - removing VM
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
@@ -61,32 +54,23 @@
|
|||||||
This VM was created in the current playbook run and will be removed
|
This VM was created in the current playbook run and will be removed
|
||||||
to prevent orphaned resources.
|
to prevent orphaned resources.
|
||||||
|
|
||||||
- name: Remove VM for libvirt
|
- name: Remove failed libvirt VM
|
||||||
when:
|
when: hypervisor_type == "libvirt"
|
||||||
- hypervisor_type == "libvirt"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Destroy libvirt VM
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: destroyed
|
state: destroyed
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
- name: Undefine VM for libvirt
|
- name: Undefine libvirt VM
|
||||||
when:
|
|
||||||
- hypervisor_type == "libvirt"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
command: undefine
|
command: undefine
|
||||||
|
|
||||||
- name: Remove VM disk for libvirt
|
- name: Remove libvirt VM disks
|
||||||
when:
|
|
||||||
- hypervisor_type == "libvirt"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
state: absent
|
state: absent
|
||||||
@@ -94,83 +78,66 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.path }}"
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Remove cloud-init disk for libvirt
|
- name: Remove libvirt cloud-init disk
|
||||||
when:
|
|
||||||
- hypervisor_type == "libvirt"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ virtualization_libvirt_cloudinit_path }}"
|
path: "{{ virtualization_libvirt_cloudinit_path }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Remove VM for proxmox
|
- name: Remove failed Proxmox VM
|
||||||
when:
|
when: hypervisor_type == "proxmox"
|
||||||
- hypervisor_type == "proxmox"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
module_defaults:
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
no_log: true
|
||||||
|
block:
|
||||||
|
- name: Stop Proxmox VM
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
state: stopped
|
state: stopped
|
||||||
|
|
||||||
- name: Delete VM for proxmox
|
- name: Delete Proxmox VM
|
||||||
when:
|
|
||||||
- hypervisor_type == "proxmox"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
state: absent
|
state: absent
|
||||||
unprivileged: false
|
unprivileged: false
|
||||||
|
|
||||||
- name: Remove VM for VMware
|
- name: Remove failed VMware VM
|
||||||
when:
|
when: hypervisor_type == "vmware"
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
module_defaults:
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
no_log: true
|
||||||
|
block:
|
||||||
|
- name: Power off VMware VM
|
||||||
|
community.vmware.vmware_guest:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
folder: "{{ system_cfg.path | default('/') }}"
|
folder: "{{ system_cfg.path | default('/') }}"
|
||||||
state: poweredoff
|
state: poweredoff
|
||||||
|
|
||||||
- name: Delete VM for VMware
|
- name: Delete VMware VM
|
||||||
when:
|
|
||||||
- hypervisor_type == "vmware"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
folder: "{{ system_cfg.path | default('/') }}"
|
folder: "{{ system_cfg.path | default('/') }}"
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Destroy Xen VM if running
|
- name: Remove failed Xen VM
|
||||||
when:
|
when: hypervisor_type == "xen"
|
||||||
- hypervisor_type == "xen"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
block:
|
||||||
|
- name: Destroy Xen VM if running
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- xl
|
- xl
|
||||||
@@ -180,12 +147,7 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: cleanup_xen_destroy.rc == 0
|
changed_when: cleanup_xen_destroy.rc == 0
|
||||||
|
|
||||||
- name: Remove Xen VM disk
|
- name: Remove Xen VM disks
|
||||||
when:
|
|
||||||
- hypervisor_type == "xen"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item.path }}"
|
path: "{{ item.path }}"
|
||||||
state: absent
|
state: absent
|
||||||
@@ -194,11 +156,6 @@
|
|||||||
label: "{{ item.path }}"
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Remove Xen VM config file
|
- name: Remove Xen VM config file
|
||||||
when:
|
|
||||||
- hypervisor_type == "xen"
|
|
||||||
- virtualization_vm_created_in_run | default(false) | bool
|
|
||||||
delegate_to: localhost
|
|
||||||
become: false
|
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/tmp/xen-{{ hostname }}.cfg"
|
path: "/tmp/xen-{{ hostname }}.cfg"
|
||||||
state: absent
|
state: absent
|
||||||
|
|||||||
@@ -3,36 +3,55 @@
|
|||||||
when: hypervisor_type == "vmware"
|
when: hypervisor_type == "vmware"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
block:
|
module_defaults:
|
||||||
- name: Remove CD-ROM from VM in vCenter
|
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
name: "{{ hostname }}"
|
|
||||||
cdrom:
|
|
||||||
- controller_number: 0
|
|
||||||
unit_number: 0
|
|
||||||
controller_type: sata
|
|
||||||
type: iso
|
|
||||||
iso_path: "{{ boot_iso }}"
|
|
||||||
state: absent
|
|
||||||
- controller_number: 0
|
|
||||||
unit_number: 1
|
|
||||||
controller_type: sata
|
|
||||||
type: iso
|
|
||||||
iso_path: "{{ rhel_iso if rhel_iso is defined and rhel_iso | length > 0 else omit }}"
|
|
||||||
state: absent
|
|
||||||
failed_when: false
|
|
||||||
|
|
||||||
- name: Start VM in vCenter
|
|
||||||
vmware.vmware.vm_powerstate:
|
vmware.vmware.vm_powerstate:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
no_log: true
|
||||||
|
block:
|
||||||
|
- name: Remove CD-ROM from VM in vCenter
|
||||||
|
community.vmware.vmware_guest:
|
||||||
|
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,
|
||||||
|
'state': 'absent'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if (rhel_iso is defined and rhel_iso | length > 0
|
||||||
|
and not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso'))
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Start VM in vCenter
|
||||||
|
vmware.vmware.vm_powerstate:
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: powered-on
|
state: powered-on
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
when: hypervisor_type == "xen"
|
when: hypervisor_type == "xen"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
vars:
|
||||||
|
xen_installer_media_enabled: "{{ xen_installer_media_enabled | default(false) }}"
|
||||||
block:
|
block:
|
||||||
- name: Ensure Xen disk definitions exist
|
- name: Ensure Xen disk definitions exist
|
||||||
when: virtualization_xen_disks is not defined
|
when: virtualization_xen_disks is not defined
|
||||||
|
|||||||
@@ -23,6 +23,22 @@
|
|||||||
- /mnt/etc/motd.d/insights-client
|
- /mnt/etc/motd.d/insights-client
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Create login banner
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "{{ item }}"
|
||||||
|
content: |
|
||||||
|
**************************************************************
|
||||||
|
* WARNING: Unauthorized access to this system is prohibited. *
|
||||||
|
* All activities are monitored and logged. *
|
||||||
|
* Disconnect immediately if you are not an authorized user. *
|
||||||
|
**************************************************************
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
loop:
|
||||||
|
- /mnt/etc/issue
|
||||||
|
- /mnt/etc/issue.net
|
||||||
|
|
||||||
- name: Configure sudo banner
|
- name: Configure sudo banner
|
||||||
when: system_cfg.features.banner.sudo | bool
|
when: system_cfg.features.banner.sudo | bool
|
||||||
block:
|
block:
|
||||||
|
|||||||
@@ -21,19 +21,19 @@
|
|||||||
|
|
||||||
- name: Check existing EFI boot entries
|
- name: Check existing EFI boot entries
|
||||||
ansible.builtin.command: efibootmgr
|
ansible.builtin.command: efibootmgr
|
||||||
register: _efi_entries
|
register: configuration_efi_entries
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Ensure EFI boot entry exists
|
- name: Ensure EFI boot entry exists
|
||||||
when: ('* ' + _efi_vendor) not in _efi_entries.stdout
|
when: ('* ' + _efi_vendor) not in configuration_efi_entries.stdout
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
efibootmgr -c
|
efibootmgr -c
|
||||||
-L '{{ _efi_vendor }}'
|
-L '{{ _efi_vendor }}'
|
||||||
-d '{{ install_drive }}'
|
-d '{{ install_drive }}'
|
||||||
-p 1
|
-p 1
|
||||||
-l '\EFI\{{ _efi_vendor }}\{{ _efi_loader }}'
|
-l '\EFI\{{ _efi_vendor }}\{{ _efi_loader }}'
|
||||||
register: _efi_entry_result
|
register: configuration_efi_entry_result
|
||||||
changed_when: _efi_entry_result.rc == 0
|
changed_when: configuration_efi_entry_result.rc == 0
|
||||||
|
|
||||||
- name: Ensure lvm2 for non btrfs filesystems
|
- name: Ensure lvm2 for non btrfs filesystems
|
||||||
when: os == "archlinux" and system_cfg.filesystem != "btrfs"
|
when: os == "archlinux" and system_cfg.filesystem != "btrfs"
|
||||||
|
|||||||
@@ -59,6 +59,14 @@
|
|||||||
when: configuration_luks_auto_method == 'keyfile'
|
when: configuration_luks_auto_method == 'keyfile'
|
||||||
ansible.builtin.include_tasks: encryption/keyfile.yml
|
ansible.builtin.include_tasks: encryption/keyfile.yml
|
||||||
|
|
||||||
|
- name: Record final LUKS auto-decrypt method
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
configuration_luks_final_method: "{{ configuration_luks_auto_method }}"
|
||||||
|
|
||||||
|
- name: Report LUKS auto-decrypt configuration
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "LUKS auto-decrypt method: {{ configuration_luks_final_method }}"
|
||||||
|
|
||||||
- name: Build LUKS parameters
|
- name: Build LUKS parameters
|
||||||
vars:
|
vars:
|
||||||
luks_keyfile_in_use: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
luks_keyfile_in_use: "{{ configuration_luks_auto_method == 'keyfile' }}"
|
||||||
@@ -142,7 +150,7 @@
|
|||||||
regexp: "^HOOKS="
|
regexp: "^HOOKS="
|
||||||
line: >-
|
line: >-
|
||||||
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
|
HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole
|
||||||
block sd-encrypt lvm2 filesystems fsck)
|
block sd-encrypt{{ ' lvm2' if system_cfg.filesystem != 'btrfs' else '' }} filesystems fsck)
|
||||||
|
|
||||||
- name: Read mkinitcpio configuration
|
- name: Read mkinitcpio configuration
|
||||||
when: os == 'archlinux'
|
when: os == 'archlinux'
|
||||||
@@ -246,7 +254,7 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: "{{ configuration_kernel_cmdline_new }}\n"
|
content: "{{ configuration_kernel_cmdline_new }}\n"
|
||||||
|
|
||||||
- name: Find BLS entries
|
- name: Find BLS entries for encryption kernel cmdline
|
||||||
when: is_rhel | bool
|
when: is_rhel | bool
|
||||||
ansible.builtin.find:
|
ansible.builtin.find:
|
||||||
paths: /mnt/boot/loader/entries
|
paths: /mnt/boot/loader/entries
|
||||||
|
|||||||
@@ -104,6 +104,13 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
|
- name: Warn about keyfile enrollment failure
|
||||||
|
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
LUKS keyfile enrollment failed — falling back to manual unlock at boot.
|
||||||
|
The system will prompt for the LUKS passphrase during startup.
|
||||||
|
|
||||||
- name: Fallback to manual LUKS unlock if keyfile enrollment failed
|
- name: Fallback to manual LUKS unlock if keyfile enrollment failed
|
||||||
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
when: (configuration_luks_keyfile_unlock_test_after.rc | default(1)) != 0
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
- name: Enroll TPM2 for LUKS
|
- name: Enroll TPM2 for LUKS
|
||||||
block:
|
block:
|
||||||
|
# Tempfile in chroot /tmp — accessible by both chroot and host commands
|
||||||
- name: Create temporary passphrase file for TPM2 enrollment
|
- name: Create temporary passphrase file for TPM2 enrollment
|
||||||
ansible.builtin.tempfile:
|
ansible.builtin.tempfile:
|
||||||
path: /mnt/tmp
|
path: /mnt/tmp
|
||||||
@@ -78,6 +79,12 @@
|
|||||||
chroot stderr={{ configuration_luks_tpm2_enroll_chroot.stderr | default('') }},
|
chroot stderr={{ configuration_luks_tpm2_enroll_chroot.stderr | default('') }},
|
||||||
host stderr={{ configuration_luks_tpm2_enroll_host.stderr | default('') }}
|
host stderr={{ configuration_luks_tpm2_enroll_host.stderr | default('') }}
|
||||||
rescue:
|
rescue:
|
||||||
|
- name: Warn about TPM2 enrollment failure
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
TPM2 enrollment failed — falling back to keyfile auto-decrypt.
|
||||||
|
The system will use a keyfile instead of TPM2 for automatic LUKS unlock.
|
||||||
|
|
||||||
- name: Fallback to keyfile auto-decrypt
|
- name: Fallback to keyfile auto-decrypt
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
configuration_luks_auto_method: keyfile
|
configuration_luks_auto_method: keyfile
|
||||||
@@ -87,4 +94,3 @@
|
|||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
path: "{{ configuration_luks_tpm2_passphrase_tempfile.path }}"
|
||||||
state: absent
|
state: absent
|
||||||
changed_when: false
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
set smartindent
|
set smartindent
|
||||||
set mouse=a
|
set mouse=a
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
marker: ""
|
marker: "# {mark} CUSTOM VIM CONFIG"
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Add memory tuning parameters
|
- name: Add memory tuning parameters
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
vm.dirty_background_ratio=1
|
vm.dirty_background_ratio=1
|
||||||
vm.dirty_ratio=10
|
vm.dirty_ratio=10
|
||||||
vm.page-cluster=10
|
vm.page-cluster=10
|
||||||
marker: ""
|
marker: "# {mark} MEMORY TUNING"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Create zram config
|
- name: Create zram config
|
||||||
@@ -45,28 +45,3 @@
|
|||||||
src: custom.sh.j2
|
src: custom.sh.j2
|
||||||
dest: /mnt/etc/profile.d/custom.sh
|
dest: /mnt/etc/profile.d/custom.sh
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Create login banner
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: "{{ item }}"
|
|
||||||
content: |
|
|
||||||
**************************************************************
|
|
||||||
* WARNING: Unauthorized access to this system is prohibited. *
|
|
||||||
* All activities are monitored and logged. *
|
|
||||||
* Disconnect immediately if you are not an authorized user. *
|
|
||||||
**************************************************************
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: "0644"
|
|
||||||
loop:
|
|
||||||
- /mnt/etc/issue
|
|
||||||
- /mnt/etc/issue.net
|
|
||||||
|
|
||||||
- name: Remove motd files
|
|
||||||
when: os == "rhel"
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: absent
|
|
||||||
loop:
|
|
||||||
- /mnt/etc/motd.d/cockpit
|
|
||||||
- /mnt/etc/motd.d/insights-client
|
|
||||||
|
|||||||
@@ -23,8 +23,19 @@
|
|||||||
regexp: "(xfs.*?)(attr2)"
|
regexp: "(xfs.*?)(attr2)"
|
||||||
replace: "\\1allocsize=64m"
|
replace: "\\1allocsize=64m"
|
||||||
|
|
||||||
|
- name: Remove RHEL ISO fstab entry when not using local repo
|
||||||
|
when:
|
||||||
|
- os == "rhel"
|
||||||
|
- system_cfg.features.rhel_repo.source != "iso"
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /mnt/etc/fstab
|
||||||
|
regexp: "^.*\\/dvd.*$"
|
||||||
|
state: absent
|
||||||
|
|
||||||
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
- name: Replace ISO UUID entry with /dev/sr0 in fstab
|
||||||
when: os == "rhel"
|
when:
|
||||||
|
- os == "rhel"
|
||||||
|
- system_cfg.features.rhel_repo.source == "iso"
|
||||||
vars:
|
vars:
|
||||||
configuration_fstab_dvd_line: >-
|
configuration_fstab_dvd_line: >-
|
||||||
{{
|
{{
|
||||||
@@ -39,7 +50,10 @@
|
|||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Write image from RHEL ISO to the target machine
|
- name: Write image from RHEL ISO to the target machine
|
||||||
when: os == "rhel" and hypervisor_type == 'vmware'
|
when:
|
||||||
|
- os == "rhel"
|
||||||
|
- hypervisor_type == "vmware"
|
||||||
|
- system_cfg.features.rhel_repo.source == "iso"
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
- dd
|
- dd
|
||||||
@@ -63,3 +77,4 @@
|
|||||||
- { regexp: "^tmpfs\\s+/dev/shm\\s+", line: "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
- { regexp: "^tmpfs\\s+/dev/shm\\s+", line: "tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0" }
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: fstab_entry
|
loop_var: fstab_entry
|
||||||
|
label: "{{ fstab_entry.regexp }}"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
line: GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3"
|
||||||
- regexp: ^GRUB_TIMEOUT=
|
- regexp: ^GRUB_TIMEOUT=
|
||||||
line: GRUB_TIMEOUT=1
|
line: GRUB_TIMEOUT=1
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.line }}"
|
||||||
|
|
||||||
- name: Ensure grub defaults file exists for RHEL-based systems
|
- name: Ensure grub defaults file exists for RHEL-based systems
|
||||||
when: is_rhel | bool
|
when: is_rhel | bool
|
||||||
@@ -95,7 +97,7 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
content: "{{ configuration_kernel_cmdline_base }}\n"
|
content: "{{ configuration_kernel_cmdline_base }}\n"
|
||||||
|
|
||||||
- name: Find BLS entries
|
- name: Find BLS entries for GRUB configuration
|
||||||
ansible.builtin.find:
|
ansible.builtin.find:
|
||||||
paths: /mnt/boot/loader/entries
|
paths: /mnt/boot/loader/entries
|
||||||
patterns: "*.conf"
|
patterns: "*.conf"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
line: "{{ item.line }}"
|
line: "{{ item.line }}"
|
||||||
loop:
|
loop:
|
||||||
- { regex: "{{ system_cfg.locale }} UTF-8", line: "{{ system_cfg.locale }} UTF-8" }
|
- { regex: "{{ system_cfg.locale }} UTF-8", line: "{{ system_cfg.locale }} UTF-8" }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.line }}"
|
||||||
|
|
||||||
- name: Generate locales
|
- name: Generate locales
|
||||||
when: not is_rhel | bool
|
when: not is_rhel | bool
|
||||||
@@ -45,7 +47,7 @@
|
|||||||
|
|
||||||
- name: Set hostname
|
- name: Set hostname
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: "{{ configuration_hostname_fqdn }}"
|
content: "{{ configuration_hostname_fqdn.split('.')[0] }}"
|
||||||
dest: /mnt/etc/hostname
|
dest: /mnt/etc/hostname
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
---
|
---
|
||||||
- name: Include configuration tasks
|
- name: Include configuration tasks
|
||||||
ansible.builtin.include_tasks: "{{ configuration_task }}"
|
when: configuration_task.when | default(true)
|
||||||
|
ansible.builtin.include_tasks: "{{ configuration_task.file }}"
|
||||||
loop:
|
loop:
|
||||||
- banner.yml
|
- file: banner.yml
|
||||||
- fstab.yml
|
- file: fstab.yml
|
||||||
- locales.yml
|
- file: locales.yml
|
||||||
- ssh.yml
|
- file: ssh.yml
|
||||||
- services.yml
|
- file: services.yml
|
||||||
- grub.yml
|
- file: grub.yml
|
||||||
- encryption.yml
|
- file: encryption.yml
|
||||||
- bootloader.yml
|
when: "{{ system_cfg.luks.enabled | bool }}"
|
||||||
- extras.yml
|
- file: bootloader.yml
|
||||||
- network.yml
|
- file: extras.yml
|
||||||
- users.yml
|
- file: network.yml
|
||||||
- sudo.yml
|
- file: users.yml
|
||||||
- selinux.yml
|
- file: sudo.yml
|
||||||
|
- file: selinux.yml
|
||||||
|
when: "{{ is_rhel | bool }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: configuration_task
|
loop_var: configuration_task
|
||||||
|
label: "{{ configuration_task.file }}"
|
||||||
|
|||||||
@@ -29,88 +29,9 @@
|
|||||||
- configuration_detected_interfaces | length > 0
|
- configuration_detected_interfaces | length > 0
|
||||||
fail_msg: Failed to detect any network interfaces.
|
fail_msg: Failed to detect any network interfaces.
|
||||||
|
|
||||||
- name: Configure NetworkManager profiles
|
- name: Configure networking
|
||||||
when: os not in ["alpine", "void"]
|
|
||||||
block:
|
|
||||||
- name: Copy NetworkManager keyfile per interface
|
|
||||||
vars:
|
vars:
|
||||||
configuration_iface: "{{ item }}"
|
configuration_network_task_map:
|
||||||
configuration_iface_name: "{{ configuration_detected_interfaces[idx] | default('eth' ~ idx) }}"
|
alpine: network_alpine.yml
|
||||||
configuration_net_uuid: "{{ ('LAN-' ~ idx ~ '-' ~ hostname) | ansible.builtin.to_uuid }}"
|
void: network_void.yml
|
||||||
ansible.builtin.template:
|
ansible.builtin.include_tasks: "{{ configuration_network_task_map[os] | default('network_nm.yml') }}"
|
||||||
src: network.j2
|
|
||||||
dest: "/mnt/etc/NetworkManager/system-connections/LAN-{{ idx }}.nmconnection"
|
|
||||||
mode: "0600"
|
|
||||||
loop: "{{ system_cfg.network.interfaces }}"
|
|
||||||
loop_control:
|
|
||||||
index_var: idx
|
|
||||||
label: "LAN-{{ idx }}"
|
|
||||||
|
|
||||||
- name: Fix Ubuntu unmanaged devices
|
|
||||||
when: os in ["ubuntu", "ubuntu-lts"]
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf
|
|
||||||
state: touch
|
|
||||||
mode: "0644"
|
|
||||||
|
|
||||||
- name: Configure Alpine networking
|
|
||||||
when: os == "alpine"
|
|
||||||
vars:
|
|
||||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
|
||||||
block:
|
|
||||||
- name: Write Alpine network interfaces
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/network/interfaces
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
auto lo
|
|
||||||
iface lo inet loopback
|
|
||||||
{% for iface in system_cfg.network.interfaces %}
|
|
||||||
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
|
||||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
|
||||||
|
|
||||||
auto {{ iface_name }}
|
|
||||||
iface {{ iface_name }} inet {{ 'static' if has_static else 'dhcp' }}
|
|
||||||
{% if has_static %}
|
|
||||||
address {{ iface.ip }}/{{ iface.prefix }}
|
|
||||||
{% if iface.gateway | default('') | string | length %}
|
|
||||||
gateway {{ iface.gateway }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
- name: Set Alpine DNS resolvers
|
|
||||||
when: configuration_dns_list | length > 0
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/resolv.conf
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
{% for resolver in configuration_dns_list %}
|
|
||||||
nameserver {{ resolver }}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
- name: Configure Void networking
|
|
||||||
when: os == "void"
|
|
||||||
vars:
|
|
||||||
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
|
||||||
block:
|
|
||||||
- name: Write dhcpcd configuration
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: /mnt/etc/dhcpcd.conf
|
|
||||||
mode: "0644"
|
|
||||||
content: |
|
|
||||||
{% for iface in system_cfg.network.interfaces %}
|
|
||||||
{% set iface_name = configuration_detected_interfaces[loop.index0] | default(iface.name | default('eth' ~ loop.index0)) %}
|
|
||||||
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
|
||||||
{% if has_static %}
|
|
||||||
interface {{ iface_name }}
|
|
||||||
static ip_address={{ iface.ip }}/{{ iface.prefix }}
|
|
||||||
{% if iface.gateway | default('') | string | length %}
|
|
||||||
static routers={{ iface.gateway }}
|
|
||||||
{% endif %}
|
|
||||||
{% if loop.index0 == 0 and configuration_dns_list | length > 0 %}
|
|
||||||
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|||||||
37
roles/configuration/tasks/network_alpine.yml
Normal file
37
roles/configuration/tasks/network_alpine.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
- name: Write Alpine network interfaces
|
||||||
|
vars:
|
||||||
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/network/interfaces
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
|
{% set inv_name = iface.name | default('') | string %}
|
||||||
|
{% set det_name = configuration_detected_interfaces[loop.index0] | default('eth' ~ loop.index0) %}
|
||||||
|
{% set iface_name = inv_name if inv_name | length > 0 else det_name %}
|
||||||
|
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||||
|
|
||||||
|
auto {{ iface_name }}
|
||||||
|
iface {{ iface_name }} inet {{ 'static' if has_static else 'dhcp' }}
|
||||||
|
{% if has_static %}
|
||||||
|
address {{ iface.ip }}/{{ iface.prefix }}
|
||||||
|
{% if iface.gateway | default('') | string | length %}
|
||||||
|
gateway {{ iface.gateway }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
- name: Set Alpine DNS resolvers
|
||||||
|
vars:
|
||||||
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
|
when: configuration_dns_list | length > 0
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/resolv.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
{% for resolver in configuration_dns_list %}
|
||||||
|
nameserver {{ resolver }}
|
||||||
|
{% endfor %}
|
||||||
26
roles/configuration/tasks/network_nm.yml
Normal file
26
roles/configuration/tasks/network_nm.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
- name: Copy NetworkManager keyfile per interface
|
||||||
|
vars:
|
||||||
|
configuration_iface: "{{ item }}"
|
||||||
|
configuration_iface_name: >-
|
||||||
|
{{
|
||||||
|
item.name
|
||||||
|
if (item.name | default('') | string | length) > 0
|
||||||
|
else (configuration_detected_interfaces[idx] | default('eth' ~ idx))
|
||||||
|
}}
|
||||||
|
configuration_net_uuid: "{{ ('LAN-' ~ idx ~ '-' ~ hostname) | ansible.builtin.to_uuid }}"
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: network.j2
|
||||||
|
dest: "/mnt/etc/NetworkManager/system-connections/LAN-{{ idx }}.nmconnection"
|
||||||
|
mode: "0600"
|
||||||
|
loop: "{{ system_cfg.network.interfaces }}"
|
||||||
|
loop_control:
|
||||||
|
index_var: idx
|
||||||
|
label: "LAN-{{ idx }}"
|
||||||
|
|
||||||
|
- name: Fix Ubuntu unmanaged devices
|
||||||
|
when: os in ["ubuntu", "ubuntu-lts"]
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /mnt/etc/NetworkManager/conf.d/10-globally-managed-devices.conf
|
||||||
|
state: touch
|
||||||
|
mode: "0644"
|
||||||
25
roles/configuration/tasks/network_void.yml
Normal file
25
roles/configuration/tasks/network_void.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
- name: Write dhcpcd configuration
|
||||||
|
vars:
|
||||||
|
configuration_dns_list: "{{ system_cfg.network.dns.servers | default([]) }}"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /mnt/etc/dhcpcd.conf
|
||||||
|
mode: "0644"
|
||||||
|
content: |
|
||||||
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
|
{% set inv_name = iface.name | default('') | string %}
|
||||||
|
{% set det_name = configuration_detected_interfaces[loop.index0] | default('eth' ~ loop.index0) %}
|
||||||
|
{% set iface_name = inv_name if inv_name | length > 0 else det_name %}
|
||||||
|
{% set has_static = (iface.ip | default('') | string | length) > 0 %}
|
||||||
|
{% if has_static %}
|
||||||
|
interface {{ iface_name }}
|
||||||
|
static ip_address={{ iface.ip }}/{{ iface.prefix }}
|
||||||
|
{% if iface.gateway | default('') | string | length %}
|
||||||
|
static routers={{ iface.gateway }}
|
||||||
|
{% endif %}
|
||||||
|
{% if loop.index0 == 0 and configuration_dns_list | length > 0 %}
|
||||||
|
static domain_name_servers={{ configuration_dns_list | join(' ') }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
---
|
---
|
||||||
- name: Enable Systemd Services
|
- name: Enable systemd services
|
||||||
when: os not in ['alpine', 'void']
|
when: os not in ['alpine', 'void']
|
||||||
ansible.builtin.command: >
|
vars:
|
||||||
{{ chroot_command }} systemctl enable NetworkManager
|
configuration_systemd_services: >-
|
||||||
{{ ' firewalld' if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else '' }}
|
|
||||||
{{ ' ufw' if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else '' }}
|
|
||||||
{{
|
{{
|
||||||
(' ssh' if is_debian | bool else ' sshd')
|
['NetworkManager']
|
||||||
if system_cfg.features.ssh.enabled | bool else ''
|
+ (['firewalld'] if system_cfg.features.firewall.backend == 'firewalld' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
|
+ (['ufw'] if system_cfg.features.firewall.backend == 'ufw' and system_cfg.features.firewall.enabled | bool else [])
|
||||||
|
+ ([('ssh' if is_debian | bool else 'sshd')] if system_cfg.features.ssh.enabled | bool else [])
|
||||||
|
+ (['logrotate', 'systemd-timesyncd'] if os == 'archlinux' else [])
|
||||||
}}
|
}}
|
||||||
{{
|
ansible.builtin.command: "{{ chroot_command }} systemctl enable {{ item }}"
|
||||||
'logrotate systemd-resolved systemd-timesyncd systemd-networkd'
|
loop: "{{ configuration_systemd_services }}"
|
||||||
if os == 'archlinux' else ''
|
register: configuration_enable_service_result
|
||||||
}}
|
changed_when: configuration_enable_service_result.rc == 0
|
||||||
register: configuration_enable_services_result
|
|
||||||
changed_when: configuration_enable_services_result.rc == 0
|
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
- name: Enable OpenRC services
|
||||||
when: os == 'alpine'
|
when: os == 'alpine'
|
||||||
@@ -37,7 +36,6 @@
|
|||||||
path: "/mnt/etc/init.d/{{ item }}"
|
path: "/mnt/etc/init.d/{{ item }}"
|
||||||
loop: "{{ configuration_openrc_services }}"
|
loop: "{{ configuration_openrc_services }}"
|
||||||
register: configuration_openrc_service_stats
|
register: configuration_openrc_service_stats
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Enable OpenRC services
|
- name: Enable OpenRC services
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -45,6 +43,8 @@
|
|||||||
dest: "/mnt/etc/runlevels/default/{{ item.item }}"
|
dest: "/mnt/etc/runlevels/default/{{ item.item }}"
|
||||||
state: link
|
state: link
|
||||||
loop: "{{ configuration_openrc_service_stats.results }}"
|
loop: "{{ configuration_openrc_service_stats.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
when: item.stat.exists
|
when: item.stat.exists
|
||||||
|
|
||||||
- name: Enable runit services
|
- name: Enable runit services
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
path: "/mnt/etc/sv/{{ item }}"
|
path: "/mnt/etc/sv/{{ item }}"
|
||||||
loop: "{{ configuration_runit_services }}"
|
loop: "{{ configuration_runit_services }}"
|
||||||
register: configuration_runit_service_stats
|
register: configuration_runit_service_stats
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Enable runit services
|
- name: Enable runit services
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@@ -76,4 +75,6 @@
|
|||||||
dest: "/mnt/var/service/{{ item.item }}"
|
dest: "/mnt/var/service/{{ item.item }}"
|
||||||
state: link
|
state: link
|
||||||
loop: "{{ configuration_runit_service_stats.results }}"
|
loop: "{{ configuration_runit_service_stats.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
when: item.stat.exists
|
when: item.stat.exists
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
---
|
---
|
||||||
- name: Set root password
|
- name: Set root password
|
||||||
vars:
|
ansible.builtin.shell: >-
|
||||||
configuration_root_cmd: >-
|
set -o pipefail &&
|
||||||
{{ chroot_command }} /usr/sbin/usermod --password
|
echo 'root:{{ system_cfg.root.password | password_hash("sha512") }}' | {{ chroot_command }} chpasswd -e
|
||||||
'{{ system_cfg.root.password | password_hash('sha512') }}' root --shell /bin/bash
|
args:
|
||||||
ansible.builtin.command: "{{ configuration_root_cmd }}"
|
executable: /bin/bash
|
||||||
register: configuration_root_result
|
register: configuration_root_result
|
||||||
changed_when: configuration_root_result.rc == 0
|
changed_when: configuration_root_result.rc == 0
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Set root shell
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
{{ chroot_command }} /usr/sbin/usermod --shell {{ system_cfg.root.shell | default('/bin/bash') }} root
|
||||||
|
register: configuration_root_shell_result
|
||||||
|
changed_when: configuration_root_shell_result.rc == 0
|
||||||
|
|
||||||
- name: Create user accounts
|
- name: Create user accounts
|
||||||
vars:
|
vars:
|
||||||
configuration_user_group: >-
|
configuration_user_group: >-
|
||||||
{{ "sudo" if is_debian | bool else "wheel" }}
|
{{ "sudo" if is_debian | bool else "wheel" }}
|
||||||
|
# UID starts at 1000; safe for fresh installs only
|
||||||
configuration_useradd_cmd: >-
|
configuration_useradd_cmd: >-
|
||||||
{{ chroot_command }} /usr/sbin/useradd --create-home --user-group
|
{{ chroot_command }} /usr/sbin/useradd --create-home --user-group
|
||||||
--uid {{ 1000 + ansible_loop.index0 }}
|
--uid {{ 1000 + ansible_loop.index0 }}
|
||||||
--groups {{ configuration_user_group }} {{ item.name }}
|
--groups {{ configuration_user_group }} {{ item.name }}
|
||||||
--password {{ item.password | password_hash('sha512') }} --shell /bin/bash
|
--password {{ item.password | password_hash('sha512') }} --shell {{ item.shell | default('/bin/bash') }}
|
||||||
ansible.builtin.command: "{{ configuration_useradd_cmd }}"
|
ansible.builtin.command: "{{ configuration_useradd_cmd }}"
|
||||||
loop: "{{ system_cfg.users }}"
|
loop: "{{ system_cfg.users }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
@@ -24,6 +32,7 @@
|
|||||||
label: "{{ item.name }}"
|
label: "{{ item.name }}"
|
||||||
register: configuration_user_result
|
register: configuration_user_result
|
||||||
changed_when: configuration_user_result.rc == 0
|
changed_when: configuration_user_result.rc == 0
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Ensure .ssh directory exists
|
- name: Ensure .ssh directory exists
|
||||||
when: item['keys'] | default([]) | length > 0
|
when: item['keys'] | default([]) | length > 0
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ method=manual
|
|||||||
method=auto
|
method=auto
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if idx | int == 0 and dns_list %}
|
{% if idx | int == 0 and dns_list %}
|
||||||
dns={{ dns_list | join(';') }}
|
dns={{ dns_list | join(';') }};
|
||||||
ignore-auto-dns=true
|
ignore-auto-dns=true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if idx | int == 0 and search_list %}
|
{% if idx | int == 0 and search_list %}
|
||||||
dns-search={{ search_list | join(';') }}
|
dns-search={{ search_list | join(';') }};
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
[ipv6]
|
[ipv6]
|
||||||
|
|||||||
@@ -132,6 +132,8 @@
|
|||||||
replace: "PermitEmptyPasswords yes"
|
replace: "PermitEmptyPasswords yes"
|
||||||
- regexp: "^#?PermitRootLogin.*"
|
- regexp: "^#?PermitRootLogin.*"
|
||||||
replace: "PermitRootLogin yes"
|
replace: "PermitRootLogin yes"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.replace }}"
|
||||||
|
|
||||||
- name: Reload SSH service to apply changes
|
- name: Reload SSH service to apply changes
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
@@ -175,6 +177,8 @@
|
|||||||
- { name: debootstrap, os: [debian, ubuntu, ubuntu-lts] }
|
- { name: debootstrap, os: [debian, ubuntu, ubuntu-lts] }
|
||||||
- { name: debian-archive-keyring, os: [debian] }
|
- { name: debian-archive-keyring, os: [debian] }
|
||||||
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
- { name: ubuntu-keyring, os: [ubuntu, ubuntu-lts] }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
retries: 4
|
retries: 4
|
||||||
delay: 15
|
delay: 15
|
||||||
|
|
||||||
@@ -187,15 +191,29 @@
|
|||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Detect RHEL ISO device
|
||||||
|
ansible.builtin.command: lsblk -rno NAME,TYPE
|
||||||
|
register: environment_lsblk_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: Select RHEL ISO device
|
- name: Select RHEL ISO device
|
||||||
|
vars:
|
||||||
|
_rom_devices: >-
|
||||||
|
{{
|
||||||
|
environment_lsblk_result.stdout_lines
|
||||||
|
| map('split', ' ')
|
||||||
|
| selectattr('1', 'equalto', 'rom')
|
||||||
|
| map('first')
|
||||||
|
| map('regex_replace', '^', '/dev/')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
environment_rhel_iso_device: >-
|
environment_rhel_iso_device: >-
|
||||||
{{
|
{{
|
||||||
'/dev/sr2'
|
_rom_devices[-1]
|
||||||
if hypervisor_type == 'libvirt'
|
if _rom_devices | length > 1
|
||||||
else '/dev/sr1'
|
else (_rom_devices[0] | default('/dev/sr1'))
|
||||||
}}
|
}}
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Mount RHEL ISO
|
- name: Mount RHEL ISO
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
@@ -205,6 +223,10 @@
|
|||||||
opts: "ro,loop"
|
opts: "ro,loop"
|
||||||
state: mounted
|
state: mounted
|
||||||
|
|
||||||
|
# Security note: RPM Sequoia signature policy is relaxed to allow
|
||||||
|
# bootstrapping RHEL-family distros from the Arch ISO, where the
|
||||||
|
# host rpm/dnf does not trust target distro GPG keys. Package
|
||||||
|
# integrity is verified by the target system's own rpm after reboot.
|
||||||
- name: Relax RPM Sequoia signature policy for RHEL bootstrap
|
- name: Relax RPM Sequoia signature policy for RHEL bootstrap
|
||||||
when: is_rhel | bool
|
when: is_rhel | bool
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
|
|||||||
@@ -1,4 +1,27 @@
|
|||||||
---
|
---
|
||||||
|
# OS family lists — single source of truth for platform detection and validation
|
||||||
|
os_family_rhel:
|
||||||
|
- almalinux
|
||||||
|
- fedora
|
||||||
|
- rhel
|
||||||
|
- rocky
|
||||||
|
os_family_debian:
|
||||||
|
- debian
|
||||||
|
- ubuntu
|
||||||
|
- ubuntu-lts
|
||||||
|
os_supported:
|
||||||
|
- almalinux
|
||||||
|
- alpine
|
||||||
|
- archlinux
|
||||||
|
- debian
|
||||||
|
- fedora
|
||||||
|
- opensuse
|
||||||
|
- rhel
|
||||||
|
- rocky
|
||||||
|
- ubuntu
|
||||||
|
- ubuntu-lts
|
||||||
|
- void
|
||||||
|
|
||||||
# User input. Normalized into hypervisor_cfg + hypervisor_type.
|
# User input. Normalized into hypervisor_cfg + hypervisor_type.
|
||||||
hypervisor:
|
hypervisor:
|
||||||
type: "none"
|
type: "none"
|
||||||
@@ -83,6 +106,9 @@ system_defaults:
|
|||||||
banner:
|
banner:
|
||||||
motd: false
|
motd: false
|
||||||
sudo: true
|
sudo: true
|
||||||
|
rhel_repo:
|
||||||
|
source: "iso" # iso|satellite|none — how RHEL systems get packages post-install
|
||||||
|
url: "" # Satellite/custom repo URL when source=satellite
|
||||||
chroot:
|
chroot:
|
||||||
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
|
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
|
||||||
|
|
||||||
|
|||||||
100
roles/global_defaults/tasks/_normalize_disks.yml
Normal file
100
roles/global_defaults/tasks/_normalize_disks.yml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
- name: Normalize system disks input
|
||||||
|
vars:
|
||||||
|
system_disks: "{{ system_cfg.disks | default([]) }}"
|
||||||
|
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
system_disk_device_prefix: >-
|
||||||
|
{{
|
||||||
|
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '')
|
||||||
|
if system_cfg.type == 'virtual'
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
block:
|
||||||
|
- name: Validate system disks structure
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_disks is sequence
|
||||||
|
- (system_disks | length) <= 26
|
||||||
|
fail_msg: "system.disks must be a list with at most 26 entries."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system disk entries
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item is mapping
|
||||||
|
- item.mount is not defined or item.mount is mapping
|
||||||
|
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
- name: Initialize normalized disk list
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_disks_cfg: []
|
||||||
|
|
||||||
|
- name: Build normalized system disk configuration
|
||||||
|
vars:
|
||||||
|
disk_idx: "{{ ansible_loop.index0 }}"
|
||||||
|
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
|
||||||
|
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
|
||||||
|
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
|
||||||
|
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
|
||||||
|
disk_mount_fstype: >-
|
||||||
|
{{
|
||||||
|
disk_mount.fstype
|
||||||
|
if (disk_mount.fstype | default('') | string | length) > 0
|
||||||
|
else ('ext4' if disk_mount_path | length > 0 else '')
|
||||||
|
}}
|
||||||
|
disk_device: >-
|
||||||
|
{{
|
||||||
|
disk_cfg_base.device
|
||||||
|
if (disk_cfg_base.device | string | length) > 0
|
||||||
|
else (
|
||||||
|
(system_disk_device_prefix ~ disk_letter)
|
||||||
|
if system_cfg.type == 'virtual'
|
||||||
|
else ''
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
disk_partition: >-
|
||||||
|
{{
|
||||||
|
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
|
||||||
|
if disk_device | length > 0
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_disks_cfg: >-
|
||||||
|
{{
|
||||||
|
system_disks_cfg + [
|
||||||
|
disk_cfg_base
|
||||||
|
| combine(
|
||||||
|
{
|
||||||
|
'device': disk_device,
|
||||||
|
'mount': {
|
||||||
|
'path': disk_mount_path,
|
||||||
|
'fstype': disk_mount_fstype,
|
||||||
|
'label': disk_mount.label | default('') | string,
|
||||||
|
'opts': disk_mount.opts | default('defaults') | string
|
||||||
|
},
|
||||||
|
'partition': disk_partition
|
||||||
|
},
|
||||||
|
recursive=True
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
loop: "{{ system_disks }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: item
|
||||||
|
extended: true
|
||||||
|
label: "{{ item | to_json }}"
|
||||||
|
|
||||||
|
- name: Update system configuration with normalized disks
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg}, recursive=True) }}"
|
||||||
|
|
||||||
|
- name: Set install_drive from primary disk
|
||||||
|
when:
|
||||||
|
- system_disks_cfg | length > 0
|
||||||
|
- system_disks_cfg[0].device | string | length > 0
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
install_drive: "{{ system_disks_cfg[0].device }}"
|
||||||
143
roles/global_defaults/tasks/_normalize_system.yml
Normal file
143
roles/global_defaults/tasks/_normalize_system.yml
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
- name: Build normalized system configuration
|
||||||
|
vars:
|
||||||
|
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
||||||
|
system_type: "{{ system_raw.type | string | lower }}"
|
||||||
|
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
|
||||||
|
system_name: >-
|
||||||
|
{{
|
||||||
|
system_raw.name | string | trim
|
||||||
|
if (system_raw.name | default('') | string | trim | length) > 0
|
||||||
|
else inventory_hostname
|
||||||
|
}}
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_cfg:
|
||||||
|
type: "{{ system_type }}"
|
||||||
|
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||||
|
version: "{{ system_raw.version | default('') | string }}"
|
||||||
|
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
|
||||||
|
name: "{{ system_name }}"
|
||||||
|
id: "{{ system_raw.id | default('') | string }}"
|
||||||
|
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
|
||||||
|
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
||||||
|
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
||||||
|
network:
|
||||||
|
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
||||||
|
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
||||||
|
ip: "{{ system_raw.network.ip | default('') | string }}"
|
||||||
|
prefix: >-
|
||||||
|
{{
|
||||||
|
(system_raw.network.prefix | int | string)
|
||||||
|
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||||
|
else ''
|
||||||
|
}}
|
||||||
|
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
||||||
|
dns:
|
||||||
|
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||||
|
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||||
|
interfaces: >-
|
||||||
|
{{
|
||||||
|
system_raw.network.interfaces
|
||||||
|
if (system_raw.network.interfaces | default([]) | length > 0)
|
||||||
|
else (
|
||||||
|
[{
|
||||||
|
'name': '',
|
||||||
|
'bridge': system_raw.network.bridge | default('') | string,
|
||||||
|
'vlan': system_raw.network.vlan | default('') | string,
|
||||||
|
'ip': system_raw.network.ip | default('') | string,
|
||||||
|
'prefix': (
|
||||||
|
(system_raw.network.prefix | int | string)
|
||||||
|
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||||
|
else ''
|
||||||
|
),
|
||||||
|
'gateway': system_raw.network.gateway | default('') | string
|
||||||
|
}]
|
||||||
|
if (system_raw.network.bridge | default('') | string | length > 0)
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
timezone: "{{ system_raw.timezone | default('Europe/Vienna') | string }}"
|
||||||
|
locale: "{{ system_raw.locale | default('en_US.UTF-8') | string }}"
|
||||||
|
keymap: "{{ system_raw.keymap | default('us') | string }}"
|
||||||
|
path: "{{ system_raw.path | default('') | string }}"
|
||||||
|
packages: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
system_raw.packages
|
||||||
|
if system_raw.packages is iterable and system_raw.packages is not string
|
||||||
|
else (system_raw.packages | string).split(',')
|
||||||
|
)
|
||||||
|
| map('trim')
|
||||||
|
| reject('equalto', '')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
disks: "{{ system_raw.disks | default([]) }}"
|
||||||
|
users: "{{ system_raw.users | default([]) }}"
|
||||||
|
root:
|
||||||
|
password: "{{ system_raw.root.password | string }}"
|
||||||
|
luks:
|
||||||
|
enabled: "{{ system_raw.luks.enabled | bool }}"
|
||||||
|
passphrase: "{{ system_raw.luks.passphrase | string }}"
|
||||||
|
mapper: "{{ system_raw.luks.mapper | string }}"
|
||||||
|
auto: "{{ system_raw.luks.auto | bool }}"
|
||||||
|
method: "{{ system_raw.luks.method | string | lower }}"
|
||||||
|
tpm2:
|
||||||
|
device: "{{ system_raw.luks.tpm2.device | string }}"
|
||||||
|
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
|
||||||
|
keysize: "{{ system_raw.luks.keysize | int }}"
|
||||||
|
options: "{{ system_raw.luks.options | string }}"
|
||||||
|
type: "{{ system_raw.luks.type | string }}"
|
||||||
|
cipher: "{{ system_raw.luks.cipher | string }}"
|
||||||
|
hash: "{{ system_raw.luks.hash | string }}"
|
||||||
|
iter: "{{ system_raw.luks.iter | int }}"
|
||||||
|
bits: "{{ system_raw.luks.bits | int }}"
|
||||||
|
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
|
||||||
|
urandom: "{{ system_raw.luks.urandom | bool }}"
|
||||||
|
verify: "{{ system_raw.luks.verify | bool }}"
|
||||||
|
features:
|
||||||
|
cis:
|
||||||
|
enabled: "{{ system_raw.features.cis.enabled | bool }}"
|
||||||
|
selinux:
|
||||||
|
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
|
||||||
|
firewall:
|
||||||
|
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
|
||||||
|
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
|
||||||
|
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
|
||||||
|
ssh:
|
||||||
|
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
|
||||||
|
zstd:
|
||||||
|
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
|
||||||
|
swap:
|
||||||
|
enabled: "{{ system_raw.features.swap.enabled | bool }}"
|
||||||
|
banner:
|
||||||
|
motd: "{{ system_raw.features.banner.motd | bool }}"
|
||||||
|
sudo: "{{ system_raw.features.banner.sudo | bool }}"
|
||||||
|
rhel_repo:
|
||||||
|
source: "{{ system_raw.features.rhel_repo.source | default('iso') | string | lower }}"
|
||||||
|
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
|
||||||
|
chroot:
|
||||||
|
tool: "{{ system_raw.features.chroot.tool | string }}"
|
||||||
|
hostname: "{{ system_name }}"
|
||||||
|
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||||
|
os_version: "{{ system_raw.version | default('') | string }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Populate primary network fields from first interface
|
||||||
|
when:
|
||||||
|
- system_cfg.network.interfaces | length > 0
|
||||||
|
- system_cfg.network.bridge | default('') | string | length == 0
|
||||||
|
vars:
|
||||||
|
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system_cfg: >-
|
||||||
|
{{
|
||||||
|
system_cfg | combine({
|
||||||
|
'network': system_cfg.network | combine({
|
||||||
|
'bridge': _primary.bridge | default(''),
|
||||||
|
'vlan': _primary.vlan | default(''),
|
||||||
|
'ip': _primary.ip | default(''),
|
||||||
|
'prefix': _primary.prefix | default(''),
|
||||||
|
'gateway': _primary.gateway | default('')
|
||||||
|
})
|
||||||
|
}, recursive=True)
|
||||||
|
}}
|
||||||
57
roles/global_defaults/tasks/_validate_input.yml
Normal file
57
roles/global_defaults/tasks/_validate_input.yml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure system input is a dictionary
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
system: "{{ system | default({}) }}"
|
||||||
|
|
||||||
|
- name: Validate system input types
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system is mapping
|
||||||
|
- system.network is not defined or system.network is mapping
|
||||||
|
- system.users is not defined or (system.users is iterable and system.users is not string and system.users is not mapping)
|
||||||
|
- system.root is not defined or system.root is mapping
|
||||||
|
- system.luks is not defined or system.luks is mapping
|
||||||
|
- system.features is not defined or system.features is mapping
|
||||||
|
fail_msg: "system and its nested keys (network, root, luks, features) must be dictionaries; system.users must be a list."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate DNS lists (not strings)
|
||||||
|
when: system.network is defined and system.network.dns is defined
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
||||||
|
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
||||||
|
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system.users entries
|
||||||
|
when: system.users is defined and system.users | length > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item is mapping
|
||||||
|
- item.name is defined and (item.name | string | length) > 0
|
||||||
|
- item['keys'] is not defined or (item['keys'] is iterable and item['keys'] is not string)
|
||||||
|
fail_msg: "Each system.users[] entry must be a dict with 'name'; 'keys' must be a list."
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ system.users }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name | default('(unnamed)') }}"
|
||||||
|
|
||||||
|
- name: Validate system features input types
|
||||||
|
when: system.features is defined
|
||||||
|
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
||||||
|
loop_control:
|
||||||
|
label: "system.features.{{ item }}"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- (system.features[item] | default({})) is mapping
|
||||||
|
fail_msg: "system.features.{{ item }} must be a dictionary."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate system LUKS TPM2 input type
|
||||||
|
when: system.luks is defined and system.luks is mapping
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
||||||
|
fail_msg: "system.luks.tpm2 must be a dictionary."
|
||||||
|
quiet: true
|
||||||
@@ -18,3 +18,4 @@
|
|||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
hypervisor_cfg: "{{ merged }}"
|
hypervisor_cfg: "{{ merged }}"
|
||||||
hypervisor_type: "{{ merged.type | string | lower }}"
|
hypervisor_type: "{{ merged.type | string | lower }}"
|
||||||
|
no_log: true
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
- name: Set OS family flags
|
- name: Set OS family flags
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
is_rhel: "{{ os in ['almalinux', 'fedora', 'rhel', 'rocky'] }}"
|
is_rhel: "{{ os in os_family_rhel }}"
|
||||||
is_debian: "{{ os in ['debian', 'ubuntu', 'ubuntu-lts'] }}"
|
is_debian: "{{ os in os_family_debian }}"
|
||||||
|
|
||||||
- name: Normalize OS version for keying
|
- name: Normalize OS version for keying
|
||||||
when:
|
when:
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
ansible_password: "{{ system_cfg.users[0].password }}"
|
ansible_password: "{{ system_cfg.users[0].password }}"
|
||||||
ansible_become_password: "{{ system_cfg.users[0].password }}"
|
ansible_become_password: "{{ system_cfg.users[0].password }}"
|
||||||
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Set connection for VMware
|
- name: Set connection for VMware
|
||||||
when: hypervisor_type == "vmware"
|
when: hypervisor_type == "vmware"
|
||||||
|
|||||||
@@ -1,297 +1,9 @@
|
|||||||
---
|
---
|
||||||
- name: Ensure system input is a dictionary
|
- name: Validate raw system input types
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.include_tasks: _validate_input.yml
|
||||||
system: "{{ system | default({}) }}"
|
|
||||||
|
|
||||||
- name: Validate system input types
|
- name: Normalize system configuration
|
||||||
ansible.builtin.assert:
|
ansible.builtin.include_tasks: _normalize_system.yml
|
||||||
that:
|
|
||||||
- system is mapping
|
|
||||||
- system.network is not defined or system.network is mapping
|
|
||||||
- system.users is not defined or (system.users is iterable and system.users is not string and system.users is not mapping)
|
|
||||||
- system.root is not defined or system.root is mapping
|
|
||||||
- system.luks is not defined or system.luks is mapping
|
|
||||||
- system.features is not defined or system.features is mapping
|
|
||||||
fail_msg: "system and its nested keys (network, root, luks, features) must be dictionaries; system.users must be a list."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate DNS lists (not strings)
|
|
||||||
when: system.network is defined and system.network.dns is defined
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
|
||||||
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
|
||||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system.users entries
|
|
||||||
when: system.users is defined and system.users | length > 0
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- item is mapping
|
|
||||||
- item.name is defined and (item.name | string | length) > 0
|
|
||||||
- item['keys'] is not defined or (item['keys'] is iterable and item['keys'] is not string)
|
|
||||||
fail_msg: "Each system.users[] entry must be a dict with 'name'; 'keys' must be a list."
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ system.users }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item.name | default('(unnamed)') }}"
|
|
||||||
|
|
||||||
- name: Validate system features input types
|
|
||||||
when: system.features is defined
|
|
||||||
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
|
||||||
loop_control:
|
|
||||||
label: "system.features.{{ item }}"
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- (system.features[item] | default({})) is mapping
|
|
||||||
fail_msg: "system.features.{{ item }} must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system LUKS TPM2 input type
|
|
||||||
when: system.luks is defined and system.luks is mapping
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
|
||||||
fail_msg: "system.luks.tpm2 must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Build normalized system configuration
|
|
||||||
vars:
|
|
||||||
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
|
||||||
system_type: "{{ system_raw.type | string | lower }}"
|
|
||||||
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
|
|
||||||
system_name: >-
|
|
||||||
{{
|
|
||||||
system_raw.name | string | trim
|
|
||||||
if (system_raw.name | default('') | string | trim | length) > 0
|
|
||||||
else inventory_hostname
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_cfg:
|
|
||||||
type: "{{ system_type }}"
|
|
||||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
|
||||||
version: "{{ system_raw.version | default('') | string }}"
|
|
||||||
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
|
|
||||||
name: "{{ system_name }}"
|
|
||||||
id: "{{ system_raw.id | default('') | string }}"
|
|
||||||
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
|
|
||||||
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
|
||||||
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
|
||||||
network:
|
|
||||||
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
|
||||||
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
|
||||||
ip: "{{ system_raw.network.ip | default('') | string }}"
|
|
||||||
prefix: >-
|
|
||||||
{{
|
|
||||||
(system_raw.network.prefix | int)
|
|
||||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
|
||||||
dns:
|
|
||||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
|
||||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
|
||||||
interfaces: >-
|
|
||||||
{{
|
|
||||||
system_raw.network.interfaces
|
|
||||||
if (system_raw.network.interfaces | default([]) | length > 0)
|
|
||||||
else (
|
|
||||||
[{
|
|
||||||
'name': 'eth0',
|
|
||||||
'bridge': system_raw.network.bridge | default('') | string,
|
|
||||||
'vlan': system_raw.network.vlan | default('') | string,
|
|
||||||
'ip': system_raw.network.ip | default('') | string,
|
|
||||||
'prefix': (
|
|
||||||
(system_raw.network.prefix | int | string)
|
|
||||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
|
||||||
else ''
|
|
||||||
),
|
|
||||||
'gateway': system_raw.network.gateway | default('') | string
|
|
||||||
}]
|
|
||||||
if (system_raw.network.bridge | default('') | string | length > 0)
|
|
||||||
else []
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
timezone: "{{ system_raw.timezone | default('Europe/Vienna') | string }}"
|
|
||||||
locale: "{{ system_raw.locale | default('en_US.UTF-8') | string }}"
|
|
||||||
keymap: "{{ system_raw.keymap | default('us') | string }}"
|
|
||||||
path: "{{ system_raw.path | default('') | string }}"
|
|
||||||
packages: >-
|
|
||||||
{{
|
|
||||||
(
|
|
||||||
system_raw.packages
|
|
||||||
if system_raw.packages is iterable and system_raw.packages is not string
|
|
||||||
else (system_raw.packages | string).split(',')
|
|
||||||
)
|
|
||||||
| map('trim')
|
|
||||||
| reject('equalto', '')
|
|
||||||
| list
|
|
||||||
}}
|
|
||||||
disks: "{{ system_raw.disks | default([]) }}"
|
|
||||||
users: "{{ system_raw.users | default([]) }}"
|
|
||||||
root:
|
|
||||||
password: "{{ system_raw.root.password | string }}"
|
|
||||||
luks:
|
|
||||||
enabled: "{{ system_raw.luks.enabled | bool }}"
|
|
||||||
passphrase: "{{ system_raw.luks.passphrase | string }}"
|
|
||||||
mapper: "{{ system_raw.luks.mapper | string }}"
|
|
||||||
auto: "{{ system_raw.luks.auto | bool }}"
|
|
||||||
method: "{{ system_raw.luks.method | string | lower }}"
|
|
||||||
tpm2:
|
|
||||||
device: "{{ system_raw.luks.tpm2.device | string }}"
|
|
||||||
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
|
|
||||||
keysize: "{{ system_raw.luks.keysize | int }}"
|
|
||||||
options: "{{ system_raw.luks.options | string }}"
|
|
||||||
type: "{{ system_raw.luks.type | string }}"
|
|
||||||
cipher: "{{ system_raw.luks.cipher | string }}"
|
|
||||||
hash: "{{ system_raw.luks.hash | string }}"
|
|
||||||
iter: "{{ system_raw.luks.iter | int }}"
|
|
||||||
bits: "{{ system_raw.luks.bits | int }}"
|
|
||||||
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
|
|
||||||
urandom: "{{ system_raw.luks.urandom | bool }}"
|
|
||||||
verify: "{{ system_raw.luks.verify | bool }}"
|
|
||||||
features:
|
|
||||||
cis:
|
|
||||||
enabled: "{{ system_raw.features.cis.enabled | bool }}"
|
|
||||||
selinux:
|
|
||||||
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
|
|
||||||
firewall:
|
|
||||||
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
|
|
||||||
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
|
|
||||||
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
|
|
||||||
ssh:
|
|
||||||
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
|
|
||||||
zstd:
|
|
||||||
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
|
|
||||||
swap:
|
|
||||||
enabled: "{{ system_raw.features.swap.enabled | bool }}"
|
|
||||||
banner:
|
|
||||||
motd: "{{ system_raw.features.banner.motd | bool }}"
|
|
||||||
sudo: "{{ system_raw.features.banner.sudo | bool }}"
|
|
||||||
chroot:
|
|
||||||
tool: "{{ system_raw.features.chroot.tool | string }}"
|
|
||||||
hostname: "{{ system_name }}"
|
|
||||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
|
||||||
os_version: "{{ system_raw.version | default('') | string }}"
|
|
||||||
|
|
||||||
- name: Populate primary network fields from first interface
|
|
||||||
when:
|
|
||||||
- system_cfg.network.interfaces | length > 0
|
|
||||||
- system_cfg.network.bridge | default('') | string | length == 0
|
|
||||||
vars:
|
|
||||||
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_cfg: >-
|
|
||||||
{{
|
|
||||||
system_cfg | combine({
|
|
||||||
'network': system_cfg.network | combine({
|
|
||||||
'bridge': _primary.bridge | default(''),
|
|
||||||
'vlan': _primary.vlan | default(''),
|
|
||||||
'ip': _primary.ip | default(''),
|
|
||||||
'prefix': _primary.prefix | default(''),
|
|
||||||
'gateway': _primary.gateway | default('')
|
|
||||||
})
|
|
||||||
}, recursive=True)
|
|
||||||
}}
|
|
||||||
|
|
||||||
- name: Normalize system disks input
|
|
||||||
vars:
|
|
||||||
system_disks: "{{ system_cfg.disks | default([]) }}"
|
|
||||||
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
system_disk_device_prefix: >-
|
|
||||||
{{
|
|
||||||
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '')
|
|
||||||
if system_cfg.type == 'virtual'
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
block:
|
|
||||||
- name: Validate system disks structure
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- system_disks is sequence
|
|
||||||
- (system_disks | length) <= 26
|
|
||||||
fail_msg: "system.disks must be a list with at most 26 entries."
|
|
||||||
quiet: true
|
|
||||||
|
|
||||||
- name: Validate system disk entries
|
|
||||||
ansible.builtin.assert:
|
|
||||||
that:
|
|
||||||
- item is mapping
|
|
||||||
- item.mount is not defined or item.mount is mapping
|
|
||||||
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
|
|
||||||
quiet: true
|
|
||||||
loop: "{{ system_disks }}"
|
|
||||||
loop_control:
|
|
||||||
label: "{{ item | to_json }}"
|
|
||||||
|
|
||||||
- name: Initialize normalized disk list
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_disks_cfg: []
|
|
||||||
|
|
||||||
- name: Build normalized system disk configuration
|
|
||||||
vars:
|
|
||||||
disk_idx: "{{ ansible_loop.index0 }}"
|
|
||||||
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
|
|
||||||
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
|
|
||||||
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
|
|
||||||
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
|
|
||||||
disk_mount_fstype: >-
|
|
||||||
{{
|
|
||||||
disk_mount.fstype
|
|
||||||
if (disk_mount.fstype | default('') | string | length) > 0
|
|
||||||
else ('ext4' if disk_mount_path | length > 0 else '')
|
|
||||||
}}
|
|
||||||
disk_device: >-
|
|
||||||
{{
|
|
||||||
disk_cfg_base.device
|
|
||||||
if (disk_cfg_base.device | string | length) > 0
|
|
||||||
else (
|
|
||||||
(system_disk_device_prefix ~ disk_letter)
|
|
||||||
if system_cfg.type == 'virtual'
|
|
||||||
else ''
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
disk_partition: >-
|
|
||||||
{{
|
|
||||||
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
|
|
||||||
if disk_device | length > 0
|
|
||||||
else ''
|
|
||||||
}}
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_disks_cfg: >-
|
|
||||||
{{
|
|
||||||
system_disks_cfg + [
|
|
||||||
disk_cfg_base
|
|
||||||
| combine(
|
|
||||||
{
|
|
||||||
'device': disk_device,
|
|
||||||
'mount': {
|
|
||||||
'path': disk_mount_path,
|
|
||||||
'fstype': disk_mount_fstype,
|
|
||||||
'label': disk_mount.label | default('') | string,
|
|
||||||
'opts': disk_mount.opts | default('defaults') | string
|
|
||||||
},
|
|
||||||
'partition': disk_partition
|
|
||||||
},
|
|
||||||
recursive=True
|
|
||||||
)
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
loop: "{{ system_disks }}"
|
|
||||||
loop_control:
|
|
||||||
loop_var: item
|
|
||||||
extended: true
|
|
||||||
label: "{{ item | to_json }}"
|
|
||||||
|
|
||||||
- name: Update system configuration with normalized disks
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg}, recursive=True) }}"
|
|
||||||
|
|
||||||
- name: Set install_drive from primary disk
|
|
||||||
when:
|
|
||||||
- system_disks_cfg | length > 0
|
|
||||||
- system_disks_cfg[0].device | string | length > 0
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
install_drive: "{{ system_disks_cfg[0].device }}"
|
|
||||||
|
|
||||||
|
- name: Normalize disk configuration
|
||||||
|
ansible.builtin.include_tasks: _normalize_disks.yml
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- os is defined
|
- os is defined
|
||||||
- os in ["almalinux", "alpine", "archlinux", "debian", "fedora", "opensuse", "rhel", "rocky", "ubuntu", "ubuntu-lts", "void"]
|
- os in os_supported
|
||||||
- >-
|
- >-
|
||||||
os not in ["debian", "fedora", "rocky", "almalinux", "rhel"]
|
os not in ["debian", "fedora", "rocky", "almalinux", "rhel"]
|
||||||
or (os_version is defined and (os_version | string | length) > 0)
|
or (os_version is defined and (os_version | string | length) > 0)
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
or (
|
or (
|
||||||
os == "debian" and (os_version | string) in ["10", "11", "12", "13", "unstable"]
|
os == "debian" and (os_version | string) in ["10", "11", "12", "13", "unstable"]
|
||||||
) or (
|
) or (
|
||||||
os == "fedora" and (os_version | string) in ["40", "41", "42", "43"]
|
os == "fedora" and (os_version | int) >= 38 and (os_version | int) <= 45
|
||||||
) or (
|
) or (
|
||||||
os in ["rocky", "almalinux"]
|
os in ["rocky", "almalinux"]
|
||||||
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
||||||
@@ -131,7 +131,9 @@
|
|||||||
os == "rhel"
|
os == "rhel"
|
||||||
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
|
||||||
) or (
|
) or (
|
||||||
os in ["alpine", "archlinux", "opensuse", "ubuntu", "ubuntu-lts", "void"]
|
os in ["ubuntu", "ubuntu-lts"]
|
||||||
|
) or (
|
||||||
|
os in ["alpine", "archlinux", "opensuse", "void"]
|
||||||
)
|
)
|
||||||
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
|
fail_msg: "Invalid os/version specified. Please check README.md for supported values."
|
||||||
quiet: true
|
quiet: true
|
||||||
@@ -162,6 +164,7 @@
|
|||||||
Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage),
|
Missing required Proxmox inputs. Define hypervisor.(url,username,password,host,storage),
|
||||||
system.id, and system.network.bridge (or system.network.interfaces[]).
|
system.id, and system.network.bridge (or system.network.interfaces[]).
|
||||||
quiet: true
|
quiet: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Validate VMware hypervisor inputs
|
- name: Validate VMware hypervisor inputs
|
||||||
when:
|
when:
|
||||||
@@ -182,6 +185,7 @@
|
|||||||
Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage)
|
Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage)
|
||||||
and system.network.bridge (or system.network.interfaces[]).
|
and system.network.bridge (or system.network.interfaces[]).
|
||||||
quiet: true
|
quiet: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Validate Xen hypervisor inputs
|
- name: Validate Xen hypervisor inputs
|
||||||
when:
|
when:
|
||||||
@@ -195,6 +199,18 @@
|
|||||||
fail_msg: "Missing required Xen inputs. Define system.network.bridge (or system.network.interfaces[])."
|
fail_msg: "Missing required Xen inputs. Define system.network.bridge (or system.network.interfaces[])."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate libvirt hypervisor inputs
|
||||||
|
when:
|
||||||
|
- system_cfg.type == "virtual"
|
||||||
|
- hypervisor_type == "libvirt"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- >-
|
||||||
|
(system_cfg.network.bridge | default('') | string | length > 0)
|
||||||
|
or (system_cfg.network.interfaces | default([]) | length > 0)
|
||||||
|
fail_msg: "Missing required libvirt inputs. Define system.network.bridge (or system.network.interfaces[])."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
- name: Validate virtual installer ISO requirement
|
- name: Validate virtual installer ISO requirement
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
@@ -245,6 +261,28 @@
|
|||||||
fail_msg: "Invalid system sizing. Check system.cpus, system.memory, and system.disks[0].size."
|
fail_msg: "Invalid system sizing. Check system.cpus, system.memory, and system.disks[0].size."
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
|
- name: Validate at least one user is defined
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.users | default([]) | length > 0
|
||||||
|
- system_cfg.users[0].name is defined and (system_cfg.users[0].name | string | length) > 0
|
||||||
|
- system_cfg.users[0].password is defined and (system_cfg.users[0].password | string | length) > 0
|
||||||
|
fail_msg: "At least one user with a name and password must be defined in system.users[]."
|
||||||
|
quiet: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Validate DNS servers is a list
|
||||||
|
when:
|
||||||
|
- system_cfg.network.dns.servers is defined
|
||||||
|
- system_cfg.network.dns.servers | length > 0
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- system_cfg.network.dns.servers is iterable
|
||||||
|
- system_cfg.network.dns.servers is not string
|
||||||
|
- system_cfg.network.dns.servers is not mapping
|
||||||
|
fail_msg: "system.network.dns.servers must be a list."
|
||||||
|
quiet: true
|
||||||
|
|
||||||
- name: Validate all virtual disks have a positive size
|
- name: Validate all virtual disks have a positive size
|
||||||
when: system_cfg.type == "virtual"
|
when: system_cfg.type == "virtual"
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
|
|||||||
@@ -6,20 +6,40 @@ partitioning_efi_size_mib: 512
|
|||||||
partitioning_efi_start_mib: 1
|
partitioning_efi_start_mib: 1
|
||||||
partitioning_efi_end_mib: "{{ (partitioning_efi_start_mib | int) + (partitioning_efi_size_mib | int) }}"
|
partitioning_efi_end_mib: "{{ (partitioning_efi_start_mib | int) + (partitioning_efi_size_mib | int) }}"
|
||||||
partitioning_boot_size_mib: 1024
|
partitioning_boot_size_mib: 1024
|
||||||
|
partitioning_vg_name: sys
|
||||||
partitioning_use_full_disk: true
|
partitioning_use_full_disk: true
|
||||||
|
|
||||||
|
# LVM logical volume sizing
|
||||||
|
partitioning_lvm_var_gb: 2
|
||||||
|
partitioning_lvm_var_log_gb: 2
|
||||||
|
partitioning_lvm_var_log_audit_gb: 1.5
|
||||||
|
|
||||||
|
# Disk overhead subtracted from available space in swap/home calculations
|
||||||
|
partitioning_disk_overhead_gb: 20
|
||||||
|
|
||||||
|
# CIS-required reserved space for /var, /var/log, /var/log/audit, /home
|
||||||
|
partitioning_cis_reserved_gb: 7.5
|
||||||
|
|
||||||
|
# Home allocation: percentage of (disk - overhead), bounded by min/max
|
||||||
|
partitioning_home_allocation_pct: 0.1
|
||||||
|
partitioning_home_min_gb: 2
|
||||||
|
partitioning_home_max_gb: 20
|
||||||
|
|
||||||
|
# Btrfs home quota (applied when CIS is enabled)
|
||||||
|
partitioning_btrfs_home_quota: 2G
|
||||||
partitioning_separate_boot: >-
|
partitioning_separate_boot: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
(system_cfg.luks.enabled | bool)
|
(system_cfg.luks.enabled | bool)
|
||||||
or (system_cfg.filesystem != 'btrfs')
|
or (system_cfg.filesystem != 'btrfs')
|
||||||
)
|
)
|
||||||
and (os not in ['archlinux'])
|
and ((os | default('')) not in ['archlinux'])
|
||||||
}}
|
}}
|
||||||
partitioning_boot_fs_fstype: >-
|
partitioning_boot_fs_fstype: >-
|
||||||
{{
|
{{
|
||||||
system_cfg.filesystem
|
system_cfg.filesystem
|
||||||
if system_cfg.filesystem != 'btrfs'
|
if system_cfg.filesystem != 'btrfs'
|
||||||
else ('xfs' if is_rhel else 'ext4')
|
else ('xfs' if (is_rhel | default(false) | bool) else 'ext4')
|
||||||
}}
|
}}
|
||||||
partitioning_boot_fs_partition_suffix: >-
|
partitioning_boot_fs_partition_suffix: >-
|
||||||
{{
|
{{
|
||||||
@@ -37,7 +57,7 @@ partitioning_efi_mountpoint: >-
|
|||||||
if (partitioning_separate_boot | bool)
|
if (partitioning_separate_boot | bool)
|
||||||
else (
|
else (
|
||||||
'/boot/efi'
|
'/boot/efi'
|
||||||
if is_rhel or (os in ['ubuntu', 'ubuntu-lts'] or (os == 'debian' and (os_version | string) in ['11', '12', '13']))
|
if (is_rhel | default(false) | bool) or ((os | default('')) in ['ubuntu', 'ubuntu-lts'] or ((os | default('')) == 'debian' and (os_version | default('') | string) in ['11', '12', '13']))
|
||||||
else '/boot'
|
else '/boot'
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -54,13 +54,17 @@
|
|||||||
- { subvol: pkg }
|
- { subvol: pkg }
|
||||||
- { subvol: var_log }
|
- { subvol: var_log }
|
||||||
- { subvol: var_log_audit }
|
- { subvol: var_log_audit }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.subvol }}"
|
||||||
register: partitioning_btrfs_subvol_result
|
register: partitioning_btrfs_subvol_result
|
||||||
|
|
||||||
- name: Set quotas for subvolumes
|
- name: Set quotas for subvolumes
|
||||||
when: system_cfg.features.cis.enabled
|
when: system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
ansible.builtin.command: btrfs qgroup limit {{ item.quota }} /mnt/{{ '@' if item.subvol == 'root' else '@' + item.subvol }}
|
||||||
loop:
|
loop:
|
||||||
- { subvol: home, quota: 2G }
|
- { subvol: home, quota: "{{ partitioning_btrfs_home_quota }}" }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.subvol }}"
|
||||||
register: partitioning_btrfs_qgroup_result
|
register: partitioning_btrfs_qgroup_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
- name: Create and format ext4 logical volumes
|
- name: Create and format ext4 logical volumes
|
||||||
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: /dev/sys/{{ item.lv }}
|
dev: /dev/{{ partitioning_vg_name }}/{{ item.lv }}
|
||||||
fstype: ext4
|
fstype: ext4
|
||||||
force: true
|
force: true
|
||||||
loop:
|
loop:
|
||||||
@@ -11,17 +11,21 @@
|
|||||||
- { lv: var }
|
- { lv: var }
|
||||||
- { lv: var_log }
|
- { lv: var_log }
|
||||||
- { lv: var_log_audit }
|
- { lv: var_log_audit }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.lv }}"
|
||||||
|
|
||||||
- name: Remove Unsupported features for older Systems
|
- name: Remove Unsupported features for older Systems
|
||||||
when: >
|
when: >
|
||||||
(os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11'))
|
(os in ['almalinux', 'rocky', 'rhel'] or (os == 'debian' and (os_version | string) == '11'))
|
||||||
and (system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'])
|
and (system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit'])
|
||||||
ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/sys/{{ item.lv }}"
|
ansible.builtin.command: tune2fs -O "^orphan_file,^metadata_csum_seed" "/dev/{{ partitioning_vg_name }}/{{ item.lv }}"
|
||||||
loop:
|
loop:
|
||||||
- { lv: root }
|
- { lv: root }
|
||||||
- { lv: home }
|
- { lv: home }
|
||||||
- { lv: var }
|
- { lv: var }
|
||||||
- { lv: var_log }
|
- { lv: var_log }
|
||||||
- { lv: var_log_audit }
|
- { lv: var_log_audit }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.lv }}"
|
||||||
register: partitioning_ext4_tune_result
|
register: partitioning_ext4_tune_result
|
||||||
changed_when: partitioning_ext4_tune_result.rc == 0
|
changed_when: partitioning_ext4_tune_result.rc == 0
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
| selectattr('mount.path')
|
| selectattr('mount.path')
|
||||||
| list
|
| list
|
||||||
}}
|
}}
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Validate additional disks do not target install_drive
|
- name: Validate additional disks do not target install_drive
|
||||||
when: partitioning_extra_disks | length > 0
|
when: partitioning_extra_disks | length > 0
|
||||||
@@ -59,7 +58,16 @@
|
|||||||
dev: "{{ item.partition }}"
|
dev: "{{ item.partition }}"
|
||||||
fstype: "{{ item.mount.fstype }}"
|
fstype: "{{ item.mount.fstype }}"
|
||||||
opts: "{{ _all_opts }}"
|
opts: "{{ _all_opts }}"
|
||||||
force: true
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.partition }}"
|
||||||
|
|
||||||
|
- name: Collect extra disk UUIDs
|
||||||
|
when: partitioning_extra_disks | length > 0
|
||||||
|
ansible.builtin.command: "blkid -s UUID -o value {{ item.partition }}"
|
||||||
|
register: partitioning_extra_disk_uuids
|
||||||
|
changed_when: false
|
||||||
|
failed_when: partitioning_extra_disk_uuids.rc != 0 or (partitioning_extra_disk_uuids.stdout | trim | length) == 0
|
||||||
loop: "{{ partitioning_extra_disks }}"
|
loop: "{{ partitioning_extra_disks }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.partition }}"
|
label: "{{ item.partition }}"
|
||||||
@@ -79,11 +87,11 @@
|
|||||||
- name: Mount additional disks for fstab generation
|
- name: Mount additional disks for fstab generation
|
||||||
when: partitioning_extra_disks | length > 0
|
when: partitioning_extra_disks | length > 0
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: "/mnt{{ item.mount.path }}"
|
path: "/mnt{{ item.0.mount.path }}"
|
||||||
src: "{{ item.partition }}"
|
src: "UUID={{ item.1.stdout }}"
|
||||||
fstype: "{{ item.mount.fstype }}"
|
fstype: "{{ item.0.mount.fstype }}"
|
||||||
opts: "{{ item.mount.opts | default('defaults') }}"
|
opts: "{{ item.0.mount.opts | default('defaults') }}"
|
||||||
state: mounted
|
state: mounted
|
||||||
loop: "{{ partitioning_extra_disks }}"
|
loop: "{{ partitioning_extra_disks | zip(partitioning_extra_disk_uuids.results) | list }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.mount.path }}"
|
label: "{{ item.0.mount.path }}"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
when:
|
when:
|
||||||
- system_cfg.features.swap.enabled | bool
|
- system_cfg.features.swap.enabled | bool
|
||||||
- partitioning_vm_memory is not defined or (partitioning_vm_memory | float) <= 0
|
- partitioning_vm_memory is not defined or (partitioning_vm_memory | float) <= 0
|
||||||
- system_cfg is not defined or (system_cfg.memory | default(0) | float) <= 0
|
- (system_cfg.memory | default(0) | float) <= 0
|
||||||
block:
|
block:
|
||||||
- name: Read system memory
|
- name: Read system memory
|
||||||
ansible.builtin.command: awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo
|
ansible.builtin.command: awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
- name: Remove LVM volume group
|
- name: Remove LVM volume group
|
||||||
community.general.lvg:
|
community.general.lvg:
|
||||||
vg: sys
|
vg: "{{ partitioning_vg_name }}"
|
||||||
state: absent
|
state: absent
|
||||||
force: true
|
force: true
|
||||||
failed_when: false
|
failed_when: false
|
||||||
@@ -93,10 +93,10 @@
|
|||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Wipe filesystem signatures
|
- name: Wipe filesystem signatures
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.shell: >-
|
||||||
find /dev -wholename "{{ install_drive }}*" -exec wipefs --force --all {} \;
|
find /dev -wholename "{{ install_drive }}*" -exec wipefs --force --all {} \;
|
||||||
register: partitioning_wipefs_result
|
register: partitioning_wipefs_result
|
||||||
changed_when: false
|
changed_when: partitioning_wipefs_result.rc == 0
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
|
||||||
- name: Refresh kernel partition table
|
- name: Refresh kernel partition table
|
||||||
@@ -122,6 +122,8 @@
|
|||||||
flags: "{{ item.flags | default(omit) }}"
|
flags: "{{ item.flags | default(omit) }}"
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ partitioning_layout }}"
|
loop: "{{ partitioning_layout }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
rescue:
|
rescue:
|
||||||
- name: Refresh kernel partition table after failure
|
- name: Refresh kernel partition table after failure
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.command: "{{ item }}"
|
||||||
@@ -144,6 +146,8 @@
|
|||||||
flags: "{{ item.flags | default(omit) }}"
|
flags: "{{ item.flags | default(omit) }}"
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ partitioning_layout }}"
|
loop: "{{ partitioning_layout }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.name }}"
|
||||||
|
|
||||||
- name: Settle partition table
|
- name: Settle partition table
|
||||||
ansible.builtin.command: "{{ item }}"
|
ansible.builtin.command: "{{ item }}"
|
||||||
@@ -251,7 +255,7 @@
|
|||||||
block:
|
block:
|
||||||
- name: Create LVM volume group
|
- name: Create LVM volume group
|
||||||
community.general.lvg:
|
community.general.lvg:
|
||||||
vg: sys
|
vg: "{{ partitioning_vg_name }}"
|
||||||
pvs: "{{ partitioning_root_device }}"
|
pvs: "{{ partitioning_root_device }}"
|
||||||
|
|
||||||
- name: Create LVM logical volumes
|
- name: Create LVM logical volumes
|
||||||
@@ -272,10 +276,11 @@
|
|||||||
partitioning_lvm_swap_target_gb: >-
|
partitioning_lvm_swap_target_gb: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
[
|
((partitioning_memory_mb | float / 1024) >= 16.0)
|
||||||
(partitioning_memory_mb | float / 1024),
|
| ternary(
|
||||||
4
|
(partitioning_memory_mb | float / 2048),
|
||||||
] | max | float
|
[(partitioning_memory_mb | float / 1024), 4] | max | float
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if system_cfg.features.swap.enabled | bool
|
if system_cfg.features.swap.enabled | bool
|
||||||
else 0
|
else 0
|
||||||
@@ -285,7 +290,7 @@
|
|||||||
(
|
(
|
||||||
4
|
4
|
||||||
+ [
|
+ [
|
||||||
(partitioning_disk_size_gb | float) - 20,
|
(partitioning_disk_size_gb | float) - (partitioning_disk_overhead_gb | float),
|
||||||
0
|
0
|
||||||
] | max
|
] | max
|
||||||
)
|
)
|
||||||
@@ -310,7 +315,7 @@
|
|||||||
(
|
(
|
||||||
(partitioning_disk_size_gb | float)
|
(partitioning_disk_size_gb | float)
|
||||||
- (partitioning_reserved_gb | float)
|
- (partitioning_reserved_gb | float)
|
||||||
- (system_cfg.features.cis.enabled | ternary(7.5, 0))
|
- (system_cfg.features.cis.enabled | ternary(partitioning_cis_reserved_gb | float, 0))
|
||||||
- partitioning_lvm_extent_reserve_gb
|
- partitioning_lvm_extent_reserve_gb
|
||||||
- 4
|
- 4
|
||||||
),
|
),
|
||||||
@@ -325,14 +330,22 @@
|
|||||||
(
|
(
|
||||||
(partitioning_disk_size_gb | float)
|
(partitioning_disk_size_gb | float)
|
||||||
- (partitioning_reserved_gb | float)
|
- (partitioning_reserved_gb | float)
|
||||||
- (system_cfg.features.cis.enabled | ternary(7.5, 0))
|
- (system_cfg.features.cis.enabled | ternary(partitioning_cis_reserved_gb | float, 0))
|
||||||
- partitioning_lvm_extent_reserve_gb
|
- partitioning_lvm_extent_reserve_gb
|
||||||
- partitioning_lvm_swap_target_limited_gb
|
- partitioning_lvm_swap_target_limited_gb
|
||||||
) | float
|
) | float
|
||||||
}}
|
}}
|
||||||
|
partitioning_lvm_home_raw_gb: >-
|
||||||
|
{{
|
||||||
|
((partitioning_disk_size_gb | float) - (partitioning_disk_overhead_gb | float))
|
||||||
|
* (partitioning_home_allocation_pct | float)
|
||||||
|
}}
|
||||||
partitioning_lvm_home_gb: >-
|
partitioning_lvm_home_gb: >-
|
||||||
{{
|
{{
|
||||||
([([(((partitioning_disk_size_gb | float) - 20) * 0.1), 2] | max), 20] | min)
|
[
|
||||||
|
[(partitioning_lvm_home_raw_gb | float), (partitioning_home_min_gb | float)] | max,
|
||||||
|
(partitioning_home_max_gb | float)
|
||||||
|
] | min
|
||||||
}}
|
}}
|
||||||
partitioning_lvm_root_default_gb: >-
|
partitioning_lvm_root_default_gb: >-
|
||||||
{{
|
{{
|
||||||
@@ -374,7 +387,10 @@
|
|||||||
- (partitioning_lvm_swap_gb | float)
|
- (partitioning_lvm_swap_gb | float)
|
||||||
- partitioning_lvm_extent_reserve_gb
|
- partitioning_lvm_extent_reserve_gb
|
||||||
- (
|
- (
|
||||||
(partitioning_lvm_home_gb | float) + 5.5
|
(partitioning_lvm_home_gb | float)
|
||||||
|
+ (partitioning_lvm_var_gb | float)
|
||||||
|
+ (partitioning_lvm_var_log_gb | float)
|
||||||
|
+ (partitioning_lvm_var_log_audit_gb | float)
|
||||||
if system_cfg.features.cis.enabled
|
if system_cfg.features.cis.enabled
|
||||||
else 0
|
else 0
|
||||||
)
|
)
|
||||||
@@ -389,7 +405,7 @@
|
|||||||
else partitioning_lvm_root_default_gb
|
else partitioning_lvm_root_default_gb
|
||||||
}}
|
}}
|
||||||
community.general.lvol:
|
community.general.lvol:
|
||||||
vg: sys
|
vg: "{{ partitioning_vg_name }}"
|
||||||
lv: "{{ item.lv }}"
|
lv: "{{ item.lv }}"
|
||||||
size: "{{ item.size }}"
|
size: "{{ item.size }}"
|
||||||
state: present
|
state: present
|
||||||
@@ -400,9 +416,11 @@
|
|||||||
size: "{{ partitioning_lvm_swap_gb | string + 'G' }}"
|
size: "{{ partitioning_lvm_swap_gb | string + 'G' }}"
|
||||||
- lv: home
|
- lv: home
|
||||||
size: "{{ partitioning_lvm_home_gb | string + 'G' }}"
|
size: "{{ partitioning_lvm_home_gb | string + 'G' }}"
|
||||||
- { lv: var, size: "2G" }
|
- { lv: var, size: "{{ partitioning_lvm_var_gb }}G" }
|
||||||
- { lv: var_log, size: "2G" }
|
- { lv: var_log, size: "{{ partitioning_lvm_var_log_gb }}G" }
|
||||||
- { lv: var_log_audit, size: "1.5G" }
|
- { lv: var_log_audit, size: "{{ partitioning_lvm_var_log_audit_gb }}G" }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.lv }}"
|
||||||
|
|
||||||
- name: Create filesystems
|
- name: Create filesystems
|
||||||
block:
|
block:
|
||||||
@@ -438,7 +456,7 @@
|
|||||||
- system_cfg.features.swap.enabled | bool
|
- system_cfg.features.swap.enabled | bool
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
fstype: swap
|
fstype: swap
|
||||||
dev: /dev/sys/swap
|
dev: /dev/{{ partitioning_vg_name }}/swap
|
||||||
|
|
||||||
- name: Create filesystem
|
- name: Create filesystem
|
||||||
ansible.builtin.include_tasks: "{{ system_cfg.filesystem }}.yml"
|
ansible.builtin.include_tasks: "{{ system_cfg.filesystem }}.yml"
|
||||||
@@ -447,6 +465,7 @@
|
|||||||
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_partition_suffix }}'
|
ansible.builtin.command: blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_partition_suffix }}'
|
||||||
register: partitioning_boot_uuid
|
register: partitioning_boot_uuid
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_boot_uuid.rc != 0 or (partitioning_boot_uuid.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for /boot filesystem
|
- name: Get UUID for /boot filesystem
|
||||||
when: partitioning_separate_boot | bool
|
when: partitioning_separate_boot | bool
|
||||||
@@ -454,57 +473,65 @@
|
|||||||
blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}'
|
blkid -s UUID -o value '{{ install_drive }}{{ partitioning_boot_fs_partition_suffix }}'
|
||||||
register: partitioning_boot_fs_uuid
|
register: partitioning_boot_fs_uuid
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_boot_fs_uuid.rc != 0 or (partitioning_boot_fs_uuid.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for main filesystem
|
- name: Get UUID for main filesystem
|
||||||
ansible.builtin.command: blkid -s UUID -o value '{{ partitioning_root_device }}'
|
ansible.builtin.command: blkid -s UUID -o value '{{ partitioning_root_device }}'
|
||||||
register: partitioning_main_uuid
|
register: partitioning_main_uuid
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_main_uuid.rc != 0 or (partitioning_main_uuid.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM root filesystem
|
- name: Get UUID for LVM root filesystem
|
||||||
when: system_cfg.filesystem != 'btrfs'
|
when: system_cfg.filesystem != 'btrfs'
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/root
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/root
|
||||||
register: partitioning_uuid_root_result
|
register: partitioning_uuid_root_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_root_result.rc != 0 or (partitioning_uuid_root_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM swap filesystem
|
- name: Get UUID for LVM swap filesystem
|
||||||
when:
|
when:
|
||||||
- system_cfg.filesystem != 'btrfs'
|
- system_cfg.filesystem != 'btrfs'
|
||||||
- system_cfg.features.swap.enabled | bool
|
- system_cfg.features.swap.enabled | bool
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/swap
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/swap
|
||||||
register: partitioning_uuid_swap_result
|
register: partitioning_uuid_swap_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_swap_result.rc != 0 or (partitioning_uuid_swap_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM home filesystem
|
- name: Get UUID for LVM home filesystem
|
||||||
when:
|
when:
|
||||||
- system_cfg.filesystem != 'btrfs'
|
- system_cfg.filesystem != 'btrfs'
|
||||||
- system_cfg.features.cis.enabled
|
- system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/home
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/home
|
||||||
register: partitioning_uuid_home_result
|
register: partitioning_uuid_home_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_home_result.rc != 0 or (partitioning_uuid_home_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM var filesystem
|
- name: Get UUID for LVM var filesystem
|
||||||
when:
|
when:
|
||||||
- system_cfg.filesystem != 'btrfs'
|
- system_cfg.filesystem != 'btrfs'
|
||||||
- system_cfg.features.cis.enabled
|
- system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/var
|
||||||
register: partitioning_uuid_var_result
|
register: partitioning_uuid_var_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_var_result.rc != 0 or (partitioning_uuid_var_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM var_log filesystem
|
- name: Get UUID for LVM var_log filesystem
|
||||||
when:
|
when:
|
||||||
- system_cfg.filesystem != 'btrfs'
|
- system_cfg.filesystem != 'btrfs'
|
||||||
- system_cfg.features.cis.enabled
|
- system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/var_log
|
||||||
register: partitioning_uuid_var_log_result
|
register: partitioning_uuid_var_log_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_var_log_result.rc != 0 or (partitioning_uuid_var_log_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Get UUID for LVM var_log_audit filesystem
|
- name: Get UUID for LVM var_log_audit filesystem
|
||||||
when:
|
when:
|
||||||
- system_cfg.filesystem != 'btrfs'
|
- system_cfg.filesystem != 'btrfs'
|
||||||
- system_cfg.features.cis.enabled
|
- system_cfg.features.cis.enabled
|
||||||
ansible.builtin.command: blkid -s UUID -o value /dev/sys/var_log_audit
|
ansible.builtin.command: blkid -s UUID -o value /dev/{{ partitioning_vg_name }}/var_log_audit
|
||||||
register: partitioning_uuid_var_log_audit_result
|
register: partitioning_uuid_var_log_audit_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
failed_when: partitioning_uuid_var_log_audit_result.rc != 0 or (partitioning_uuid_var_log_audit_result.stdout | trim | length) == 0
|
||||||
|
|
||||||
- name: Assign UUIDs to Variables
|
- name: Assign UUIDs to Variables
|
||||||
when: system_cfg.filesystem != 'btrfs'
|
when: system_cfg.filesystem != 'btrfs'
|
||||||
@@ -514,31 +541,31 @@
|
|||||||
{{
|
{{
|
||||||
partitioning_uuid_swap_result.stdout_lines | default([])
|
partitioning_uuid_swap_result.stdout_lines | default([])
|
||||||
if system_cfg.features.swap.enabled | bool
|
if system_cfg.features.swap.enabled | bool
|
||||||
else ''
|
else []
|
||||||
}}
|
}}
|
||||||
partitioning_uuid_home: >-
|
partitioning_uuid_home: >-
|
||||||
{{
|
{{
|
||||||
partitioning_uuid_home_result.stdout_lines | default([])
|
partitioning_uuid_home_result.stdout_lines | default([])
|
||||||
if system_cfg.features.cis.enabled
|
if system_cfg.features.cis.enabled
|
||||||
else ''
|
else []
|
||||||
}}
|
}}
|
||||||
partitioning_uuid_var: >-
|
partitioning_uuid_var: >-
|
||||||
{{
|
{{
|
||||||
partitioning_uuid_var_result.stdout_lines | default([])
|
partitioning_uuid_var_result.stdout_lines | default([])
|
||||||
if system_cfg.features.cis.enabled
|
if system_cfg.features.cis.enabled
|
||||||
else ''
|
else []
|
||||||
}}
|
}}
|
||||||
partitioning_uuid_var_log: >-
|
partitioning_uuid_var_log: >-
|
||||||
{{
|
{{
|
||||||
partitioning_uuid_var_log_result.stdout_lines | default([])
|
partitioning_uuid_var_log_result.stdout_lines | default([])
|
||||||
if system_cfg.features.cis.enabled
|
if system_cfg.features.cis.enabled
|
||||||
else ''
|
else []
|
||||||
}}
|
}}
|
||||||
partitioning_uuid_var_log_audit: >-
|
partitioning_uuid_var_log_audit: >-
|
||||||
{{
|
{{
|
||||||
partitioning_uuid_var_log_audit_result.stdout_lines | default([])
|
partitioning_uuid_var_log_audit_result.stdout_lines | default([])
|
||||||
if system_cfg.features.cis.enabled
|
if system_cfg.features.cis.enabled
|
||||||
else ''
|
else []
|
||||||
}}
|
}}
|
||||||
|
|
||||||
- name: Mount filesystems
|
- name: Mount filesystems
|
||||||
@@ -562,6 +589,7 @@
|
|||||||
opts: "{{ item.opts }}"
|
opts: "{{ item.opts }}"
|
||||||
state: mounted
|
state: mounted
|
||||||
loop:
|
loop:
|
||||||
|
# ssd: no-op on kernels 5.15+ (btrfs auto-detects); kept for older kernel compat
|
||||||
- path: ""
|
- path: ""
|
||||||
uuid: "{{ partitioning_uuid_root[0] | default(omit) }}"
|
uuid: "{{ partitioning_uuid_root[0] | default(omit) }}"
|
||||||
opts: >-
|
opts: >-
|
||||||
@@ -636,6 +664,8 @@
|
|||||||
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit'
|
'ssd', 'space_cache=v2', 'discard=async', 'subvol=@var_log_audit'
|
||||||
] | reject('equalto', '') | join(',')
|
] | reject('equalto', '') | join(',')
|
||||||
}}
|
}}
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.path }}"
|
||||||
|
|
||||||
- name: Mount /boot filesystem
|
- name: Mount /boot filesystem
|
||||||
when: partitioning_separate_boot | bool
|
when: partitioning_separate_boot | bool
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
- name: Create and format XFS logical volumes
|
- name: Create and format XFS logical volumes
|
||||||
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
when: system_cfg.features.cis.enabled or item.lv not in ['home', 'var', 'var_log', 'var_log_audit']
|
||||||
community.general.filesystem:
|
community.general.filesystem:
|
||||||
dev: /dev/sys/{{ item.lv }}
|
dev: /dev/{{ partitioning_vg_name }}/{{ item.lv }}
|
||||||
fstype: xfs
|
fstype: xfs
|
||||||
opts: "{{ '-m bigtime=0 -i nrext64=0,exchange=0 -n parent=0' if is_rhel | bool else omit }}"
|
opts: "{{ '-m bigtime=0 -i nrext64=0,exchange=0 -n parent=0' if is_rhel | bool else omit }}"
|
||||||
force: true
|
force: true
|
||||||
@@ -12,3 +12,5 @@
|
|||||||
- { lv: var }
|
- { lv: var }
|
||||||
- { lv: var_log }
|
- { lv: var_log }
|
||||||
- { lv: var_log_audit }
|
- { lv: var_log_audit }
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.lv }}"
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
---
|
---
|
||||||
|
- name: Physical install safety confirmation
|
||||||
|
when: system_cfg.type == "physical"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- physical_install_confirmed | default(false) | bool
|
||||||
|
fail_msg: >-
|
||||||
|
DANGER: Physical install will WIPE {{ install_drive }} on {{ inventory_hostname }}.
|
||||||
|
Set physical_install_confirmed=true in inventory to proceed.
|
||||||
|
quiet: true
|
||||||
|
|
||||||
- name: VM existence protection check
|
- name: VM existence protection check
|
||||||
when: system_cfg.type == "virtual"
|
when: system_cfg.type == "virtual"
|
||||||
block:
|
block:
|
||||||
@@ -23,23 +33,27 @@
|
|||||||
Please choose a different hostname or remove the existing VM manually before proceeding.
|
Please choose a different hostname or remove the existing VM manually before proceeding.
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Check if VM already exists on Proxmox
|
- name: Check VM existence on Proxmox
|
||||||
when: hypervisor_type == "proxmox"
|
when: hypervisor_type == "proxmox"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
become: false
|
become: false
|
||||||
|
module_defaults:
|
||||||
community.proxmox.proxmox_vm_info:
|
community.proxmox.proxmox_vm_info:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
block:
|
||||||
|
- name: Query Proxmox for existing VM
|
||||||
|
community.proxmox.proxmox_vm_info:
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
type: qemu
|
type: qemu
|
||||||
register: system_check_proxmox_check_result
|
register: system_check_proxmox_check_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Abort if VM already exists on Proxmox
|
- name: Abort if VM already exists on Proxmox
|
||||||
when: hypervisor_type == "proxmox"
|
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- system_check_proxmox_check_result.proxmox_vms | default([]) | length == 0
|
- system_check_proxmox_check_result.proxmox_vms | default([]) | length == 0
|
||||||
@@ -49,23 +63,27 @@
|
|||||||
Please choose a different hostname or VM ID, or remove the existing VM manually before proceeding.
|
Please choose a different hostname or VM ID, or remove the existing VM manually before proceeding.
|
||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Check if VM already exists in vCenter
|
- name: Check VM existence in vCenter
|
||||||
when: hypervisor_type == "vmware"
|
when: hypervisor_type == "vmware"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
module_defaults:
|
||||||
community.vmware.vmware_guest_info:
|
community.vmware.vmware_guest_info:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
block:
|
||||||
|
- name: Query vCenter for existing VM
|
||||||
|
community.vmware.vmware_guest_info:
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
folder: "{{ system_cfg.path if system_cfg.path | length > 0 else omit }}"
|
folder: "{{ system_cfg.path if system_cfg.path | length > 0 else omit }}"
|
||||||
register: system_check_vmware_check_result
|
register: system_check_vmware_check_result
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Fail if vCenter lookup failed unexpectedly
|
- name: Fail if vCenter lookup failed unexpectedly
|
||||||
when: hypervisor_type == "vmware"
|
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- not system_check_vmware_check_result.failed
|
- not system_check_vmware_check_result.failed
|
||||||
@@ -76,7 +94,6 @@
|
|||||||
quiet: true
|
quiet: true
|
||||||
|
|
||||||
- name: Abort if VM already exists in vCenter
|
- name: Abort if VM already exists in vCenter
|
||||||
when: hypervisor_type == "vmware"
|
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- system_check_vmware_check_result.instance is not defined
|
- system_check_vmware_check_result.instance is not defined
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ virtualization_libvirt_cloudinit_path: >-
|
|||||||
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
{{ [virtualization_libvirt_image_dir, hostname ~ '-cloudinit.iso'] | ansible.builtin.path_join }}
|
||||||
virtualization_xen_disk_path: /var/lib/xen/images
|
virtualization_xen_disk_path: /var/lib/xen/images
|
||||||
|
|
||||||
|
virtualization_libvirt_machine_type: q35
|
||||||
|
virtualization_libvirt_ovmf_code: /usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd
|
||||||
|
virtualization_libvirt_ovmf_vars: /usr/share/edk2/x64/OVMF_VARS.4m.fd
|
||||||
|
|
||||||
virtualization_tpm2_enabled: >-
|
virtualization_tpm2_enabled: >-
|
||||||
{{
|
{{
|
||||||
(system_cfg.luks.enabled | bool)
|
(system_cfg.luks.enabled | bool)
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item | to_json }}"
|
label: "{{ item | to_json }}"
|
||||||
extended: true
|
extended: true
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Create VM disks
|
- name: Create VM disks
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
- "/tmp/cloud-network-config-{{ hostname }}.yml"
|
- "/tmp/cloud-network-config-{{ hostname }}.yml"
|
||||||
creates: "{{ virtualization_libvirt_cloudinit_path }}"
|
creates: "{{ virtualization_libvirt_cloudinit_path }}"
|
||||||
|
|
||||||
|
# uri defaults to qemu:///system (local libvirtd)
|
||||||
- name: Create VM using libvirt
|
- name: Create VM using libvirt
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
community.libvirt.virt:
|
community.libvirt.virt:
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: Deploy VM on Proxmox
|
- name: Deploy VM on Proxmox
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
module_defaults:
|
||||||
|
community.proxmox.proxmox_kvm:
|
||||||
|
api_host: "{{ hypervisor_cfg.url }}"
|
||||||
|
api_user: "{{ hypervisor_cfg.username }}"
|
||||||
|
api_password: "{{ hypervisor_cfg.password }}"
|
||||||
|
node: "{{ hypervisor_cfg.host }}"
|
||||||
|
block:
|
||||||
|
- name: Create VM on Proxmox
|
||||||
vars:
|
vars:
|
||||||
virtualization_proxmox_scsi: >-
|
virtualization_proxmox_scsi: >-
|
||||||
{%- set out = {} -%}
|
{%- set out = {} -%}
|
||||||
@@ -31,13 +39,9 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{{ out }}
|
{{ out }}
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
|
||||||
ciuser: "{{ system_cfg.users[0].name }}"
|
ciuser: "{{ system_cfg.users[0].name }}"
|
||||||
cipassword: "{{ system_cfg.users[0].password }}"
|
cipassword: "{{ system_cfg.users[0].password }}"
|
||||||
ciupgrade: false
|
ciupgrade: false
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
cpu: host
|
cpu: host
|
||||||
@@ -74,17 +78,14 @@
|
|||||||
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
searchdomains: "{{ system_cfg.network.dns.search if system_cfg.network.dns.search | length else omit }}"
|
||||||
onboot: true
|
onboot: true
|
||||||
state: present
|
state: present
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Start VM on Proxmox
|
- name: Start VM on Proxmox
|
||||||
delegate_to: localhost
|
|
||||||
community.proxmox.proxmox_kvm:
|
community.proxmox.proxmox_kvm:
|
||||||
api_host: "{{ hypervisor_cfg.url }}"
|
|
||||||
api_user: "{{ hypervisor_cfg.username }}"
|
|
||||||
api_password: "{{ hypervisor_cfg.password }}"
|
|
||||||
node: "{{ hypervisor_cfg.host }}"
|
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
vmid: "{{ system_cfg.id }}"
|
vmid: "{{ system_cfg.id }}"
|
||||||
state: started
|
state: started
|
||||||
|
no_log: true
|
||||||
register: virtualization_proxmox_start_result
|
register: virtualization_proxmox_start_result
|
||||||
|
|
||||||
- name: Set VM created fact
|
- name: Set VM created fact
|
||||||
|
|||||||
@@ -10,10 +10,31 @@
|
|||||||
loop: "{{ system_cfg.disks }}"
|
loop: "{{ system_cfg.disks }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item | to_json }}"
|
label: "{{ item | to_json }}"
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Create VM in vCenter
|
- name: Deploy VM in vCenter
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
module_defaults:
|
||||||
|
community.vmware.vmware_guest:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
community.vmware.vmware_guest_tpm:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
vmware.vmware.vm_powerstate:
|
||||||
|
hostname: "{{ hypervisor_cfg.url }}"
|
||||||
|
username: "{{ hypervisor_cfg.username }}"
|
||||||
|
password: "{{ hypervisor_cfg.password }}"
|
||||||
|
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
||||||
|
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
||||||
|
block:
|
||||||
|
# community.vmware: full-featured guest management
|
||||||
|
- name: Create VM in vCenter
|
||||||
vars:
|
vars:
|
||||||
virtualization_vmware_networks: >-
|
virtualization_vmware_networks: >-
|
||||||
{%- set ns = namespace(out=[]) -%}
|
{%- set ns = namespace(out=[]) -%}
|
||||||
@@ -26,14 +47,10 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
{{ ns.out }}
|
{{ ns.out }}
|
||||||
community.vmware.vmware_guest:
|
community.vmware.vmware_guest:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
|
||||||
cluster: "{{ hypervisor_cfg.cluster }}"
|
cluster: "{{ hypervisor_cfg.cluster }}"
|
||||||
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
|
# Generic guest ID — VMware auto-detects OS post-install
|
||||||
guest_id: otherLinux64Guest
|
guest_id: otherLinux64Guest
|
||||||
annotation: |
|
annotation: |
|
||||||
{{ note if note is defined else '' }}
|
{{ note if note is defined else '' }}
|
||||||
@@ -65,6 +82,7 @@
|
|||||||
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
} ] if rhel_iso is defined and rhel_iso | length > 0 else [] )
|
||||||
}}
|
}}
|
||||||
networks: "{{ virtualization_vmware_networks }}"
|
networks: "{{ virtualization_vmware_networks }}"
|
||||||
|
no_log: true
|
||||||
register: virtualization_vmware_create_result
|
register: virtualization_vmware_create_result
|
||||||
|
|
||||||
- name: Set VM created fact when VM was powered on during creation
|
- name: Set VM created fact when VM was powered on during creation
|
||||||
@@ -77,28 +95,19 @@
|
|||||||
|
|
||||||
- name: Ensure vTPM2 is enabled when required
|
- name: Ensure vTPM2 is enabled when required
|
||||||
when: virtualization_tpm2_enabled | bool
|
when: virtualization_tpm2_enabled | bool
|
||||||
delegate_to: localhost
|
|
||||||
community.vmware.vmware_guest_tpm:
|
community.vmware.vmware_guest_tpm:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
|
||||||
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
folder: "{{ system_cfg.path if system_cfg.path | string | length > 0 else omit }}"
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: present
|
state: present
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
# vmware.vmware: modern collection for power operations
|
||||||
- name: Start VM in vCenter
|
- name: Start VM in vCenter
|
||||||
when: virtualization_tpm2_enabled | bool
|
when: virtualization_tpm2_enabled | bool
|
||||||
delegate_to: localhost
|
|
||||||
vmware.vmware.vm_powerstate:
|
vmware.vmware.vm_powerstate:
|
||||||
hostname: "{{ hypervisor_cfg.url }}"
|
|
||||||
username: "{{ hypervisor_cfg.username }}"
|
|
||||||
password: "{{ hypervisor_cfg.password }}"
|
|
||||||
validate_certs: "{{ hypervisor_cfg.certs | bool }}"
|
|
||||||
datacenter: "{{ hypervisor_cfg.datacenter }}"
|
|
||||||
name: "{{ hostname }}"
|
name: "{{ hostname }}"
|
||||||
state: powered-on
|
state: powered-on
|
||||||
|
no_log: true
|
||||||
register: virtualization_vmware_start_result
|
register: virtualization_vmware_start_result
|
||||||
|
|
||||||
- name: Set VM created fact when VM was started separately (TPM2 case)
|
- name: Set VM created fact when VM was started separately (TPM2 case)
|
||||||
|
|||||||
@@ -49,6 +49,16 @@
|
|||||||
dest: /tmp/xen-{{ hostname }}.cfg
|
dest: /tmp/xen-{{ hostname }}.cfg
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Check if Xen VM already exists
|
||||||
|
delegate_to: localhost
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- xl
|
||||||
|
- list
|
||||||
|
register: virtualization_xen_pre_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
- name: Create Xen VM
|
- name: Create Xen VM
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
@@ -58,8 +68,11 @@
|
|||||||
- /tmp/xen-{{ hostname }}.cfg
|
- /tmp/xen-{{ hostname }}.cfg
|
||||||
register: virtualization_xen_create_result
|
register: virtualization_xen_create_result
|
||||||
changed_when: virtualization_xen_create_result.rc == 0
|
changed_when: virtualization_xen_create_result.rc == 0
|
||||||
|
when: >-
|
||||||
|
not (virtualization_xen_pre_check.stdout | default('')
|
||||||
|
is search('(?m)^' ~ (hostname | ansible.builtin.regex_escape) ~ '\\s+\\d+\\s'))
|
||||||
|
|
||||||
- name: Ensure VM is running
|
- name: Verify VM is running
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv:
|
argv:
|
||||||
@@ -67,13 +80,10 @@
|
|||||||
- list
|
- list
|
||||||
register: virtualization_xen_list_result
|
register: virtualization_xen_list_result
|
||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: >-
|
||||||
|
not (virtualization_xen_list_result.stdout | default('')
|
||||||
|
is search('(?m)^' ~ (hostname | ansible.builtin.regex_escape) ~ '\\s+\\d+\\s'))
|
||||||
|
|
||||||
- name: Set VM created fact
|
- name: Set VM created fact
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
virtualization_vm_created_in_run: true
|
virtualization_vm_created_in_run: "{{ (virtualization_xen_create_result.rc | default(1)) == 0 }}"
|
||||||
when:
|
|
||||||
- virtualization_xen_list_result is defined
|
|
||||||
- >-
|
|
||||||
virtualization_xen_list_result.stdout | default('')
|
|
||||||
is search('(?m)^' ~ (hostname | ansible.builtin.regex_escape) ~ '\\s+\\d+\\s')
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ network:
|
|||||||
addresses:
|
addresses:
|
||||||
- "{{ iface.ip }}/{{ iface.prefix }}"
|
- "{{ iface.ip }}/{{ iface.prefix }}"
|
||||||
{% if iface.gateway | default('') | string | length %}
|
{% if iface.gateway | default('') | string | length %}
|
||||||
gateway4: "{{ iface.gateway }}"
|
routes:
|
||||||
|
- to: default
|
||||||
|
via: "{{ iface.gateway }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
dhcp4: true
|
dhcp4: true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#cloud-config
|
#cloud-config
|
||||||
hostname: "archiso"
|
hostname: "{{ hostname }}"
|
||||||
ssh_pwauth: true
|
ssh_pwauth: true
|
||||||
package_update: false
|
package_update: false
|
||||||
package_upgrade: false
|
package_upgrade: false
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<domain type='kvm'>
|
<domain type='kvm'>
|
||||||
<name>{{ hostname }}</name>
|
<name>{{ hostname }}</name>
|
||||||
<memory>{{ system_cfg.memory | int * 1024 }}</memory>
|
<memory unit='KiB'>{{ system_cfg.memory | int * 1024 }}</memory>
|
||||||
{% if system_cfg.balloon is defined and system_cfg.balloon | int > 0 %}<currentMemory>{{ system_cfg.balloon | int * 1024 }}</currentMemory>{% endif %}
|
{% if system_cfg.balloon is defined and system_cfg.balloon | int > 0 %}<currentMemory unit='KiB'>{{ system_cfg.balloon | int * 1024 }}</currentMemory>{% endif %}
|
||||||
<vcpu placement='static'>{{ system_cfg.cpus }}</vcpu>
|
<vcpu placement='static'>{{ system_cfg.cpus }}</vcpu>
|
||||||
<os>
|
<os>
|
||||||
<type arch='x86_64' machine="pc-q35-8.0">hvm</type>
|
<type arch='x86_64' machine="{{ virtualization_libvirt_machine_type }}">hvm</type>
|
||||||
<bootmenu enable='no'/>
|
<bootmenu enable='no'/>
|
||||||
<boot dev='hd'/>
|
<boot dev='hd'/>
|
||||||
<boot dev='cdrom'/>
|
<boot dev='cdrom'/>
|
||||||
<loader readonly="yes" type="pflash">/usr/share/edk2/x64/OVMF_CODE.secboot.4m.fd</loader>
|
<loader readonly="yes" type="pflash">{{ virtualization_libvirt_ovmf_code }}</loader>
|
||||||
<nvram template="/usr/share/edk2/x64/OVMF_VARS.4m.fd"/>
|
<nvram template="{{ virtualization_libvirt_ovmf_vars }}"/>
|
||||||
</os>
|
</os>
|
||||||
<features>
|
<features>
|
||||||
<acpi/>
|
<acpi/>
|
||||||
@@ -33,17 +33,20 @@
|
|||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ boot_iso }}"/>
|
<source file="{{ boot_iso }}"/>
|
||||||
<target dev="sda" bus="sata"/>
|
<target dev="sda" bus="sata"/>
|
||||||
|
<readonly/>
|
||||||
</disk>
|
</disk>
|
||||||
<disk type="file" device="cdrom">
|
<disk type="file" device="cdrom">
|
||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ virtualization_libvirt_cloudinit_path }}"/>
|
<source file="{{ virtualization_libvirt_cloudinit_path }}"/>
|
||||||
<target dev="sdb" bus="sata"/>
|
<target dev="sdb" bus="sata"/>
|
||||||
|
<readonly/>
|
||||||
</disk>
|
</disk>
|
||||||
{% if rhel_iso is defined and rhel_iso | length > 0 %}
|
{% if rhel_iso is defined and rhel_iso | length > 0 %}
|
||||||
<disk type="file" device="cdrom">
|
<disk type="file" device="cdrom">
|
||||||
<driver name="qemu" type="raw"/>
|
<driver name="qemu" type="raw"/>
|
||||||
<source file="{{ rhel_iso }}"/>
|
<source file="{{ rhel_iso }}"/>
|
||||||
<target dev="sdc" bus="sata"/>
|
<target dev="sdc" bus="sata"/>
|
||||||
|
<readonly/>
|
||||||
</disk>
|
</disk>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for iface in system_cfg.network.interfaces %}
|
{% for iface in system_cfg.network.interfaces %}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ disk = [
|
|||||||
]
|
]
|
||||||
vif = [
|
vif = [
|
||||||
{%- for iface in system_cfg.network.interfaces -%}
|
{%- for iface in system_cfg.network.interfaces -%}
|
||||||
'bridge={{ iface.bridge }},model=e1000'{% if not loop.last %}, {% endif %}
|
'bridge={{ iface.bridge }},model=virtio'{% if not loop.last %}, {% endif %}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
]
|
]
|
||||||
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
boot = "{{ 'dc' if xen_installer_media_enabled | bool else 'c' }}"
|
||||||
Reference in New Issue
Block a user