Co-developed-by: Gemini AI <renich+gemini@woralelandia.com> Signed-off-by: Rénich Bon Ćirić <renich@woralelandia.com>
354 lines
11 KiB
YAML
354 lines
11 KiB
YAML
- name: "Manage local known_hosts for VM nodes"
|
|
when: inventory_hostname == 'localhost'
|
|
tags: [common, ssh_keys]
|
|
block:
|
|
- name: "Compile list of local VM hosts"
|
|
ansible.builtin.set_fact:
|
|
local_vm_hosts: "{{ local_vm_hosts_raw | from_json }}"
|
|
vars:
|
|
local_vm_hosts_raw: >-
|
|
{% set hosts = [] %}
|
|
{% for host in groups['all'] %}
|
|
{% if host != 'localhost' and host != '127.0.0.1' and hostvars[host].ansible_ssh_common_args is not defined %}
|
|
{% set _ = hosts.append(host) %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ hosts | to_json }}
|
|
|
|
- name: "Resolve IP addresses of inventory hosts"
|
|
ansible.builtin.command: "getent hosts {{ item }}"
|
|
register: resolved_ips
|
|
loop: "{{ local_vm_hosts }}"
|
|
failed_when: false
|
|
changed_when: false
|
|
|
|
- name: "Compile list of hosts and IPs to process"
|
|
ansible.builtin.set_fact:
|
|
hosts_and_ips: "{{ hosts_and_ips_raw | from_json }}"
|
|
vars:
|
|
hosts_and_ips_raw: >-
|
|
{% set list = [] %}
|
|
{% for host in local_vm_hosts %}
|
|
{% set _ = list.append(host) %}
|
|
{% if hostvars[host].ansible_host is defined %}
|
|
{% set _ = list.append(hostvars[host].ansible_host) %}
|
|
{% endif %}
|
|
{% set ip_res = resolved_ips.results | selectattr('item', 'equalto', host) | first %}
|
|
{% if ip_res.rc == 0 and (ip_res.stdout.split() | length) > 0 %}
|
|
{% set ip = ip_res.stdout.split() | first %}
|
|
{% set _ = list.append(ip) %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ list | unique | to_json }}
|
|
|
|
- name: "Remove existing host keys from local known_hosts"
|
|
ansible.builtin.known_hosts:
|
|
path: "~/.ssh/known_hosts"
|
|
name: "{{ item }}"
|
|
state: absent
|
|
loop: "{{ hosts_and_ips }}"
|
|
failed_when: false
|
|
|
|
- name: "Wait for SSH port 22 to be open on VM nodes"
|
|
ansible.builtin.wait_for:
|
|
host: "{{ hostvars[item].ansible_host | default(item) }}"
|
|
port: 22
|
|
timeout: 300
|
|
loop: "{{ local_vm_hosts }}"
|
|
|
|
- name: "Scan SSH host keys"
|
|
ansible.builtin.command: "ssh-keyscan -t rsa,ecdsa,ed25519 -H {{ item }}"
|
|
register: keyscan_results
|
|
loop: "{{ hosts_and_ips }}"
|
|
failed_when: false
|
|
changed_when: false
|
|
|
|
- name: "Add scanned host keys to local known_hosts"
|
|
ansible.builtin.known_hosts:
|
|
path: "~/.ssh/known_hosts"
|
|
name: "{{ item.0.item }}"
|
|
key: "{{ item.1 }}"
|
|
state: present
|
|
when: item.1 is not match('^#')
|
|
loop: "{{ keyscan_results.results | subelements('stdout_lines', skip_missing=True) }}"
|
|
|
|
- name: "Setup: Koji PKI Directory Structure"
|
|
tags: setup
|
|
block:
|
|
- name: "Create Koji PKI directory"
|
|
ansible.builtin.file:
|
|
path: "{{ koji_pki_dir }}"
|
|
state: directory
|
|
mode: "{{ koji_pki_dir_mode }}"
|
|
|
|
- name: "Create certificate database files"
|
|
ansible.builtin.copy:
|
|
content: "{{ item.content }}"
|
|
dest: "{{ koji_pki_dir }}/{{ item.name }}"
|
|
mode: "{{ koji_pki_file_mode }}"
|
|
loop:
|
|
- { name: "index.txt", content: "" }
|
|
- { name: "serial", content: "01\n" }
|
|
|
|
- name: "Generate: Koji Certificate Authority (CA)"
|
|
tags: ca
|
|
when: inventory_hostname == 'localhost'
|
|
block:
|
|
- name: "Generate CA private key"
|
|
community.crypto.openssl_privatekey:
|
|
path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.key"
|
|
size: "{{ koji_cert_key_size }}"
|
|
mode: "{{ koji_pki_private_key_mode }}"
|
|
|
|
- name: "Generate CA CSR"
|
|
community.crypto.openssl_csr:
|
|
path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.csr"
|
|
privatekey_path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.key"
|
|
common_name: "Koji Certificate Authority"
|
|
organization_name: "{{ koji_cert_organization }}"
|
|
state_or_province_name: "{{ koji_cert_state }}"
|
|
locality_name: "{{ koji_cert_locality }}"
|
|
country_name: "{{ koji_cert_country }}"
|
|
basic_constraints:
|
|
- "CA:TRUE"
|
|
key_usage:
|
|
- keyCertSign
|
|
- cRLSign
|
|
|
|
- name: "Generate self-signed CA certificate"
|
|
community.crypto.x509_certificate:
|
|
path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.crt"
|
|
csr_path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.csr"
|
|
privatekey_path: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.key"
|
|
provider: selfsigned
|
|
selfsigned_not_after: "+{{ koji_cert_expiration_days }}d"
|
|
|
|
- name: "Construct list of certificates to generate"
|
|
ansible.builtin.set_fact:
|
|
koji_certs_to_generate: "{{ koji_certs_to_generate_raw | from_json }}"
|
|
vars:
|
|
koji_certs_to_generate_raw: >-
|
|
{% set certs = [] %}
|
|
{% for cert in common_koji_certs_to_generate %}
|
|
{% if cert.name != 'kojid' %}
|
|
{% set _ = certs.append(cert) %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% for host in groups['kojibuilders'] | default([]) %}
|
|
{% set host_short = host.split('.')[0] %}
|
|
{% set _ = certs.append({'name': 'kojid-' ~ host_short, 'cn': host, 'is_user': false}) %}
|
|
{% endfor %}
|
|
{{ certs | to_json }}
|
|
when: inventory_hostname == 'localhost'
|
|
tags: components
|
|
|
|
- name: "Generate: Component and User Certificates"
|
|
ansible.builtin.include_tasks: generate_single_cert.yaml
|
|
loop: "{{ koji_certs_to_generate }}"
|
|
loop_control:
|
|
loop_var: cert
|
|
when: inventory_hostname == 'localhost'
|
|
tags: components
|
|
|
|
- name: Create GPG key batch file
|
|
when: inventory_hostname == 'localhost'
|
|
tags: gpg
|
|
block:
|
|
- name: Create temporary GPG batch file
|
|
ansible.builtin.tempfile:
|
|
state: file
|
|
suffix: .gpg
|
|
register: gpg_batch_file
|
|
|
|
- name: Populate GPG batch file
|
|
ansible.builtin.copy:
|
|
content: |
|
|
%echo Generating a basic OpenPGP key
|
|
Key-Type: RSA
|
|
Key-Length: 4096
|
|
Subkey-Type: RSA
|
|
Subkey-Length: 4096
|
|
Name-Real: Koji Signing Key
|
|
Name-Email: koji@{{ koji_hub_fqdn }}
|
|
Expire-Date: 0
|
|
Passphrase: {{ gpg_key_passphrase }}
|
|
%commit
|
|
%echo done
|
|
dest: "{{ gpg_batch_file.path }}"
|
|
mode: "0600"
|
|
|
|
- name: Generate GPG key
|
|
ansible.builtin.command:
|
|
cmd: "gpg --batch --gen-key {{ gpg_batch_file.path }}"
|
|
args:
|
|
creates: "{{ koji_pki_dir }}/gpg_key.pub"
|
|
|
|
- name: Export GPG public key
|
|
ansible.builtin.shell:
|
|
cmd: "gpg --armor --export 'Koji Signing Key' > {{ koji_pki_dir }}/gpg_key.pub"
|
|
changed_when: false
|
|
|
|
always:
|
|
- name: Remove temporary GPG batch file
|
|
ansible.builtin.file:
|
|
path: "{{ gpg_batch_file.path }}"
|
|
state: absent
|
|
when: gpg_batch_file.path is defined
|
|
|
|
- name: Set hostname
|
|
ansible.builtin.hostname:
|
|
name: "{{ inventory_hostname }}"
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Enable persistent systemd journal
|
|
ansible.builtin.file:
|
|
path: /var/log/journal
|
|
state: directory
|
|
mode: "0755"
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Flush systemd journal to disk
|
|
ansible.builtin.command: journalctl --flush
|
|
changed_when: false
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: "Manage known_hosts"
|
|
tags: ssh
|
|
block:
|
|
- name: "Remove existing host keys"
|
|
ansible.builtin.known_hosts:
|
|
path: "~/.ssh/known_hosts"
|
|
name: "{{ item }}"
|
|
state: absent
|
|
loop: "{{ hosts_raw | from_json }}"
|
|
vars:
|
|
hosts_raw: >-
|
|
{% set hosts = [] %}
|
|
{% for host in groups['all'] %}
|
|
{% if host != 'localhost' and host != '127.0.0.1' and hostvars[host].ansible_ssh_common_args is not defined %}
|
|
{% set _ = hosts.append(host) %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ hosts | to_json }}
|
|
|
|
- name: Scan SSH keys
|
|
ansible.builtin.command: ssh-keyscan -t rsa,ecdsa,ed25519 {{ hostvars[item].ansible_host | default(item) }}
|
|
loop: "{{ hosts_raw | from_json }}"
|
|
vars:
|
|
hosts_raw: >-
|
|
{% set hosts = [] %}
|
|
{% for host in groups['all'] %}
|
|
{% if host != 'localhost' and host != '127.0.0.1' and hostvars[host].ansible_ssh_common_args is not defined %}
|
|
{% set _ = hosts.append(host) %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ hosts | to_json }}
|
|
register: scanned_keys
|
|
delegate_to: localhost
|
|
run_once: true
|
|
changed_when: false
|
|
until: scanned_keys.rc == 0
|
|
retries: 3
|
|
delay: 2
|
|
|
|
- name: "Add current host keys"
|
|
ansible.builtin.known_hosts:
|
|
path: "~/.ssh/known_hosts"
|
|
name: "{{ item.item }}"
|
|
key: "{{ item.stdout | regex_replace('(?m)^[^#\\s]+', item.item) }}"
|
|
state: present
|
|
loop: "{{ scanned_keys.results }}"
|
|
|
|
- name: Add all Koji node hosts to /etc/hosts
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/hosts
|
|
line: >-
|
|
{{
|
|
hostvars[item].wg_ip
|
|
if (ansible_host is defined and ansible_host.startswith('10.10.'))
|
|
else (hostvars[item].ansible_default_ipv4.address | default(hostvars[item].wg_ip))
|
|
}} {{ item }}
|
|
regexp: ".*{{ item }}"
|
|
state: present
|
|
loop: "{{ groups['all'] }}"
|
|
when:
|
|
- inventory_hostname != 'localhost'
|
|
- item != 'localhost'
|
|
- (hostvars[item].wg_ip is defined) or (hostvars[item].ansible_default_ipv4.address is defined)
|
|
|
|
- name: Ensure public nameservers are configured in /etc/resolv.conf
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/resolv.conf
|
|
line: "nameserver {{ item }}"
|
|
insertbefore: BOF
|
|
loop:
|
|
- 8.8.8.8
|
|
- 1.1.1.1
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Configure DNF speed and mirrors
|
|
ansible.builtin.blockinfile:
|
|
path: /etc/dnf/dnf.conf
|
|
marker: "# {mark} ANSIBLE MANAGED DNF CONFIG"
|
|
block: |
|
|
max_parallel_downloads = 10
|
|
minrate=1M
|
|
timeout=10
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Deploy MxOS Forgejo Packages repository
|
|
ansible.builtin.yum_repository:
|
|
name: mxos-packages
|
|
description: MxOS Forgejo Packages
|
|
baseurl: https://git.mx-os.mx/api/packages/RPMs/rpm
|
|
enabled: true
|
|
gpgcheck: false
|
|
metadata_expire: "0"
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Install EPEL release repository
|
|
ansible.builtin.dnf:
|
|
name: epel-release
|
|
state: present
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Install system utility tools
|
|
ansible.builtin.dnf:
|
|
name:
|
|
- bash-completion
|
|
- htop
|
|
- iftop
|
|
- iotop
|
|
- rsync
|
|
- tmux
|
|
- vim-enhanced
|
|
state: present
|
|
when: inventory_hostname != 'localhost'
|
|
|
|
- name: Perform full package upgrade and distro-sync
|
|
ansible.builtin.dnf:
|
|
name: "*"
|
|
state: latest
|
|
register: dnf_upgrade_result
|
|
when:
|
|
- inventory_hostname != 'localhost'
|
|
- "'reposerver' not in group_names"
|
|
|
|
|
|
- name: Reboot if packages were upgraded (non-gateway hosts)
|
|
ansible.builtin.reboot:
|
|
reboot_timeout: 600
|
|
when:
|
|
- inventory_hostname != 'localhost'
|
|
- dnf_upgrade_result.changed
|
|
- "'gcp_gateway' not in group_names"
|
|
|
|
- name: Reboot gateway if packages were upgraded
|
|
ansible.builtin.reboot:
|
|
reboot_timeout: 600
|
|
when:
|
|
- inventory_hostname != 'localhost'
|
|
- dnf_upgrade_result.changed
|
|
- "'gcp_gateway' in group_names"
|
|
|
|
|