From 499afc8c371c8160c26065e383d4a4f5b734a4d6 Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Sat, 20 Jun 2026 15:57:06 +0300 Subject: [PATCH 1/6] feat: #45 Missing logic --- molecule/default/group_vars/all.yml | 6 +++ .../roles/system/files/system-test-script | 3 ++ .../roles/system/tasks/debian/locales.yml | 36 +++++++++++++ .../default/roles/system/tasks/locales.yml | 19 +++++++ molecule/default/roles/system/tasks/main.yml | 3 ++ .../roles/system/tasks/redhat/locales.yml | 40 ++++++++++++++ .../default/roles/system/tasks/scripts.yml | 52 +++++++++++++++++++ roles/system/README.md | 41 ++++++++++----- roles/system/defaults/main.yml | 12 +++++ roles/system/tasks/debian/locales.yml | 29 +++++++++++ roles/system/tasks/locales.yml | 3 ++ roles/system/tasks/main.yml | 2 +- roles/system/tasks/redhat/locales.yml | 19 +++++++ roles/system/tasks/scripts.yml | 5 +- roles/system/tasks/services.yml | 12 +++++ roles/system/tasks/system.yml | 7 +++ 16 files changed, 273 insertions(+), 16 deletions(-) create mode 100644 molecule/default/roles/system/files/system-test-script create mode 100644 molecule/default/roles/system/tasks/debian/locales.yml create mode 100644 molecule/default/roles/system/tasks/locales.yml create mode 100644 molecule/default/roles/system/tasks/redhat/locales.yml create mode 100644 roles/system/tasks/debian/locales.yml create mode 100644 roles/system/tasks/locales.yml create mode 100644 roles/system/tasks/redhat/locales.yml create mode 100644 roles/system/tasks/services.yml diff --git a/molecule/default/group_vars/all.yml b/molecule/default/group_vars/all.yml index 48dc432..e6d92c9 100644 --- a/molecule/default/group_vars/all.yml +++ b/molecule/default/group_vars/all.yml @@ -1,6 +1,12 @@ # System system_os_family: "{{ ansible_os_family | lower }}" +system_locale_default: "en_US.UTF-8" +system_locales: + - "en_GB.UTF-8" +system_scripts: + - roles/system/files/system-test-script + system_motd_contacts: - name: "John Doe" position: "System Administrator" diff --git a/molecule/default/roles/system/files/system-test-script b/molecule/default/roles/system/files/system-test-script new file mode 100644 index 0000000..40ce0f6 --- /dev/null +++ b/molecule/default/roles/system/files/system-test-script @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "molecule custom script" diff --git a/molecule/default/roles/system/tasks/debian/locales.yml b/molecule/default/roles/system/tasks/debian/locales.yml new file mode 100644 index 0000000..d8c8cd5 --- /dev/null +++ b/molecule/default/roles/system/tasks/debian/locales.yml @@ -0,0 +1,36 @@ +--- +- name: Verify Debian locales package installation + block: + - name: Gather installed package facts + ansible.builtin.package_facts: + manager: auto + + - name: Assert locales package is installed + ansible.builtin.assert: + that: "'locales' in ansible_facts.packages" + fail_msg: "'locales' package is not installed" + success_msg: "'locales' package is installed" + +- name: Verify Debian default locale configuration + block: + - name: Stat Debian default locale file + ansible.builtin.stat: + path: /etc/default/locale + register: system_debian_default_locale_stat + + - name: Assert Debian default locale file exists + ansible.builtin.assert: + that: system_debian_default_locale_stat.stat.exists + fail_msg: "'/etc/default/locale' file does not exist" + success_msg: "'/etc/default/locale' file exists" + + - name: Slurp Debian default locale file content + ansible.builtin.slurp: + src: /etc/default/locale + register: system_debian_default_locale_file_content + + - name: Assert Debian default locale is configured + ansible.builtin.assert: + that: "'LANG=' ~ system_locale_default in (system_debian_default_locale_file_content.content | b64decode)" + fail_msg: "Default locale is not set to '{{ system_locale_default }}' in '/etc/default/locale'" + success_msg: "Default locale is set to '{{ system_locale_default }}' in '/etc/default/locale'" diff --git a/molecule/default/roles/system/tasks/locales.yml b/molecule/default/roles/system/tasks/locales.yml new file mode 100644 index 0000000..fdb81cf --- /dev/null +++ b/molecule/default/roles/system/tasks/locales.yml @@ -0,0 +1,19 @@ +--- +- name: Include distribution-specific locale verification tasks + ansible.builtin.include_tasks: "{{ system_os_family }}/locales.yml" + +- name: Verify configured locales are generated + block: + - name: Collect generated locales + ansible.builtin.command: locale -a + register: system_generated_locales + changed_when: false + + - name: Assert configured locales are generated + ansible.builtin.assert: + that: "(locale | lower | regex_replace('\\.utf-8$', '.utf8')) in (system_generated_locales.stdout_lines | map('lower') | list)" + fail_msg: "Locale '{{ locale }}' is not generated" + success_msg: "Locale '{{ locale }}' is generated" + loop: "{{ (system_locales + [system_locale_default]) | unique }}" + loop_control: + loop_var: locale diff --git a/molecule/default/roles/system/tasks/main.yml b/molecule/default/roles/system/tasks/main.yml index 621888c..9353807 100644 --- a/molecule/default/roles/system/tasks/main.yml +++ b/molecule/default/roles/system/tasks/main.yml @@ -2,6 +2,9 @@ - name: Include update verifications ansible.builtin.include_tasks: update.yml +- name: Include locales verifications + ansible.builtin.include_tasks: locales.yml + - name: Include motd verifications ansible.builtin.include_tasks: motd.yml diff --git a/molecule/default/roles/system/tasks/redhat/locales.yml b/molecule/default/roles/system/tasks/redhat/locales.yml new file mode 100644 index 0000000..51b02cc --- /dev/null +++ b/molecule/default/roles/system/tasks/redhat/locales.yml @@ -0,0 +1,40 @@ +--- +- name: Verify RedHat locale packages installation + block: + - name: Gather installed package facts + ansible.builtin.package_facts: + manager: auto + + - name: Assert locale packages are installed + ansible.builtin.assert: + that: "package in ansible_facts.packages" + fail_msg: "'{{ package }}' package is not installed" + success_msg: "'{{ package }}' package is installed" + loop: "{{ (system_locales + [system_locale_default]) | unique | \ + map('regex_replace', '^([^_]+)_.*$', 'glibc-langpack-\\1') | unique | list }}" + loop_control: + loop_var: package + +- name: Verify RedHat default locale configuration + block: + - name: Stat RedHat locale configuration file + ansible.builtin.stat: + path: /etc/locale.conf + register: system_redhat_locale_file_stat + + - name: Assert RedHat locale configuration file exists + ansible.builtin.assert: + that: system_redhat_locale_file_stat.stat.exists + fail_msg: "'/etc/locale.conf' file does not exist" + success_msg: "'/etc/locale.conf' file exists" + + - name: Slurp RedHat locale configuration file content + ansible.builtin.slurp: + src: /etc/locale.conf + register: system_redhat_locale_file_content + + - name: Assert RedHat default locale is configured + ansible.builtin.assert: + that: "'LANG=' ~ system_locale_default in (system_redhat_locale_file_content.content | b64decode)" + fail_msg: "Default locale is not set to '{{ system_locale_default }}' in '/etc/locale.conf'" + success_msg: "Default locale is set to '{{ system_locale_default }}' in '/etc/locale.conf'" diff --git a/molecule/default/roles/system/tasks/scripts.yml b/molecule/default/roles/system/tasks/scripts.yml index bd0e6db..4ea917a 100644 --- a/molecule/default/roles/system/tasks/scripts.yml +++ b/molecule/default/roles/system/tasks/scripts.yml @@ -44,3 +44,55 @@ that: "'\\r\\n' not in (system_thermal_script_content.content | b64decode)" fail_msg: "Thermal script has Windows line endings (CRLF)" success_msg: "Thermal script has Unix line endings (LF)" + +- name: Verify custom script attributes + block: + - name: Stat custom script + ansible.builtin.stat: + path: /usr/local/bin/system-test-script + register: system_custom_script_stat + + - name: Assert custom script exists + ansible.builtin.assert: + that: system_custom_script_stat.stat.exists + fail_msg: "Custom script does not exist" + success_msg: "Custom script exists" + + - name: Assert custom script is a regular file + ansible.builtin.assert: + that: system_custom_script_stat.stat.isreg + fail_msg: "Custom script is not a regular file" + success_msg: "Custom script is a regular file" + + - name: Assert custom script mode + ansible.builtin.assert: + that: system_custom_script_stat.stat.mode == '0755' + fail_msg: "Custom script has incorrect mode: {{ system_custom_script_stat.stat.mode }}" + success_msg: "Custom script has correct mode" + + - name: Assert custom script ownership + ansible.builtin.assert: + that: + - system_custom_script_stat.stat.pw_name == 'root' + - system_custom_script_stat.stat.gr_name == 'root' + fail_msg: "Custom script has incorrect ownership: {{ system_custom_script_stat.stat.pw_name }}:{{ system_custom_script_stat.stat.gr_name }}" + success_msg: "Custom script has correct ownership" + +- name: Verify custom script content + block: + - name: Slurp custom script content + ansible.builtin.slurp: + src: /usr/local/bin/system-test-script + register: system_custom_script_content + + - name: Assert custom script has Unix line endings + ansible.builtin.assert: + that: "'\\r\\n' not in (system_custom_script_content.content | b64decode)" + fail_msg: "Custom script has Windows line endings (CRLF)" + success_msg: "Custom script has Unix line endings (LF)" + + - name: Assert custom script expected marker is present + ansible.builtin.assert: + that: "'molecule custom script' in (system_custom_script_content.content | b64decode)" + fail_msg: "Custom script content does not match expected marker" + success_msg: "Custom script content includes expected marker" diff --git a/roles/system/README.md b/roles/system/README.md index 06029a7..ac7e223 100644 --- a/roles/system/README.md +++ b/roles/system/README.md @@ -45,18 +45,23 @@ Configures essential system settings and performs server setup ## Variables -| Variable | Description | Default Value | -|---------------------------------|-------------------------------------------------|--------------------------| -| `system_reboot_timeout` | Timeout in seconds for system reboot operations | `300` | -| `system_update_only` | Only perform system updates, skip other tasks | `false` | -| `system_update_type` | Type of system updates: `safe` or `full` | `safe` | -| `system_update_autoremove` | Remove unused packages after updates | `true` | -| `system_update_autoclean` | Clean package cache after updates | `true` | -| `system_hostname` | Hostname to set on target servers | `{{ ansible_hostname }}` | -| `system_timezone` | Timezone to set on target servers | `UTC` | -| `system_vm_max_map_count` | Virtual memory max map count for applications | `262144` | -| `system_motd_src` | MOTD banner template source path | `motd` | -| `system_motd_contacts` | Array of contact information for MOTD banner | `[]` | +| Variable | Description | Default Value | +|---------------------------------|-------------------------------------------------|----------------------------------------------------------| +| `system_reboot_timeout` | Timeout in seconds for system reboot operations | `300` | +| `system_update_only` | Only perform system updates, skip other tasks | `false` | +| `system_update_type` | Type of system updates: `safe` or `full` | `safe` | +| `system_update_autoremove` | Remove unused packages after updates | `true` | +| `system_update_autoclean` | Clean package cache after updates | `true` | +| `system_hostname` | Hostname to set on target servers | `{{ ansible_hostname }}` | +| `system_timezone` | Timezone to set on target servers | `UTC` | +| `system_locales_default` | Default system locale | `en_US.UTF-8` | +| `system_locales` | List of system locales | `[]` | +| `system_sysctl` | System kernel parameters | `{"vm.max_map_count": 262144, "fs.aio-max-nr": 1048576}` | +| `system_motd_src` | MOTD banner template source path | `motd` | +| `system_motd_contacts` | Array of contact information for MOTD banner | `[]` | +| `system_scripts_default` | Default scripts deployed to `/usr/local/bin` | `['thermal']` | +| `system_scripts` | Scripts deployed to `/usr/local/bin` | `[]` | +| `system_services` | Service management (see [services](#services)) | `[]` | ### MOTD Contact Configuration @@ -70,6 +75,18 @@ system_motd_contacts: mobile: "+1-555-0123" # Optional ``` +### Services + +The `system_services` variable accepts an array of services with the following structure: + +```yaml +system_services: + - name: getty@tty1 + enabled: false + state: stopped + masked: true +``` + ## Facts | Fact | Description | Example Values | diff --git a/roles/system/defaults/main.yml b/roles/system/defaults/main.yml index cdf81e3..3a4a68b 100644 --- a/roles/system/defaults/main.yml +++ b/roles/system/defaults/main.yml @@ -14,6 +14,10 @@ system_hostname: "{{ ansible_hostname }}" # Timezone configuration system_timezone: "UTC" +# Locales configuration +system_locale_default: "en_US.UTF-8" +system_locales: [] + # System kernel parameters system_sysctl: vm.max_map_count: 262144 @@ -22,3 +26,11 @@ system_sysctl: # MOTD settings system_motd_src: "motd" system_motd_contacts: [] + +# Scripts +system_scripts_default: + - thermal +system_scripts: [] + +# Services +system_services: [] diff --git a/roles/system/tasks/debian/locales.yml b/roles/system/tasks/debian/locales.yml new file mode 100644 index 0000000..906069a --- /dev/null +++ b/roles/system/tasks/debian/locales.yml @@ -0,0 +1,29 @@ +--- +- name: Install locales package + ansible.builtin.package: + name: locales + state: present + become: true + retries: 3 + delay: 10 + +- name: Configure system locales + community.general.locale_gen: + name: "{{ locale }}" + state: present + become: true + loop: "{{ (system_locales + [system_locale_default]) | unique }}" + loop_control: + loop_var: locale + +- name: Read default locale file + ansible.builtin.slurp: + src: /etc/default/locale + register: system_locale_content + +- name: Set default locale + ansible.builtin.command: + cmd: update-locale LANG={{ system_locale_default }} + when: ('LANG=' ~ system_locale_default) not in (system_locale_content.content | b64decode) + changed_when: true + become: true diff --git a/roles/system/tasks/locales.yml b/roles/system/tasks/locales.yml new file mode 100644 index 0000000..b10c066 --- /dev/null +++ b/roles/system/tasks/locales.yml @@ -0,0 +1,3 @@ +--- +- name: Include distribution-specific tasks + ansible.builtin.include_tasks: "{{ system_os_family }}/locales.yml" diff --git a/roles/system/tasks/main.yml b/roles/system/tasks/main.yml index ecb623c..cc9c512 100644 --- a/roles/system/tasks/main.yml +++ b/roles/system/tasks/main.yml @@ -1,6 +1,6 @@ - name: Set system containerized fact ansible.builtin.set_fact: - system_containerized: "{{ ansible_virtualization_type in ['docker', 'podman', 'lxc', 'systemd-nspawn'] }}" + system_containerized: "{{ ansible_virtualization_type in ['docker', 'container', 'podman', 'lxc', 'systemd-nspawn'] }}" - name: Detect PID 1 command ansible.builtin.slurp: diff --git a/roles/system/tasks/redhat/locales.yml b/roles/system/tasks/redhat/locales.yml new file mode 100644 index 0000000..a0bb2d2 --- /dev/null +++ b/roles/system/tasks/redhat/locales.yml @@ -0,0 +1,19 @@ +--- +- name: Install locales + ansible.builtin.package: + name: "glibc-langpack-{{ locale.split('_')[0] }}" + state: present + become: true + loop: "{{ (system_locales + [system_locale_default]) | unique }}" + loop_control: + loop_var: locale + +- name: Set default locale + ansible.builtin.copy: + dest: /etc/locale.conf + content: | + LANG={{ system_locale_default }} + owner: root + group: root + mode: '0644' + become: true diff --git a/roles/system/tasks/scripts.yml b/roles/system/tasks/scripts.yml index d8e4bfa..483f07d 100644 --- a/roles/system/tasks/scripts.yml +++ b/roles/system/tasks/scripts.yml @@ -2,12 +2,11 @@ - name: Deploy scripts ansible.builtin.copy: src: "{{ script }}" - dest: "/usr/local/bin/{{ script }}" + dest: "/usr/local/bin/{{ script | basename }}" owner: root group: root mode: '0755' become: true - loop: - - thermal + loop: "{{ (system_scripts_default + system_scripts) | unique }}" loop_control: loop_var: script diff --git a/roles/system/tasks/services.yml b/roles/system/tasks/services.yml new file mode 100644 index 0000000..3ce251c --- /dev/null +++ b/roles/system/tasks/services.yml @@ -0,0 +1,12 @@ +--- +- name: Manage system services + ansible.builtin.systemd_service: + name: "{{ service.name }}" + state: "{{ service.state }}" + enabled: "{{ service.enabled | default(omit) }}" + masked: "{{ service.masked | default(omit) }}" + become: true + when: system_services | default([]) | length > 0 + loop: "{{ system_services }}" + loop_control: + loop_var: service diff --git a/roles/system/tasks/system.yml b/roles/system/tasks/system.yml index 16ce0bc..b27b737 100644 --- a/roles/system/tasks/system.yml +++ b/roles/system/tasks/system.yml @@ -7,6 +7,9 @@ ansible.builtin.include_tasks: time.yml when: not system_containerized +- name: Include system locales configuration tasks + ansible.builtin.include_tasks: locales.yml + - name: Include system kernel parameters tasks ansible.builtin.include_tasks: sysctl.yml when: not system_containerized @@ -16,3 +19,7 @@ - name: Include scripts tasks ansible.builtin.include_tasks: scripts.yml + +- name: Include services tasks + ansible.builtin.include_tasks: services.yml + when: not system_containerized From 86c0b85ab8e8e534ed7a59a3d01643cd8c00f108 Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Sat, 20 Jun 2026 16:33:56 +0300 Subject: [PATCH 2/6] feat: #45 Missing logic: j2 --- .github/workflows/build.yml | 8 ++++---- .github/workflows/publish.yml | 2 +- CHANGELOG.md | 10 ++++++++++ galaxy.yml | 2 +- roles/fs/tasks/mount.yml | 2 +- roles/fs/templates/{auto-remount => auto-remount.j2} | 0 roles/mail/tasks/debian/mail.yml | 6 +++--- roles/mail/tasks/redhat/mail.yml | 4 ++-- ..._rewrite_rules => 99_exim4-config_rewrite_rules.j2} | 0 .../debian/{passwd.client => passwd.client.j2} | 0 ...pdate-exim4.conf.conf => update-exim4.conf.conf.j2} | 0 roles/mail/templates/redhat/{main.cf => main.cf.j2} | 0 .../templates/redhat/{sasl_passwd => sasl_passwd.j2} | 0 roles/nut/tasks/cgi.yml | 6 +++--- roles/nut/tasks/nut.yml | 2 +- roles/nut/tasks/ups.yml | 2 +- roles/nut/tasks/ups_vars.yml | 2 +- roles/nut/tasks/upsd.yml | 6 +++--- roles/nut/tasks/upsmon.yml | 2 +- roles/nut/tasks/upssched.yml | 4 ++-- roles/nut/templates/{hosts.conf => hosts.conf.j2} | 0 roles/nut/templates/{nut.conf => nut.conf.j2} | 0 .../{ups-update-variable => ups-update-variable.j2} | 0 roles/nut/templates/{ups.conf => ups.conf.j2} | 0 roles/nut/templates/{upsd.conf => upsd.conf.j2} | 0 roles/nut/templates/{upsd.users => upsd.users.j2} | 0 roles/nut/templates/{upsmon.conf => upsmon.conf.j2} | 0 roles/nut/templates/{upssched-cmd => upssched-cmd.j2} | 0 .../nut/templates/{upssched.conf => upssched.conf.j2} | 0 roles/nut/templates/{upsset.conf => upsset.conf.j2} | 0 roles/packages/tasks/bash.yml | 2 +- .../templates/{.bash_aliases => .bash_aliases.j2} | 0 roles/samba/tasks/samba.yml | 2 +- roles/samba/templates/{smb.conf => smb.conf.j2} | 0 roles/system/README.md | 2 +- roles/system/defaults/main.yml | 2 +- roles/system/templates/{motd => motd.j2} | 0 37 files changed, 38 insertions(+), 28 deletions(-) rename roles/fs/templates/{auto-remount => auto-remount.j2} (100%) rename roles/mail/templates/debian/{99_exim4-config_rewrite_rules => 99_exim4-config_rewrite_rules.j2} (100%) rename roles/mail/templates/debian/{passwd.client => passwd.client.j2} (100%) rename roles/mail/templates/debian/{update-exim4.conf.conf => update-exim4.conf.conf.j2} (100%) rename roles/mail/templates/redhat/{main.cf => main.cf.j2} (100%) rename roles/mail/templates/redhat/{sasl_passwd => sasl_passwd.j2} (100%) rename roles/nut/templates/{hosts.conf => hosts.conf.j2} (100%) rename roles/nut/templates/{nut.conf => nut.conf.j2} (100%) rename roles/nut/templates/{ups-update-variable => ups-update-variable.j2} (100%) rename roles/nut/templates/{ups.conf => ups.conf.j2} (100%) rename roles/nut/templates/{upsd.conf => upsd.conf.j2} (100%) rename roles/nut/templates/{upsd.users => upsd.users.j2} (100%) rename roles/nut/templates/{upsmon.conf => upsmon.conf.j2} (100%) rename roles/nut/templates/{upssched-cmd => upssched-cmd.j2} (100%) rename roles/nut/templates/{upssched.conf => upssched.conf.j2} (100%) rename roles/nut/templates/{upsset.conf => upsset.conf.j2} (100%) rename roles/packages/templates/{.bash_aliases => .bash_aliases.j2} (100%) rename roles/samba/templates/{smb.conf => smb.conf.j2} (100%) rename roles/system/templates/{motd => motd.j2} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a4f3a39..49cf338 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Run ansible-lint - uses: ansible/ansible-lint@v25 + uses: ansible/ansible-lint@v26 with: requirements_file: requirements.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: path: "${{ github.repository }}" - name: Molecule @@ -52,6 +52,6 @@ jobs: needs: [lint, sanity, molecule] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Build collection uses: ansible-community/github-action-build-collection@main diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bca6fb0..bab248c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Build collection id: build-collection uses: ansible-community/github-action-build-collection@main diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fdf0da..1c09450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 2.8.0 + +[#45 Missing logic](https://github.com/Scalified/ansible-setup-collection/issues/45) + +* System: locales +* System: scripts +* System: services +* j2 extensions for template files +* Docker: QEMU emulation, tcp host + # 2.7.7 [#44 Skip Age tasks on empty age private key file](https://github.com/Scalified/ansible-setup-collection/issues/44) diff --git a/galaxy.yml b/galaxy.yml index 2aa9cd0..ba48bac 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,6 +1,6 @@ namespace: scalified name: setup -version: 2.7.7 +version: 2.8.0 readme: README.md authors: - Vladyslav Baidak diff --git a/roles/fs/tasks/mount.yml b/roles/fs/tasks/mount.yml index 71ce34c..1d6f693 100644 --- a/roles/fs/tasks/mount.yml +++ b/roles/fs/tasks/mount.yml @@ -1,7 +1,7 @@ --- - name: Deploy auto remount script ansible.builtin.template: - src: auto-remount + src: auto-remount.j2 dest: /usr/local/sbin/auto-remount mode: "0755" become: true diff --git a/roles/fs/templates/auto-remount b/roles/fs/templates/auto-remount.j2 similarity index 100% rename from roles/fs/templates/auto-remount rename to roles/fs/templates/auto-remount.j2 diff --git a/roles/mail/tasks/debian/mail.yml b/roles/mail/tasks/debian/mail.yml index 4c76027..bddac66 100644 --- a/roles/mail/tasks/debian/mail.yml +++ b/roles/mail/tasks/debian/mail.yml @@ -11,21 +11,21 @@ - name: Deploy exim4 passwd.client configuraiton ansible.builtin.template: - src: "{{ system_os_family }}/passwd.client" + src: "{{ system_os_family }}/passwd.client.j2" dest: /etc/exim4/passwd.client mode: "0640" become: true - name: Deploy exim4 rewrite rules ansible.builtin.template: - src: "{{ system_os_family }}/99_exim4-config_rewrite_rules" + src: "{{ system_os_family }}/99_exim4-config_rewrite_rules.j2" dest: /etc/exim4/conf.d/rewrite/99_exim4-config_rewrite_rules mode: "0644" become: true - name: Deploy exim4 update-exim4.conf.conf configuration ansible.builtin.template: - src: "{{ system_os_family }}/update-exim4.conf.conf" + src: "{{ system_os_family }}/update-exim4.conf.conf.j2" dest: /etc/exim4/update-exim4.conf.conf mode: "0644" become: true diff --git a/roles/mail/tasks/redhat/mail.yml b/roles/mail/tasks/redhat/mail.yml index 2b45248..25a2758 100644 --- a/roles/mail/tasks/redhat/mail.yml +++ b/roles/mail/tasks/redhat/mail.yml @@ -13,7 +13,7 @@ - name: Deploy postfix sasl_passwd configuration ansible.builtin.template: - src: "redhat/sasl_passwd" + src: "{{ system_os_family }}/sasl_passwd.j2" dest: /etc/postfix/sasl_passwd mode: "0600" become: true @@ -23,7 +23,7 @@ - name: Deploy postfix main.cf configuration ansible.builtin.template: - src: "redhat/main.cf" + src: "{{ system_os_family }}/main.cf.j2" dest: /etc/postfix/main.cf mode: "0644" become: true diff --git a/roles/mail/templates/debian/99_exim4-config_rewrite_rules b/roles/mail/templates/debian/99_exim4-config_rewrite_rules.j2 similarity index 100% rename from roles/mail/templates/debian/99_exim4-config_rewrite_rules rename to roles/mail/templates/debian/99_exim4-config_rewrite_rules.j2 diff --git a/roles/mail/templates/debian/passwd.client b/roles/mail/templates/debian/passwd.client.j2 similarity index 100% rename from roles/mail/templates/debian/passwd.client rename to roles/mail/templates/debian/passwd.client.j2 diff --git a/roles/mail/templates/debian/update-exim4.conf.conf b/roles/mail/templates/debian/update-exim4.conf.conf.j2 similarity index 100% rename from roles/mail/templates/debian/update-exim4.conf.conf rename to roles/mail/templates/debian/update-exim4.conf.conf.j2 diff --git a/roles/mail/templates/redhat/main.cf b/roles/mail/templates/redhat/main.cf.j2 similarity index 100% rename from roles/mail/templates/redhat/main.cf rename to roles/mail/templates/redhat/main.cf.j2 diff --git a/roles/mail/templates/redhat/sasl_passwd b/roles/mail/templates/redhat/sasl_passwd.j2 similarity index 100% rename from roles/mail/templates/redhat/sasl_passwd rename to roles/mail/templates/redhat/sasl_passwd.j2 diff --git a/roles/nut/tasks/cgi.yml b/roles/nut/tasks/cgi.yml index 0b9095f..3f2229e 100644 --- a/roles/nut/tasks/cgi.yml +++ b/roles/nut/tasks/cgi.yml @@ -11,13 +11,13 @@ - name: Deploy NUT CGI configurations ansible.builtin.template: src: "{{ file }}" - dest: "{{ nut_dir }}/{{ file }}" + dest: "{{ nut_dir }}/{{ file | splitext | first }}" owner: root group: nut mode: "0640" loop: - - hosts.conf - - upsset.conf + - hosts.conf.j2 + - upsset.conf.j2 loop_control: loop_var: file become: true diff --git a/roles/nut/tasks/nut.yml b/roles/nut/tasks/nut.yml index 537a362..fe0787c 100644 --- a/roles/nut/tasks/nut.yml +++ b/roles/nut/tasks/nut.yml @@ -13,7 +13,7 @@ - name: Deploy NUT configuration ansible.builtin.template: - src: nut.conf + src: nut.conf.j2 dest: "{{ nut_dir }}/nut.conf" owner: root group: nut diff --git a/roles/nut/tasks/ups.yml b/roles/nut/tasks/ups.yml index a01e72e..5e36e2b 100644 --- a/roles/nut/tasks/ups.yml +++ b/roles/nut/tasks/ups.yml @@ -12,7 +12,7 @@ - name: Deploy NUT UPS configuration ansible.builtin.template: - src: ups.conf + src: ups.conf.j2 dest: "{{ nut_dir }}/ups.conf" owner: root group: nut diff --git a/roles/nut/tasks/ups_vars.yml b/roles/nut/tasks/ups_vars.yml index 562b7df..4e8bd87 100644 --- a/roles/nut/tasks/ups_vars.yml +++ b/roles/nut/tasks/ups_vars.yml @@ -1,6 +1,6 @@ - name: Deploy UPS update vairable script ansible.builtin.template: - src: ups-update-variable + src: ups-update-variable.j2 dest: /usr/local/sbin/ups-update-variable owner: root group: nut diff --git a/roles/nut/tasks/upsd.yml b/roles/nut/tasks/upsd.yml index 84aa558..54f1d60 100644 --- a/roles/nut/tasks/upsd.yml +++ b/roles/nut/tasks/upsd.yml @@ -11,13 +11,13 @@ - name: Deploy NUT UPSD configuration ansible.builtin.template: src: "{{ file }}" - dest: "{{ nut_dir }}/{{ file }}" + dest: "{{ nut_dir }}/{{ file | splitext | first }}" owner: root group: nut mode: "640" loop: - - upsd.conf - - upsd.users + - upsd.conf.j2 + - upsd.users.j2 loop_control: loop_var: file become: true diff --git a/roles/nut/tasks/upsmon.yml b/roles/nut/tasks/upsmon.yml index a650c52..73954ea 100644 --- a/roles/nut/tasks/upsmon.yml +++ b/roles/nut/tasks/upsmon.yml @@ -8,7 +8,7 @@ - name: Deploy NUT UPSMON configuration ansible.builtin.template: - src: upsmon.conf + src: upsmon.conf.j2 dest: "{{ nut_dir }}/upsmon.conf" owner: root group: nut diff --git a/roles/nut/tasks/upssched.yml b/roles/nut/tasks/upssched.yml index 89d9f64..9474a62 100644 --- a/roles/nut/tasks/upssched.yml +++ b/roles/nut/tasks/upssched.yml @@ -10,7 +10,7 @@ - name: Deploy NUT UPSSCHED CMD script ansible.builtin.template: - src: upssched-cmd + src: upssched-cmd.j2 dest: /usr/local/sbin/upssched-cmd owner: root group: nut @@ -19,7 +19,7 @@ - name: Deploy NUT UPSSCHED configuration ansible.builtin.template: - src: upssched.conf + src: upssched.conf.j2 dest: "{{ nut_dir }}/upssched.conf" owner: root group: nut diff --git a/roles/nut/templates/hosts.conf b/roles/nut/templates/hosts.conf.j2 similarity index 100% rename from roles/nut/templates/hosts.conf rename to roles/nut/templates/hosts.conf.j2 diff --git a/roles/nut/templates/nut.conf b/roles/nut/templates/nut.conf.j2 similarity index 100% rename from roles/nut/templates/nut.conf rename to roles/nut/templates/nut.conf.j2 diff --git a/roles/nut/templates/ups-update-variable b/roles/nut/templates/ups-update-variable.j2 similarity index 100% rename from roles/nut/templates/ups-update-variable rename to roles/nut/templates/ups-update-variable.j2 diff --git a/roles/nut/templates/ups.conf b/roles/nut/templates/ups.conf.j2 similarity index 100% rename from roles/nut/templates/ups.conf rename to roles/nut/templates/ups.conf.j2 diff --git a/roles/nut/templates/upsd.conf b/roles/nut/templates/upsd.conf.j2 similarity index 100% rename from roles/nut/templates/upsd.conf rename to roles/nut/templates/upsd.conf.j2 diff --git a/roles/nut/templates/upsd.users b/roles/nut/templates/upsd.users.j2 similarity index 100% rename from roles/nut/templates/upsd.users rename to roles/nut/templates/upsd.users.j2 diff --git a/roles/nut/templates/upsmon.conf b/roles/nut/templates/upsmon.conf.j2 similarity index 100% rename from roles/nut/templates/upsmon.conf rename to roles/nut/templates/upsmon.conf.j2 diff --git a/roles/nut/templates/upssched-cmd b/roles/nut/templates/upssched-cmd.j2 similarity index 100% rename from roles/nut/templates/upssched-cmd rename to roles/nut/templates/upssched-cmd.j2 diff --git a/roles/nut/templates/upssched.conf b/roles/nut/templates/upssched.conf.j2 similarity index 100% rename from roles/nut/templates/upssched.conf rename to roles/nut/templates/upssched.conf.j2 diff --git a/roles/nut/templates/upsset.conf b/roles/nut/templates/upsset.conf.j2 similarity index 100% rename from roles/nut/templates/upsset.conf rename to roles/nut/templates/upsset.conf.j2 diff --git a/roles/packages/tasks/bash.yml b/roles/packages/tasks/bash.yml index 32e5155..70b4fed 100644 --- a/roles/packages/tasks/bash.yml +++ b/roles/packages/tasks/bash.yml @@ -34,7 +34,7 @@ - name: Deploy bash aliases ansible.builtin.template: - src: .bash_aliases + src: .bash_aliases.j2 dest: "{{ ansible_env.HOME }}/.bash_aliases" mode: "0644" diff --git a/roles/packages/templates/.bash_aliases b/roles/packages/templates/.bash_aliases.j2 similarity index 100% rename from roles/packages/templates/.bash_aliases rename to roles/packages/templates/.bash_aliases.j2 diff --git a/roles/samba/tasks/samba.yml b/roles/samba/tasks/samba.yml index 212402a..e3f2832 100644 --- a/roles/samba/tasks/samba.yml +++ b/roles/samba/tasks/samba.yml @@ -23,7 +23,7 @@ - name: Deploy Samba configuration ansible.builtin.template: - src: smb.conf + src: smb.conf.j2 dest: /etc/samba/smb.conf mode: '0644' become: true diff --git a/roles/samba/templates/smb.conf b/roles/samba/templates/smb.conf.j2 similarity index 100% rename from roles/samba/templates/smb.conf rename to roles/samba/templates/smb.conf.j2 diff --git a/roles/system/README.md b/roles/system/README.md index ac7e223..068624d 100644 --- a/roles/system/README.md +++ b/roles/system/README.md @@ -57,7 +57,7 @@ Configures essential system settings and performs server setup | `system_locales_default` | Default system locale | `en_US.UTF-8` | | `system_locales` | List of system locales | `[]` | | `system_sysctl` | System kernel parameters | `{"vm.max_map_count": 262144, "fs.aio-max-nr": 1048576}` | -| `system_motd_src` | MOTD banner template source path | `motd` | +| `system_motd_src` | MOTD banner template source path | `motd.j2` | | `system_motd_contacts` | Array of contact information for MOTD banner | `[]` | | `system_scripts_default` | Default scripts deployed to `/usr/local/bin` | `['thermal']` | | `system_scripts` | Scripts deployed to `/usr/local/bin` | `[]` | diff --git a/roles/system/defaults/main.yml b/roles/system/defaults/main.yml index 3a4a68b..12d3633 100644 --- a/roles/system/defaults/main.yml +++ b/roles/system/defaults/main.yml @@ -24,7 +24,7 @@ system_sysctl: fs.aio-max-nr: 1048576 # MOTD settings -system_motd_src: "motd" +system_motd_src: "motd.j2" system_motd_contacts: [] # Scripts diff --git a/roles/system/templates/motd b/roles/system/templates/motd.j2 similarity index 100% rename from roles/system/templates/motd rename to roles/system/templates/motd.j2 From c190acc94056633c892ad2004cf6cac7ed573462 Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Sun, 21 Jun 2026 12:33:04 +0300 Subject: [PATCH 3/6] feat: #45 Missing logic: ssl clients --- molecule/default/group_vars/all.yml | 6 + .../default/roles/packages/tasks/bash.yml | 17 +++ molecule/default/roles/ssl/defaults/main.yml | 14 ++ molecule/default/roles/ssl/tasks/client.yml | 87 +++++++++++ molecule/default/roles/ssl/tasks/main.yml | 6 + roles/docker/README.md | 15 +- roles/docker/defaults/main.yml | 1 + roles/docker/tasks/main.yml | 4 + roles/docker/tasks/qemu.yml | 19 +++ roles/packages/templates/.bash_aliases.j2 | 6 +- roles/ssl/README.md | 137 +++++++++++------- roles/ssl/defaults/main.yml | 24 +++ roles/ssl/tasks/clients.yml | 50 +++++++ roles/ssl/tasks/main.yml | 4 + roles/ssl/tasks/setup.yml | 20 +++ 15 files changed, 347 insertions(+), 63 deletions(-) create mode 100644 molecule/default/roles/ssl/tasks/client.yml create mode 100644 roles/docker/tasks/qemu.yml create mode 100644 roles/ssl/tasks/clients.yml diff --git a/molecule/default/group_vars/all.yml b/molecule/default/group_vars/all.yml index e6d92c9..7408939 100644 --- a/molecule/default/group_vars/all.yml +++ b/molecule/default/group_vars/all.yml @@ -83,6 +83,12 @@ ssl_certs_ca_name: "ca.crt" ssl_certs_server_name: "server.crt" ssl_bundle_generate: true ssl_bundle_name: "bundle.crt" +ssl_clients: + - name: docker + common_name: pc-workstation + extensions: + - clientAuth + - emailProtection # Docker docker_user: "{{ ansible_user_id }}" diff --git a/molecule/default/roles/packages/tasks/bash.yml b/molecule/default/roles/packages/tasks/bash.yml index 8b0e822..4341d85 100644 --- a/molecule/default/roles/packages/tasks/bash.yml +++ b/molecule/default/roles/packages/tasks/bash.yml @@ -115,6 +115,23 @@ loop_control: loop_var: alias + - name: Assert user-defined aliases separated by blank line + ansible.builtin.assert: + that: + - >- + "alias testls='ls -la'\n\n# Test alias for grep with line numbers" + in (packages_bash_aliases_content.content | b64decode) + fail_msg: "User-defined aliases are not separated by a blank line" + success_msg: "User-defined aliases are separated by a blank line" + + - name: Assert .bash_aliases file is valid bash syntax + ansible.builtin.command: + argv: + - bash + - -n + - "{{ ansible_env.HOME }}/.bash_aliases" + changed_when: false + - name: Verify .bashrc includes bash aliases block: - name: Stat .bashrc file exists diff --git a/molecule/default/roles/ssl/defaults/main.yml b/molecule/default/roles/ssl/defaults/main.yml index ba218b9..43caddb 100644 --- a/molecule/default/roles/ssl/defaults/main.yml +++ b/molecule/default/roles/ssl/defaults/main.yml @@ -13,6 +13,13 @@ ssl_keys_server_dir: >- 'redhat': '/etc/pki/tls/private' }[system_os_family] }} +ssl_keys_clients_dir: >- + {{ + { + 'debian': '/etc/ssl/private/clients', + 'redhat': '/etc/pki/tls/private/clients' + }[system_os_family] + }} ssl_certs_ca_dir: >- {{ { @@ -27,6 +34,13 @@ ssl_certs_server_dir: >- 'redhat': '/etc/pki/tls/certs' }[system_os_family] }} +ssl_certs_clients_dir: >- + {{ + { + 'debian': '/etc/ssl/certs/clients', + 'redhat': '/etc/pki/tls/certs/clients' + }[system_os_family] + }} ssl_certs_ca_truststore_bundle: >- {{ { diff --git a/molecule/default/roles/ssl/tasks/client.yml b/molecule/default/roles/ssl/tasks/client.yml new file mode 100644 index 0000000..d7f9584 --- /dev/null +++ b/molecule/default/roles/ssl/tasks/client.yml @@ -0,0 +1,87 @@ +--- +- name: Stat SSL client private key file + ansible.builtin.stat: + path: "{{ ssl_keys_clients_dir }}/{{ client.name }}.key" + register: ssl_client_key_stat + +- name: Assert SSL client private key file exists and has correct attributes + ansible.builtin.assert: + that: + - ssl_client_key_stat.stat.exists + - ssl_client_key_stat.stat.isreg + - ssl_client_key_stat.stat.mode == '0600' + - ssl_client_key_stat.stat.pw_name == 'root' + - ssl_client_key_stat.stat.gr_name == 'root' + fail_msg: >- + SSL client private key file has incorrect attributes for '{{ client.name }}': + exists={{ ssl_client_key_stat.stat.exists }}, + isreg={{ ssl_client_key_stat.stat.isreg }}, + mode={{ ssl_client_key_stat.stat.mode }}, + owner={{ ssl_client_key_stat.stat.pw_name }}:{{ ssl_client_key_stat.stat.gr_name }} + success_msg: "SSL client private key file has correct attributes for '{{ client.name }}'" + +- name: Stat SSL client certificate file + ansible.builtin.stat: + path: "{{ ssl_certs_clients_dir }}/{{ client.name }}.crt" + register: ssl_client_cert_stat + +- name: Assert SSL client certificate file exists and has correct attributes + ansible.builtin.assert: + that: + - ssl_client_cert_stat.stat.exists + - ssl_client_cert_stat.stat.isreg + - ssl_client_cert_stat.stat.mode == '0644' + - ssl_client_cert_stat.stat.pw_name == 'root' + - ssl_client_cert_stat.stat.gr_name == 'root' + fail_msg: >- + SSL client certificate file has incorrect attributes for '{{ client.name }}': + exists={{ ssl_client_cert_stat.stat.exists }}, + isreg={{ ssl_client_cert_stat.stat.isreg }}, + mode={{ ssl_client_cert_stat.stat.mode }}, + owner={{ ssl_client_cert_stat.stat.pw_name }}:{{ ssl_client_cert_stat.stat.gr_name }} + success_msg: "SSL client certificate file has correct attributes for '{{ client.name }}'" + +- name: Get SSL client certificate information + ansible.builtin.command: openssl x509 -in "{{ ssl_certs_clients_dir }}/{{ client.name }}.crt" -text -noout + register: ssl_client_cert_info + changed_when: false + +- name: Assert SSL client certificate content + ansible.builtin.assert: + that: + - "ssl_client_cert_info.stdout is search('CN *= *' ~ (client.common_name | regex_escape))" + - "'CA:FALSE' in ssl_client_cert_info.stdout" + - "'TLS Web Client Authentication' in ssl_client_cert_info.stdout" + fail_msg: >- + SSL client certificate validation failed for '{{ client.name }}' + success_msg: "SSL client certificate is valid for '{{ client.name }}'" + +- name: Assert SSL client certificate has optional extended key usages + ansible.builtin.assert: + that: "optional_usage_mapped in ssl_client_cert_info.stdout" + fail_msg: "SSL client certificate does not have optional extended key usage '{{ optional_usage }}'" + success_msg: "SSL client certificate has optional extended key usage '{{ optional_usage }}'" + loop: "{{ client.extensions | default([]) | difference(['clientAuth']) }}" + loop_control: + loop_var: optional_usage + vars: + optional_usage_mapped: "{{ optional_usage | regex_replace('^emailProtection$', 'E-mail Protection') }}" + when: client.extensions | default([]) | difference(['clientAuth']) | length > 0 + +- name: Check SSL client certificate validity + ansible.builtin.command: openssl x509 -in "{{ ssl_certs_clients_dir }}/{{ client.name }}.crt" -checkend 86400 + register: ssl_client_cert_validity + changed_when: false + failed_when: ssl_client_cert_validity.rc != 0 + +- name: Check SSL client certificate is signed by CA + ansible.builtin.command: >- + openssl verify -CAfile "{{ ssl_certs_ca_path }}" "{{ ssl_certs_clients_dir }}/{{ client.name }}.crt" + register: ssl_client_cert_chain + changed_when: false + +- name: Assert SSL client certificate chain is valid + ansible.builtin.assert: + that: "'OK' in ssl_client_cert_chain.stdout" + fail_msg: "SSL client certificate chain verification failed for '{{ client.name }}'" + success_msg: "SSL client certificate chain is valid for '{{ client.name }}'" diff --git a/molecule/default/roles/ssl/tasks/main.yml b/molecule/default/roles/ssl/tasks/main.yml index d0b53b5..8039fee 100644 --- a/molecule/default/roles/ssl/tasks/main.yml +++ b/molecule/default/roles/ssl/tasks/main.yml @@ -10,3 +10,9 @@ - name: Include SSL bundle verifications ansible.builtin.include_tasks: bundle.yml + +- name: Include SSL clients verifications + ansible.builtin.include_tasks: client.yml + loop: "{{ ssl_clients }}" + loop_control: + loop_var: client diff --git a/roles/docker/README.md b/roles/docker/README.md index 95dd540..7208d84 100644 --- a/roles/docker/README.md +++ b/roles/docker/README.md @@ -14,13 +14,14 @@ Installs and configures Docker ## Variables -| Variable | Description | Default Value | -|----------------------------|-----------------------------------------------|-------------------------| -| `docker_user` | User to add to the docker group | `{{ ansible_user_id }}` | -| `docker_system_prune_cron` | Docker system prune cron schedule | `17 1 * * *` | -| `docker_swarm_enabled` | Enable Docker Swarm mode | `false` | -| `docker_registries` | Docker registry authentication configuration | | -| `docker_networks` | Docker networks configuration | | +| Variable | Description | Default Value | +|----------------------------|------------------------------------------------|-------------------------| +| `docker_user` | User to add to the docker group | `{{ ansible_user_id }}` | +| `docker_system_prune_cron` | Docker system prune cron schedule | `17 1 * * *` | +| `docker_swarm_enabled` | Enable Docker Swarm mode | `false` | +| `docker_qemu_enabled` | Ename QEMU emulation for multi-platform builds | `false` | +| `docker_registries` | Docker registry authentication configuration | | +| `docker_networks` | Docker networks configuration | | ## Docker Networks diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml index 2bfdab7..707e31f 100644 --- a/roles/docker/defaults/main.yml +++ b/roles/docker/defaults/main.yml @@ -2,3 +2,4 @@ docker_user: "{{ ansible_user_id }}" docker_system_prune_cron: "17 1 * * *" docker_swarm_enabled: false +docker_qemu_enabled: false diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 6f320ef..6eef935 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -12,6 +12,10 @@ - name: Include prune tasks ansible.builtin.include_tasks: prune.yml +- name: Include qemu tasks + ansible.builtin.include_tasks: qemu.yml + when: docker_qemu_enabled + - name: Include swarm tasks ansible.builtin.include_tasks: swarm.yml diff --git a/roles/docker/tasks/qemu.yml b/roles/docker/tasks/qemu.yml new file mode 100644 index 0000000..5a6448a --- /dev/null +++ b/roles/docker/tasks/qemu.yml @@ -0,0 +1,19 @@ +--- +- name: Inspect Docker Buildx platforms + ansible.builtin.command: docker buildx inspect --bootstrap + register: docker_qemu_buildx_inspect + changed_when: false + become: true + +- name: Configure QEMU emulation + community.docker.docker_container: + name: docker-qemu + image: tonistiigi/binfmt + command: --install all + privileged: true + detach: false + cleanup: true + pull: missing + become: true + when: >- + ['linux/amd64', 'linux/arm64'] | reject('in', docker_qemu_buildx_inspect.stdout) | list | length > 0 diff --git a/roles/packages/templates/.bash_aliases.j2 b/roles/packages/templates/.bash_aliases.j2 index 58f10a9..51a0de0 100644 --- a/roles/packages/templates/.bash_aliases.j2 +++ b/roles/packages/templates/.bash_aliases.j2 @@ -13,10 +13,12 @@ alias hg='history|grep' {% if packages_bash_aliases is defined and packages_bash_aliases|length > 0 %} # User-defined aliases + {% for alias in packages_bash_aliases %} {% if alias.comment is defined %} # {{ alias.comment }} {% endif %} -alias {{ alias.alias }}='{{ alias.command }}' +alias {{ alias.alias }}='{{ alias.command | regex_replace("'", "'\"'\"'") }}' + {% endfor %} -{% endif %} +{% endif %} \ No newline at end of file diff --git a/roles/ssl/README.md b/roles/ssl/README.md index e8d42f5..eee70ba 100644 --- a/roles/ssl/README.md +++ b/roles/ssl/README.md @@ -14,65 +14,94 @@ Manages SSL certificates and keys ## Variables -| Variable | Description | Default Value | -|-------------------------------------------|------------------------------------------|----------------------------------------------------------------------| -| `ssl_domain` | Domain name for certificates | `{{ domain }}` | -| `ssl_keys_ca_src` | CA private key source path | | -| `ssl_keys_ca_name` | CA private key filename | `ca.key` | -| `ssl_keys_ca_dir` | CA private key directory | See [SSL Directories](#ssl-directories) | -| `ssl_keys_server_name` | Server private key filename | `{{ ssl_domain }}.key` | -| `ssl_keys_server_dir` | Server private key directory | See [SSL Directories](#ssl-directories) | -| `ssl_keys_server_mode` | File mode for server private key | `"0600"` | -| `ssl_keys_server_owner` | Owner for server private key | `root` | -| `ssl_keys_server_group` | Group for server private key | `root` | -| `ssl_certs_ca_src` | CA certificate source path | | -| `ssl_certs_ca_name` | CA certificate filename | `ca.crt` | -| `ssl_certs_ca_dir` | CA certificate directory | See [SSL Directories](#ssl-directories) | -| `ssl_certs_server_generate` | Generate SSL server certificate | `true` | -| `ssl_certs_server_name` | Server certificate filename | `{{ ssl_domain }}.crt` | -| `ssl_certs_server_dir` | Server certificate directory | See [SSL Directories](#ssl-directories) | -| `ssl_certs_server_mode` | File mode for server certificate | `"0644"` | -| `ssl_certs_server_owner` | Owner for server certificate | `root` | -| `ssl_certs_server_group` | Group for server certificate | `root` | -| `ssl_bundle_generate` | Generate SSL bundle | `false` | -| `ssl_bundle_name` | Certificate bundle filename | `{{ ssl_domain }}-bundle.pem` | -| `ssl_bundle_mode` | File mode for certificate bundle | `"0600"` | -| `ssl_bundle_owner` | Owner for certificate bundle | `root` | -| `ssl_bundle_group` | Group for certificate bundle | `root` | -| `ssl_certs_not_after` | Certificate validity period | `"+52000w"` | -| `ssl_csr_country_name` | Country for CSR subject | `UA` | -| `ssl_csr_state_or_province_name` | State or province for CSR subject | `Kyiv` | -| `ssl_csr_locality_name` | Locality (city) for CSR subject | `Kyiv` | -| `ssl_csr_organization_name` | Organization for CSR subject | `""` | -| `ssl_csr_organizational_unit_name` | Organizational unit for CSR subject | `""` | -| `ssl_csr_email_address` | Email address for CSR subject | `team@{{ ssl_domain }}` | -| `ssl_csr_ca_common_name` | Common name for CA CSR | `{{ ssl_domain }}` | -| `ssl_csr_ca_country_name` | Country for CA CSR | `{{ ssl_csr_country_name }}` | -| `ssl_csr_ca_state_or_province_name` | State or province for CA CSR | `{{ ssl_csr_state_or_province_name }}` | -| `ssl_csr_ca_locality_name` | Locality (city) for CA CSR | `{{ ssl_csr_locality_name }}` | -| `ssl_csr_ca_organization_name` | Organization for CA CSR | `{{ ssl_csr_organization_name }}` | -| `ssl_csr_ca_organizational_unit_name` | Organizational unit for CA CSR | `{{ ssl_csr_organizational_unit_name }}` | -| `ssl_csr_ca_email_address` | Email address for CA CSR | `{{ ssl_csr_email_address }}` | -| `ssl_csr_server_common_name` | Common name for server CSR | `"*.{{ ssl_domain }}"` | -| `ssl_csr_server_country_name` | Country for server CSR | `{{ ssl_csr_country_name }}` | -| `ssl_csr_server_state_or_province_name` | State or province for server CSR | `{{ ssl_csr_state_or_province_name }}` | -| `ssl_csr_server_locality_name` | Locality (city) for server CSR | `{{ ssl_csr_locality_name }}` | -| `ssl_csr_server_organization_name` | Organization for server CSR | `{{ ssl_csr_organization_name }}` | -| `ssl_csr_server_organizational_unit_name` | Organizational unit for server CSR | `{{ ssl_csr_organizational_unit_name }}` | -| `ssl_csr_server_email_address` | Email address for server CSR | `{{ ssl_csr_email_address }}` | -| `ssl_csr_server_subject_alt_name` | Subject Alternative Names for server CSR | `["DNS:*.{{ ssl_domain }}", "DNS:{{ ssl_domain }}", "IP:127.0.0.1"]` | +| Variable | Description | Default Value | +|-------------------------------------------|-----------------------------------------------------------------------|----------------------------------------------------------------------| +| `ssl_domain` | Domain name for certificates | `{{ domain }}` | +| `ssl_clients` | SSL client certificates to generate (see [SSL Clients](#ssl-clients)) | `[]` | +| `ssl_keys_ca_src` | CA private key source path | | +| `ssl_keys_ca_name` | CA private key filename | `ca.key` | +| `ssl_keys_ca_dir` | CA private key directory | See [SSL Directories](#ssl-directories) | +| `ssl_keys_server_name` | Server private key filename | `{{ ssl_domain }}.key` | +| `ssl_keys_server_dir` | Server private key directory | See [SSL Directories](#ssl-directories) | +| `ssl_keys_server_mode` | File mode for server private key | `"0600"` | +| `ssl_keys_server_owner` | Owner for server private key | `root` | +| `ssl_keys_server_group` | Group for server private key | `root` | +| `ssl_keys_clients_dir` | Client private key directory | See [SSL Directories](#ssl-directories) | +| `ssl_keys_clients_mode` | File mode for client private keys | `"0600"` | +| `ssl_keys_clients_owner` | Owner for client private keys | `root` | +| `ssl_keys_clients_group` | Group for client private keys | `root` | +| `ssl_certs_ca_src` | CA certificate source path | | +| `ssl_certs_ca_name` | CA certificate filename | `ca.crt` | +| `ssl_certs_ca_dir` | CA certificate directory | See [SSL Directories](#ssl-directories) | +| `ssl_certs_server_generate` | Generate SSL server certificate | `true` | +| `ssl_certs_server_name` | Server certificate filename | `{{ ssl_domain }}.crt` | +| `ssl_certs_server_dir` | Server certificate directory | See [SSL Directories](#ssl-directories) | +| `ssl_certs_server_mode` | File mode for server certificate | `"0644"` | +| `ssl_certs_server_owner` | Owner for server certificate | `root` | +| `ssl_certs_server_group` | Group for server certificate | `root` | +| `ssl_certs_clients_dir` | Client certificate directory | See [SSL Directories](#ssl-directories) | +| `ssl_certs_clients_mode` | File mode for client certificates | `"0644"` | +| `ssl_certs_clients_owner` | Owner for client certificates | `root` | +| `ssl_certs_clients_group` | Group for client certificates | `root` | +| `ssl_bundle_generate` | Generate SSL bundle | `false` | +| `ssl_bundle_name` | Certificate bundle filename | `{{ ssl_domain }}-bundle.pem` | +| `ssl_bundle_mode` | File mode for certificate bundle | `"0600"` | +| `ssl_bundle_owner` | Owner for certificate bundle | `root` | +| `ssl_bundle_group` | Group for certificate bundle | `root` | +| `ssl_certs_not_after` | Certificate validity period | `"+52000w"` | +| `ssl_csr_country_name` | Country for CSR subject | `UA` | +| `ssl_csr_state_or_province_name` | State or province for CSR subject | `Kyiv` | +| `ssl_csr_locality_name` | Locality (city) for CSR subject | `Kyiv` | +| `ssl_csr_organization_name` | Organization for CSR subject | `""` | +| `ssl_csr_organizational_unit_name` | Organizational unit for CSR subject | `""` | +| `ssl_csr_email_address` | Email address for CSR subject | `team@{{ ssl_domain }}` | +| `ssl_csr_ca_common_name` | Common name for CA CSR | `{{ ssl_domain }}` | +| `ssl_csr_ca_country_name` | Country for CA CSR | `{{ ssl_csr_country_name }}` | +| `ssl_csr_ca_state_or_province_name` | State or province for CA CSR | `{{ ssl_csr_state_or_province_name }}` | +| `ssl_csr_ca_locality_name` | Locality (city) for CA CSR | `{{ ssl_csr_locality_name }}` | +| `ssl_csr_ca_organization_name` | Organization for CA CSR | `{{ ssl_csr_organization_name }}` | +| `ssl_csr_ca_organizational_unit_name` | Organizational unit for CA CSR | `{{ ssl_csr_organizational_unit_name }}` | +| `ssl_csr_ca_email_address` | Email address for CA CSR | `{{ ssl_csr_email_address }}` | +| `ssl_csr_server_common_name` | Common name for server CSR | `"*.{{ ssl_domain }}"` | +| `ssl_csr_server_country_name` | Country for server CSR | `{{ ssl_csr_country_name }}` | +| `ssl_csr_server_state_or_province_name` | State or province for server CSR | `{{ ssl_csr_state_or_province_name }}` | +| `ssl_csr_server_locality_name` | Locality (city) for server CSR | `{{ ssl_csr_locality_name }}` | +| `ssl_csr_server_organization_name` | Organization for server CSR | `{{ ssl_csr_organization_name }}` | +| `ssl_csr_server_organizational_unit_name` | Organizational unit for server CSR | `{{ ssl_csr_organizational_unit_name }}` | +| `ssl_csr_server_email_address` | Email address for server CSR | `{{ ssl_csr_email_address }}` | +| `ssl_csr_server_subject_alt_name` | Subject Alternative Names for server CSR | `["DNS:*.{{ ssl_domain }}", "DNS:{{ ssl_domain }}", "IP:127.0.0.1"]` | ## SSL Directories The following table lists the default values for key and certificate directories depending on the operating system: -| Variable | Description | Debian/Ubuntu | RedHat/CentOS | -|------------------------|------------------------------|------------------------------------|------------------------------------| -| `ssl_keys_ca_dir` | CA private key directory | `/etc/ssl/private` | `/etc/pki/CA/private` | -| `ssl_keys_server_dir` | Server private key directory | `/etc/ssl/private` | `/etc/pki/tls/private` | -| `ssl_certs_ca_dir` | CA certificate directory | `/usr/local/share/ca-certificates` | `/etc/pki/ca-trust/source/anchors` | -| `ssl_certs_server_dir` | Server certificate directory | `/etc/ssl/certs` | `/etc/pki/tls/certs` | +| Variable | Description | Debian/Ubuntu | RedHat/CentOS | +|-------------------------|------------------------------|------------------------------------|------------------------------------| +| `ssl_keys_ca_dir` | CA private key directory | `/etc/ssl/private` | `/etc/pki/CA/private` | +| `ssl_keys_server_dir` | Server private key directory | `/etc/ssl/private` | `/etc/pki/tls/private` | +| `ssl_keys_clients_dir` | Client private key directory | `/etc/ssl/private/clients` | `/etc/pki/tls/private/clients` | +| `ssl_certs_ca_dir` | CA certificate directory | `/usr/local/share/ca-certificates` | `/etc/pki/ca-trust/source/anchors` | +| `ssl_certs_server_dir` | Server certificate directory | `/etc/ssl/certs` | `/etc/pki/tls/certs` | +| `ssl_certs_clients_dir` | Client certificate directory | `/etc/ssl/certs/clients` | `/etc/pki/tls/certs/clients` | + +## SSL Clients + +The role can generate client certificates and keys signed by the same CA as the server certificate. +Each client is defined by its `name` (used as the certificate and key file basename) and `common_name` (used in the certificate subject). +Optional `extensions` can be added to specify additional extended key usages; the role always includes `clientAuth` and deduplicates any provided extensions + +Example: + +```yaml +ssl_clients: + - name: docker + common_name: shell-workstation + extensions: + - emailProtection +``` + +This generates `{{ ssl_keys_clients_dir }}/docker.key` and `{{ ssl_certs_clients_dir }}/docker.crt`. ## Copy CA Certificate and Key -When both `ssl_certs_ca_path` and `ssl_keys_ca_path` are set, the existing CA certificate and key are copied instead of being generated +When both `ssl_certs_ca_src` and `ssl_keys_ca_src` are set, the existing CA certificate and key are copied instead of being generated diff --git a/roles/ssl/defaults/main.yml b/roles/ssl/defaults/main.yml index de80fa9..c4093ed 100644 --- a/roles/ssl/defaults/main.yml +++ b/roles/ssl/defaults/main.yml @@ -1,6 +1,8 @@ --- ssl_domain: "{{ domain }}" +ssl_clients: [] + ssl_keys_ca_name: ca.key ssl_keys_ca_dir: >- {{ @@ -22,6 +24,17 @@ ssl_keys_server_mode: "0600" ssl_keys_server_owner: root ssl_keys_server_group: root +ssl_keys_clients_dir: >- + {{ + { + 'debian': '/etc/ssl/private/clients', + 'redhat': '/etc/pki/tls/private/clients' + }[system_os_family] + }} +ssl_keys_clients_mode: "0600" +ssl_keys_clients_owner: root +ssl_keys_clients_group: root + ssl_certs_ca_name: ca.crt ssl_certs_ca_dir: >- {{ @@ -44,6 +57,17 @@ ssl_certs_server_mode: "0644" ssl_certs_server_owner: root ssl_certs_server_group: root +ssl_certs_clients_dir: >- + {{ + { + 'debian': '/etc/ssl/certs/clients', + 'redhat': '/etc/pki/tls/certs/clients' + }[system_os_family] + }} +ssl_certs_clients_mode: "0644" +ssl_certs_clients_owner: root +ssl_certs_clients_group: root + ssl_bundle_generate: false ssl_bundle_name: "{{ ssl_domain }}-bundle.pem" ssl_bundle_mode: "0600" diff --git a/roles/ssl/tasks/clients.yml b/roles/ssl/tasks/clients.yml new file mode 100644 index 0000000..06f67f3 --- /dev/null +++ b/roles/ssl/tasks/clients.yml @@ -0,0 +1,50 @@ +--- +- name: Generate SSL client private keys + community.crypto.openssl_privatekey: + path: "{{ ssl_keys_clients_dir }}/{{ client.name }}.key" + owner: "{{ ssl_keys_clients_owner }}" + group: "{{ ssl_keys_clients_group }}" + mode: "{{ ssl_keys_clients_mode }}" + become: true + no_log: true + loop: "{{ ssl_clients }}" + loop_control: + loop_var: client + register: ssl_client_keys + +- name: Configure SSL client CSRs + community.crypto.openssl_csr_pipe: + privatekey_path: "{{ ssl_keys_clients_dir }}/{{ client.name }}.key" + common_name: "{{ client.common_name }}" + basic_constraints: + - CA:FALSE + extended_key_usage: "{{ (['clientAuth'] + (client.extensions | default([]))) | unique }}" + use_common_name_for_san: false + create_subject_key_identifier: true + become: true + changed_when: ssl_client_keys.results[client_idx].changed + no_log: true + loop: "{{ ssl_clients }}" + loop_control: + loop_var: client + index_var: client_idx + register: ssl_client_csrs + +- name: Generate SSL client certificates + community.crypto.x509_certificate: + path: "{{ ssl_certs_clients_dir }}/{{ client.name }}.crt" + privatekey_path: "{{ ssl_keys_clients_dir }}/{{ client.name }}.key" + csr_content: "{{ ssl_client_csrs.results[client_idx].csr }}" + provider: ownca + ownca_path: "{{ ssl_certs_ca_path }}" + ownca_privatekey_path: "{{ ssl_keys_ca_path }}" + ownca_not_after: "{{ ssl_certs_not_after }}" + owner: "{{ ssl_certs_clients_owner }}" + group: "{{ ssl_certs_clients_group }}" + mode: "{{ ssl_certs_clients_mode }}" + become: true + no_log: true + loop: "{{ ssl_clients }}" + loop_control: + loop_var: client + index_var: client_idx diff --git a/roles/ssl/tasks/main.yml b/roles/ssl/tasks/main.yml index 5c70204..b059504 100644 --- a/roles/ssl/tasks/main.yml +++ b/roles/ssl/tasks/main.yml @@ -24,3 +24,7 @@ - name: Include SSL bundle tasks ansible.builtin.include_tasks: "bundle.yml" when: ssl_bundle_generate + +- name: Include SSL clients tasks + ansible.builtin.include_tasks: "clients.yml" + when: ssl_clients | length > 0 diff --git a/roles/ssl/tasks/setup.yml b/roles/ssl/tasks/setup.yml index 495c266..cbcd097 100644 --- a/roles/ssl/tasks/setup.yml +++ b/roles/ssl/tasks/setup.yml @@ -42,3 +42,23 @@ become: true loop_control: loop_var: dir + +- name: Create SSL client keys directory + ansible.builtin.file: + path: "{{ ssl_keys_clients_dir }}" + state: directory + owner: root + group: ssl-cert + mode: "0710" + become: true + when: ssl_clients | length > 0 + +- name: Create SSL client certificates directory + ansible.builtin.file: + path: "{{ ssl_certs_clients_dir }}" + state: directory + owner: root + group: root + mode: "0755" + become: true + when: ssl_clients | length > 0 From d985858f75b8a04f5d638f5d0641964a7c7c7c20 Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Sun, 21 Jun 2026 23:24:52 +0300 Subject: [PATCH 4/6] feat: #45 docker tls --- molecule/default/group_vars/all.yml | 29 +++++++++ molecule/default/roles/docker/tasks/setup.yml | 62 +++++++++++++++++++ molecule/default/roles/ssl/defaults/main.yml | 21 ------- molecule/default/roles/ssl/vars/main.yml | 4 -- roles/docker/README.md | 21 ++++--- roles/docker/defaults/main.yml | 5 ++ roles/docker/files/override.conf | 6 ++ roles/docker/meta/main.yml | 1 + roles/docker/tasks/setup.yml | 21 +++++++ roles/docker/templates/daemon.json.j2 | 13 ++++ roles/docker/vars/main.yml | 3 + roles/ssl/README.md | 2 +- 12 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 roles/docker/files/override.conf create mode 100644 roles/docker/templates/daemon.json.j2 create mode 100644 roles/docker/vars/main.yml diff --git a/molecule/default/group_vars/all.yml b/molecule/default/group_vars/all.yml index 7408939..2bdb889 100644 --- a/molecule/default/group_vars/all.yml +++ b/molecule/default/group_vars/all.yml @@ -77,10 +77,34 @@ mail_recipient: "test@example.com" # SSL ssl_domain: "test.example.com" ssl_keys_ca_name: "ca.key" +ssl_keys_server_dir: >- + {{ + { + 'debian': '/etc/ssl/private', + 'redhat': '/etc/pki/tls/private' + }[system_os_family] + }} ssl_keys_server_name: "server.key" +ssl_keys_server_path: "{{ ssl_keys_server_dir }}/{{ ssl_keys_server_name }}" ssl_certs_server_generate: true +ssl_certs_ca_dir: >- + {{ + { + 'debian': '/usr/local/share/ca-certificates', + 'redhat': '/etc/pki/ca-trust/source/anchors' + }[system_os_family] + }} ssl_certs_ca_name: "ca.crt" +ssl_certs_ca_path: "{{ ssl_certs_ca_dir }}/{{ ssl_certs_ca_name }}" +ssl_certs_server_dir: >- + {{ + { + 'debian': '/etc/ssl/certs', + 'redhat': '/etc/pki/tls/certs' + }[system_os_family] + }} ssl_certs_server_name: "server.crt" +ssl_certs_server_path: "{{ ssl_certs_server_dir }}/{{ ssl_certs_server_name }}" ssl_bundle_generate: true ssl_bundle_name: "bundle.crt" ssl_clients: @@ -92,7 +116,12 @@ ssl_clients: # Docker docker_user: "{{ ansible_user_id }}" +docker_systemd_override_dir: "/etc/systemd/system/docker.service.d" docker_system_prune_cron: "" +docker_tls_enabled: true +docker_tls_ca_path: "{{ ssl_certs_ca_path }}" +docker_tls_key_path: "{{ ssl_keys_server_path }}" +docker_tls_cert_path: "{{ ssl_certs_server_path }}" # Samba samba_interfaces: diff --git a/molecule/default/roles/docker/tasks/setup.yml b/molecule/default/roles/docker/tasks/setup.yml index 85fe518..ef5e2f5 100644 --- a/molecule/default/roles/docker/tasks/setup.yml +++ b/molecule/default/roles/docker/tasks/setup.yml @@ -32,3 +32,65 @@ that: docker_user in docker_user_groups.stdout.split() fail_msg: "{{ docker_user }} user is not in the docker group" success_msg: "{{ docker_user }} user is in the docker group" + +- name: Verify Docker systemd override configuration + block: + - name: Stat Docker systemd override file + ansible.builtin.stat: + path: "{{ docker_systemd_override_dir }}/override.conf" + register: docker_systemd_override_stat + + - name: Assert Docker systemd override file exists + ansible.builtin.assert: + that: + - docker_systemd_override_stat.stat.exists + - docker_systemd_override_stat.stat.isreg + - docker_systemd_override_stat.stat.mode == '0644' + fail_msg: "Docker systemd override file is missing or has incorrect attributes" + success_msg: "Docker systemd override file exists with correct attributes" + + - name: Read Docker systemd override file + ansible.builtin.slurp: + src: "{{ docker_systemd_override_dir }}/override.conf" + register: docker_systemd_override_content + + - name: Assert Docker systemd override file contains expected directives + ansible.builtin.assert: + that: + - "'ExecStart=' in (docker_systemd_override_content.content | b64decode)" + - "'--config-file=/etc/docker/daemon.json' in (docker_systemd_override_content.content | b64decode)" + fail_msg: "Docker systemd override file content does not match expected configuration" + success_msg: "Docker systemd override file contains expected directives" + +- name: Verify Docker daemon TLS configuration + block: + - name: Stat Docker daemon configuration file + ansible.builtin.stat: + path: /etc/docker/daemon.json + register: docker_daemon_json_stat + + - name: Assert Docker daemon configuration file exists + ansible.builtin.assert: + that: + - docker_daemon_json_stat.stat.exists + - docker_daemon_json_stat.stat.isreg + - docker_daemon_json_stat.stat.mode == '0644' + fail_msg: "Docker daemon configuration file is missing or has incorrect attributes" + success_msg: "Docker daemon configuration file exists with correct attributes" + + - name: Read Docker daemon configuration file + ansible.builtin.slurp: + src: /etc/docker/daemon.json + register: docker_daemon_json_content + + - name: Assert Docker daemon TLS options are configured + ansible.builtin.assert: + that: + - daemon_json.tlsverify | bool + - daemon_json.tlscacert == docker_tls_ca_path + - daemon_json.tlskey == docker_tls_key_path + - daemon_json.tlscert == docker_tls_cert_path + fail_msg: "Docker daemon TLS configuration is missing or incorrect" + success_msg: "Docker daemon TLS configuration is valid" + vars: + daemon_json: "{{ docker_daemon_json_content.content | b64decode | from_json }}" diff --git a/molecule/default/roles/ssl/defaults/main.yml b/molecule/default/roles/ssl/defaults/main.yml index 43caddb..e56fe15 100644 --- a/molecule/default/roles/ssl/defaults/main.yml +++ b/molecule/default/roles/ssl/defaults/main.yml @@ -6,13 +6,6 @@ ssl_keys_ca_dir: >- 'redhat': '/etc/pki/CA/private' }[system_os_family] }} -ssl_keys_server_dir: >- - {{ - { - 'debian': '/etc/ssl/private', - 'redhat': '/etc/pki/tls/private' - }[system_os_family] - }} ssl_keys_clients_dir: >- {{ { @@ -20,20 +13,6 @@ ssl_keys_clients_dir: >- 'redhat': '/etc/pki/tls/private/clients' }[system_os_family] }} -ssl_certs_ca_dir: >- - {{ - { - 'debian': '/usr/local/share/ca-certificates', - 'redhat': '/etc/pki/ca-trust/source/anchors' - }[system_os_family] - }} -ssl_certs_server_dir: >- - {{ - { - 'debian': '/etc/ssl/certs', - 'redhat': '/etc/pki/tls/certs' - }[system_os_family] - }} ssl_certs_clients_dir: >- {{ { diff --git a/molecule/default/roles/ssl/vars/main.yml b/molecule/default/roles/ssl/vars/main.yml index af3103c..274b6ca 100644 --- a/molecule/default/roles/ssl/vars/main.yml +++ b/molecule/default/roles/ssl/vars/main.yml @@ -1,7 +1,3 @@ --- ssl_keys_ca_path: "{{ ssl_keys_ca_dir }}/{{ ssl_keys_ca_name }}" -ssl_keys_server_path: "{{ ssl_keys_server_dir }}/{{ ssl_keys_server_name }}" - -ssl_certs_ca_path: "{{ ssl_certs_ca_dir }}/{{ ssl_certs_ca_name }}" -ssl_certs_server_path: "{{ ssl_certs_server_dir }}/{{ ssl_certs_server_name }}" ssl_bundle_path: "{{ ssl_certs_server_dir }}/{{ ssl_bundle_name }}" diff --git a/roles/docker/README.md b/roles/docker/README.md index 7208d84..6059e34 100644 --- a/roles/docker/README.md +++ b/roles/docker/README.md @@ -14,14 +14,19 @@ Installs and configures Docker ## Variables -| Variable | Description | Default Value | -|----------------------------|------------------------------------------------|-------------------------| -| `docker_user` | User to add to the docker group | `{{ ansible_user_id }}` | -| `docker_system_prune_cron` | Docker system prune cron schedule | `17 1 * * *` | -| `docker_swarm_enabled` | Enable Docker Swarm mode | `false` | -| `docker_qemu_enabled` | Ename QEMU emulation for multi-platform builds | `false` | -| `docker_registries` | Docker registry authentication configuration | | -| `docker_networks` | Docker networks configuration | | +| Variable | Description | Default Value | +|----------------------------|-------------------------------------------------|---------------------------------------------| +| `docker_user` | User to add to the docker group | `{{ ansible_user_id }}` | +| `docker_system_prune_cron` | Docker system prune cron schedule | `17 1 * * *` | +| `docker_hosts` | List of docker daemon hosts | `[]` | +| `docker_tls_enabled` | Enable Docker daemon TLS configuration | `false` | +| `docker_tls_ca_path` | Path to the Docker TLS CA certificate | `{{ ssl_certs_ca_path }}` | +| `docker_tls_key_path` | Path to the Docker TLS private key | `{{ ssl_keys_server_path }}` | +| `docker_tls_cert_path` | Path to the Docker TLS server certificate | `{{ ssl_certs_server_path }}` | +| `docker_networks` | Docker networks configuration | See [Docker Networks](#docker-networks) | +| `docker_registries` | Docker registry authentication configuration | See [Docker Registries](#docker-registries) | +| `docker_swarm_enabled` | Enable Docker Swarm mode | `false` | +| `docker_qemu_enabled` | Enable QEMU emulation for multi-platform builds | `false` | ## Docker Networks diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml index 707e31f..7f14f39 100644 --- a/roles/docker/defaults/main.yml +++ b/roles/docker/defaults/main.yml @@ -1,5 +1,10 @@ --- docker_user: "{{ ansible_user_id }}" docker_system_prune_cron: "17 1 * * *" +docker_hosts: [] +docker_tls_enabled: false +docker_tls_ca_path: "{{ ssl_certs_ca_path }}" +docker_tls_key_path: "{{ ssl_keys_server_path }}" +docker_tls_cert_path: "{{ ssl_certs_server_path }}" docker_swarm_enabled: false docker_qemu_enabled: false diff --git a/roles/docker/files/override.conf b/roles/docker/files/override.conf new file mode 100644 index 0000000..dfabaa6 --- /dev/null +++ b/roles/docker/files/override.conf @@ -0,0 +1,6 @@ +[Service] +ExecStart= +ExecStart=/usr/bin/dockerd \ + --config-file=/etc/docker/daemon.json \ + --containerd=/run/containerd/containerd.sock + \ No newline at end of file diff --git a/roles/docker/meta/main.yml b/roles/docker/meta/main.yml index 950811b..d984909 100644 --- a/roles/docker/meta/main.yml +++ b/roles/docker/meta/main.yml @@ -26,3 +26,4 @@ galaxy_info: dependencies: - role: scalified.setup.system + - role: scalified.setup.ssl diff --git a/roles/docker/tasks/setup.yml b/roles/docker/tasks/setup.yml index 54fe429..dff603c 100644 --- a/roles/docker/tasks/setup.yml +++ b/roles/docker/tasks/setup.yml @@ -17,6 +17,27 @@ become: true notify: "system : request reboot" +- name: Create Docker systemd override directory + ansible.builtin.file: + path: "{{ docker_systemd_override_dir }}" + state: directory + mode: "0755" + become: true + +- name: Deploy Docker systemd override configuration + ansible.builtin.copy: + src: override.conf + dest: "{{ docker_systemd_override_dir }}/override.conf" + mode: "0644" + become: true + +- name: Deploy Docker daemon configuration + ansible.builtin.template: + src: daemon.json.j2 + dest: /etc/docker/daemon.json + mode: "0644" + become: true + - name: Add Docker user to the Docker group ansible.builtin.user: name: "{{ docker_user }}" diff --git a/roles/docker/templates/daemon.json.j2 b/roles/docker/templates/daemon.json.j2 new file mode 100644 index 0000000..c50c16a --- /dev/null +++ b/roles/docker/templates/daemon.json.j2 @@ -0,0 +1,13 @@ +{ + "hosts": [ +{% for host in (([docker_default_host] + (docker_hosts | default([]))) | unique | list) %} + "{{ host }}"{% if not loop.last %},{% endif %} + +{% endfor %} + ]{% if docker_tls_enabled | default(false) | bool %}, + "tlsverify": true, + "tlscacert": "{{ docker_tls_ca_path }}", + "tlskey": "{{ docker_tls_key_path }}", + "tlscert": "{{ docker_tls_cert_path }}"{% endif %} + +} diff --git a/roles/docker/vars/main.yml b/roles/docker/vars/main.yml new file mode 100644 index 0000000..fd597c4 --- /dev/null +++ b/roles/docker/vars/main.yml @@ -0,0 +1,3 @@ +--- +docker_systemd_override_dir: "/etc/systemd/system/docker.service.d" +docker_default_host: "unix:///var/run/docker.sock" diff --git a/roles/ssl/README.md b/roles/ssl/README.md index eee70ba..20b5aca 100644 --- a/roles/ssl/README.md +++ b/roles/ssl/README.md @@ -95,7 +95,7 @@ Example: ```yaml ssl_clients: - name: docker - common_name: shell-workstation + common_name: pc-workstation extensions: - emailProtection ``` From 3137f3896a7cbeae1385a06e566966c195439471 Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Mon, 22 Jun 2026 07:31:57 +0300 Subject: [PATCH 5/6] feat: #45 Missing logic review fixes --- CHANGELOG.md | 1 + CLAUDE.md | 10 +++++----- roles/docker/files/override.conf | 1 - roles/docker/handlers/main.yml | 10 ++++++++++ roles/docker/tasks/setup.yml | 2 ++ roles/mail/tasks/debian/mail.yml | 1 + roles/mail/tasks/redhat/mail.yml | 1 + roles/nut/tasks/ups_vars.yml | 1 + roles/nut/tasks/upsd.yml | 1 + roles/nut/tasks/upsmon.yml | 1 + roles/packages/templates/.bash_aliases.j2 | 3 +-- roles/system/README.md | 2 +- roles/system/tasks/services.yml | 2 +- 13 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 roles/docker/handlers/main.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c09450..e38ed7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * System: scripts * System: services * j2 extensions for template files +* SSL clients certificates generation * Docker: QEMU emulation, tcp host # 2.7.7 diff --git a/CLAUDE.md b/CLAUDE.md index c563c0c..a63e402 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,9 +52,9 @@ This is an **Ansible Collection** for server setup automation, published as: `sc ## Molecule Tests * Run integration tests with: molecule test. -* Every file with logic must have a corresponding test file in: `extensions/molecule/default/roles//tasks/` -* Role-level verifications are placed in: `extensions/molecule/default/verify.yml` +* Every file with logic must have a corresponding test file in: `molecule/default/roles//tasks/` +* Role-level verifications are placed in: `molecule/default/verify.yml` * Use the following test files as templates: - * `extensions/molecule/default/packages/tasks/packages.yml` — verify package installations. - * `extensions/molecule/default/fs/tasks/mount.yml` — verify files, directories, scripts attributes and content. - * `extensions/molecule/default/docker/tasks/setup.yml` - verify user group. + * `molecule/default/packages/tasks/packages.yml` — verify package installations. + * `molecule/default/fs/tasks/mount.yml` — verify files, directories, scripts attributes and content. + * `molecule/default/docker/tasks/setup.yml` - verify user group. diff --git a/roles/docker/files/override.conf b/roles/docker/files/override.conf index dfabaa6..e0f42a7 100644 --- a/roles/docker/files/override.conf +++ b/roles/docker/files/override.conf @@ -3,4 +3,3 @@ ExecStart= ExecStart=/usr/bin/dockerd \ --config-file=/etc/docker/daemon.json \ --containerd=/run/containerd/containerd.sock - \ No newline at end of file diff --git a/roles/docker/handlers/main.yml b/roles/docker/handlers/main.yml new file mode 100644 index 0000000..1921055 --- /dev/null +++ b/roles/docker/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: Restart docker + ansible.builtin.systemd_service: + name: docker + state: restarted + daemon-reload: true + enabled: true + become: true + when: system_systemd_managed + listen: "docker : restart" diff --git a/roles/docker/tasks/setup.yml b/roles/docker/tasks/setup.yml index dff603c..92ac95e 100644 --- a/roles/docker/tasks/setup.yml +++ b/roles/docker/tasks/setup.yml @@ -30,6 +30,7 @@ dest: "{{ docker_systemd_override_dir }}/override.conf" mode: "0644" become: true + notify: "docker : restart" - name: Deploy Docker daemon configuration ansible.builtin.template: @@ -37,6 +38,7 @@ dest: /etc/docker/daemon.json mode: "0644" become: true + notify: "docker : restart" - name: Add Docker user to the Docker group ansible.builtin.user: diff --git a/roles/mail/tasks/debian/mail.yml b/roles/mail/tasks/debian/mail.yml index bddac66..a908cae 100644 --- a/roles/mail/tasks/debian/mail.yml +++ b/roles/mail/tasks/debian/mail.yml @@ -14,6 +14,7 @@ src: "{{ system_os_family }}/passwd.client.j2" dest: /etc/exim4/passwd.client mode: "0640" + no_log: true become: true - name: Deploy exim4 rewrite rules diff --git a/roles/mail/tasks/redhat/mail.yml b/roles/mail/tasks/redhat/mail.yml index 25a2758..5926fe7 100644 --- a/roles/mail/tasks/redhat/mail.yml +++ b/roles/mail/tasks/redhat/mail.yml @@ -16,6 +16,7 @@ src: "{{ system_os_family }}/sasl_passwd.j2" dest: /etc/postfix/sasl_passwd mode: "0600" + no_log: true become: true notify: - postmap sasl_password diff --git a/roles/nut/tasks/ups_vars.yml b/roles/nut/tasks/ups_vars.yml index 4e8bd87..3b7b9a8 100644 --- a/roles/nut/tasks/ups_vars.yml +++ b/roles/nut/tasks/ups_vars.yml @@ -5,6 +5,7 @@ owner: root group: nut mode: "0750" + no_log: true become: true - name: Update UPS variables diff --git a/roles/nut/tasks/upsd.yml b/roles/nut/tasks/upsd.yml index 54f1d60..332b854 100644 --- a/roles/nut/tasks/upsd.yml +++ b/roles/nut/tasks/upsd.yml @@ -15,6 +15,7 @@ owner: root group: nut mode: "640" + no_log: true loop: - upsd.conf.j2 - upsd.users.j2 diff --git a/roles/nut/tasks/upsmon.yml b/roles/nut/tasks/upsmon.yml index 73954ea..db2a05c 100644 --- a/roles/nut/tasks/upsmon.yml +++ b/roles/nut/tasks/upsmon.yml @@ -13,6 +13,7 @@ owner: root group: nut mode: "0640" + no_log: true become: true - name: Enable NUT UPSMON service diff --git a/roles/packages/templates/.bash_aliases.j2 b/roles/packages/templates/.bash_aliases.j2 index 51a0de0..62b1f61 100644 --- a/roles/packages/templates/.bash_aliases.j2 +++ b/roles/packages/templates/.bash_aliases.j2 @@ -18,7 +18,6 @@ alias hg='history|grep' {% if alias.comment is defined %} # {{ alias.comment }} {% endif %} -alias {{ alias.alias }}='{{ alias.command | regex_replace("'", "'\"'\"'") }}' +alias {{ alias.alias }}='{{ alias.command }}' {% endfor %} -{% endif %} \ No newline at end of file diff --git a/roles/system/README.md b/roles/system/README.md index 068624d..3bcce87 100644 --- a/roles/system/README.md +++ b/roles/system/README.md @@ -54,7 +54,7 @@ Configures essential system settings and performs server setup | `system_update_autoclean` | Clean package cache after updates | `true` | | `system_hostname` | Hostname to set on target servers | `{{ ansible_hostname }}` | | `system_timezone` | Timezone to set on target servers | `UTC` | -| `system_locales_default` | Default system locale | `en_US.UTF-8` | +| `system_locale_default` | Default system locale | `en_US.UTF-8` | | `system_locales` | List of system locales | `[]` | | `system_sysctl` | System kernel parameters | `{"vm.max_map_count": 262144, "fs.aio-max-nr": 1048576}` | | `system_motd_src` | MOTD banner template source path | `motd.j2` | diff --git a/roles/system/tasks/services.yml b/roles/system/tasks/services.yml index 3ce251c..b927eea 100644 --- a/roles/system/tasks/services.yml +++ b/roles/system/tasks/services.yml @@ -2,7 +2,7 @@ - name: Manage system services ansible.builtin.systemd_service: name: "{{ service.name }}" - state: "{{ service.state }}" + state: "{{ service.state | default(omit) }}" enabled: "{{ service.enabled | default(omit) }}" masked: "{{ service.masked | default(omit) }}" become: true From be69fa4f475328ebc99542fb39b48e81bac4d2ac Mon Sep 17 00:00:00 2001 From: Vladyslav Baidak Date: Mon, 22 Jun 2026 07:43:17 +0300 Subject: [PATCH 6/6] feat: #45 Missing logic review fixes --- roles/packages/templates/.bash_aliases.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/packages/templates/.bash_aliases.j2 b/roles/packages/templates/.bash_aliases.j2 index 62b1f61..7b0d12b 100644 --- a/roles/packages/templates/.bash_aliases.j2 +++ b/roles/packages/templates/.bash_aliases.j2 @@ -9,8 +9,7 @@ alias grep='grep --color=auto' # Find command in history alias hg='history|grep' - -{% if packages_bash_aliases is defined and packages_bash_aliases|length > 0 %} +{% if packages_bash_aliases | default([]) | length > 0 %} # User-defined aliases @@ -21,3 +20,4 @@ alias hg='history|grep' alias {{ alias.alias }}='{{ alias.command }}' {% endfor %} +{% endif %}