From 04e38f42b5e823f54bbfec2b9ab1d2caab432cd8 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Wed, 17 Jun 2026 14:08:04 +0200 Subject: [PATCH 01/18] initial commit --- .gitignore | 4 +++ README.md | 3 ++ ansible.cfg | 3 ++ galaxy.yml | 10 +++++++ roles/base/defaults/main.yml | 14 +++++++++ roles/base/tasks/main.yml | 41 +++++++++++++++++++++++++++ roles/docker/tasks/main.yml | 35 +++++++++++++++++++++++ roles/security/handlers/main.yml | 5 ++++ roles/security/tasks/main.yml | 35 +++++++++++++++++++++++ roles/security/templates/sshd.conf.j2 | 5 ++++ roles/users/tasks/main.yml | 38 +++++++++++++++++++++++++ 11 files changed, 193 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 ansible.cfg create mode 100644 galaxy.yml create mode 100644 roles/base/defaults/main.yml create mode 100644 roles/base/tasks/main.yml create mode 100644 roles/docker/tasks/main.yml create mode 100644 roles/security/handlers/main.yml create mode 100644 roles/security/tasks/main.yml create mode 100644 roles/security/templates/sshd.conf.j2 create mode 100644 roles/users/tasks/main.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c2058c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +## OS generated files +# macOS +.DS_Store +._* diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5b04a9 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Ansible Collection + +Opinionated set of roles to configure reasonable defaults for newly provisioned Debian / Ubuntu VMs running Docker. The repository provides a ready-to-use Ansible Collection (`studio.ansible`) containing reusable roles. diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..ade6677 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,3 @@ +[defaults] +inventory = inventory/hosts.ini +roles_path = roles diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..9e0f03e --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,10 @@ +## galaxy.yml + +namespace: studio +name: ansible +readme: README.md +version: 1.0.0 +authors: + - Studio Vitali +description: Studio Ansible collection containing common roles +license: MIT diff --git a/roles/base/defaults/main.yml b/roles/base/defaults/main.yml new file mode 100644 index 0000000..696eb59 --- /dev/null +++ b/roles/base/defaults/main.yml @@ -0,0 +1,14 @@ +--- +## roles/base/defaults/main.yml + +base_timezone: "Europe/Zurich" +base_packages: + - curl + - git + - htop + - rsync + - unzip + - unattended-upgrades + # - acl ## needed for Ansible to become unprivileged user + # - python3-pip + # - wget diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml new file mode 100644 index 0000000..c398fff --- /dev/null +++ b/roles/base/tasks/main.yml @@ -0,0 +1,41 @@ +--- +- name: Set system timezone + community.general.timezone: + # name: "{{ base_timezone }}" + name: Europe/Zurich + become: true + +- name: Fully upgrade system + ansible.builtin.apt: + update_cache: true + upgrade: dist + cache_valid_time: 3600 + when: ansible_facts['os_family'] == "Debian" ## only on Debian / Ubuntu systems + become: true + +- name: Install essential system utilities + ansible.builtin.apt: + # name: "{{ base_packages }}" + name: + - acl + - curl + - git + - htop + - rsync + - unzip + - unattended-upgrades + state: present + update_cache: true + when: ansible_facts['os_family'] == "Debian" + become: true +# +# - name: Enable unattended-upgrades +# ansible.builtin.copy: +# dest: /etc/apt/apt.conf.d/20auto-upgrades +# content: | +# APT::Periodic::Update-Package-Lists "1"; +# APT::Periodic::Unattended-Upgrade "1"; +# mode: "0644" +# when: ansible_facts['os_family'] == "Debian" + +## SCHEDULE REBOOTS 4 kernel upgrades: with cron, unattended-upgrades or action workflows? diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml new file mode 100644 index 0000000..4229019 --- /dev/null +++ b/roles/docker/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: Install necessary Docker packages + ansible.builtin.apt: + name: + - docker.io + - docker-compose + state: present + when: ansible_facts['os_family'] == "Debian" ## Debian / Ubuntu + become: true + +- name: Ensure Docker service is running + ansible.builtin.service: + name: docker + state: started + enabled: true + +- name: Create docker user for container execution + ansible.builtin.user: + name: docker + state: present + system: true + shell: /usr/sbin/nologin + groups: docker + append: true + create_home: false + become: true + +- name: Create target for docker compose + ansible.builtin.file: + path: /opt/docker + state: directory + owner: deployment + group: deployment + mode: "0755" + become: true diff --git a/roles/security/handlers/main.yml b/roles/security/handlers/main.yml new file mode 100644 index 0000000..6998953 --- /dev/null +++ b/roles/security/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart sshd + ansible.builtin.service: + name: sshd + state: restarted diff --git a/roles/security/tasks/main.yml b/roles/security/tasks/main.yml new file mode 100644 index 0000000..a0b5b33 --- /dev/null +++ b/roles/security/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: Create SSH drop-in directory + ansible.builtin.file: + path: /etc/ssh/sshd_config.d + state: directory + owner: root + group: root + mode: "0755" + become: true + +- name: Harden SSH configuration with drop-in template + ansible.builtin.template: + src: sshd.conf.j2 + dest: /etc/ssh/sshd_config.d/sshd.conf + owner: root + group: root + mode: "0644" + validate: "sshd -t -f %s" + notify: Restart sshd + become: true + +- name: Install fail2ban + ansible.builtin.apt: + name: + - fail2ban + state: present + when: ansible_facts['os_family'] == "Debian" + become: true + +- name: Start fail2ban + ansible.builtin.service: + name: fail2ban + state: started + enabled: yes + when: ansible_facts['os_family'] == "Debian" diff --git a/roles/security/templates/sshd.conf.j2 b/roles/security/templates/sshd.conf.j2 new file mode 100644 index 0000000..5bc4370 --- /dev/null +++ b/roles/security/templates/sshd.conf.j2 @@ -0,0 +1,5 @@ +## Disable root login +PermitRootLogin no + +## Disable password authentication +PasswordAuthentication no diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml new file mode 100644 index 0000000..6628891 --- /dev/null +++ b/roles/users/tasks/main.yml @@ -0,0 +1,38 @@ +--- +# - name: Create primary admin user +# ansible.builtin.user: +# name: sysadmin +# groups: sudo +# append: yes +# shell: /bin/bash +# create_home: yes +# become: true + +# - name: Add SSH public key for sysadmin +# ansible.posix.authorized_key: +# user: sysadmin +# state: present +# key: "{{ sysadmin_public_ssh_key }}" +# become: true + +# - name: Allow passwordless sudo to sysadmin user +# ansible.builtin.copy: +# dest: /etc/sudoers.d/sysadmin +# content: "sysadmin ALL=(ALL) NOPASSWD: ALL" +# mode: "0440" +# validate: /usr/sbin/visudo -cf %s +# become: true + +- name: Create deployment user for CI/CD + ansible.builtin.user: + name: deployment + shell: /bin/bash + create_home: yes + become: true + +- name: Add SSH public key for deployment user + ansible.posix.authorized_key: + user: deployment + state: present + key: "{{ deployment_public_ssh_key }}" + become: true From 9fbba9ffd6426296c9662584151014c5ed25129f Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Thu, 9 Jul 2026 16:17:30 +0200 Subject: [PATCH 02/18] fix: remove OS checks either use the generic package module or OS-specific tasks that won't silently fail playbook runs --- README.md | 4 +++- roles/base/tasks/main.yml | 2 -- roles/docker/tasks/main.yml | 1 - roles/security/tasks/main.yml | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a5b04a9..19a8767 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # Ansible Collection -Opinionated set of roles to configure reasonable defaults for newly provisioned Debian / Ubuntu VMs running Docker. The repository provides a ready-to-use Ansible Collection (`studio.ansible`) containing reusable roles. +Opinionated set of roles to configure reasonable defaults for newly provisioned virtual machines. The repository provides a ready-to-use Ansible Collection (`studio.ansible`) containing reusable roles. + +Currently only supports Debian-based systems. diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index c398fff..7849c82 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -10,7 +10,6 @@ update_cache: true upgrade: dist cache_valid_time: 3600 - when: ansible_facts['os_family'] == "Debian" ## only on Debian / Ubuntu systems become: true - name: Install essential system utilities @@ -26,7 +25,6 @@ - unattended-upgrades state: present update_cache: true - when: ansible_facts['os_family'] == "Debian" become: true # # - name: Enable unattended-upgrades diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 4229019..3449ae3 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -5,7 +5,6 @@ - docker.io - docker-compose state: present - when: ansible_facts['os_family'] == "Debian" ## Debian / Ubuntu become: true - name: Ensure Docker service is running diff --git a/roles/security/tasks/main.yml b/roles/security/tasks/main.yml index a0b5b33..956a63f 100644 --- a/roles/security/tasks/main.yml +++ b/roles/security/tasks/main.yml @@ -24,7 +24,6 @@ name: - fail2ban state: present - when: ansible_facts['os_family'] == "Debian" become: true - name: Start fail2ban @@ -32,4 +31,3 @@ name: fail2ban state: started enabled: yes - when: ansible_facts['os_family'] == "Debian" From d4587423a1c9f9de0e232ffe792a01ee2cd0737d Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Thu, 23 Jul 2026 16:20:01 +0200 Subject: [PATCH 03/18] fix: galaxy.yml --- galaxy.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index 9e0f03e..c79a793 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -3,8 +3,9 @@ namespace: studio name: ansible readme: README.md -version: 1.0.0 +version: 0.0.1 authors: - Studio Vitali description: Studio Ansible collection containing common roles -license: MIT +license: + - MIT From 16e403a4396df9b71c51593f540caeed69ac38fa Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Thu, 23 Jul 2026 17:45:06 +0200 Subject: [PATCH 04/18] fix: use correct ssh daemon for debian --- roles/security/handlers/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/security/handlers/main.yml b/roles/security/handlers/main.yml index 6998953..cad74d8 100644 --- a/roles/security/handlers/main.yml +++ b/roles/security/handlers/main.yml @@ -1,5 +1,5 @@ --- - name: Restart sshd ansible.builtin.service: - name: sshd + name: ssh state: restarted From 960379bd167b56d90c217f5084af90b4ee6a056f Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Tue, 4 Aug 2026 19:34:56 +0200 Subject: [PATCH 05/18] refactor: docker role (#6) use official docker images instead of outdated apt packages following official docker installation docs --- roles/docker/tasks/main.yml | 53 ++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 3449ae3..9ef5e46 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -1,17 +1,55 @@ --- -- name: Install necessary Docker packages +- name: Install Docker prerequisites ansible.builtin.apt: name: - - docker.io - - docker-compose + - ca-certificates + - curl + state: present + update_cache: true + become: true + +- name: Ensure APT keyrings directory exists + ansible.builtin.file: + path: /etc/apt/keyrings + state: directory + mode: "0755" + become: true + +- name: Download Docker official GPG key + ansible.builtin.get_url: + url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg" + dest: /etc/apt/keyrings/docker.asc + mode: "0644" + become: true + +- name: Add Docker official APT repository + ansible.builtin.apt_repository: + repo: >- + deb [arch={{ ansible_pkg_arch }} signed-by=/etc/apt/keyrings/docker.asc] + https://download.docker.com/linux/{{ ansible_distribution | lower }} + {{ ansible_distribution_release }} stable + state: present + filename: docker + update_cache: true + become: true + +- name: Install modern Docker CE packages + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin state: present become: true -- name: Ensure Docker service is running +- name: Ensure Docker services are running and enabled ansible.builtin.service: name: docker state: started enabled: true + become: true - name: Create docker user for container execution ansible.builtin.user: @@ -24,6 +62,13 @@ create_home: false become: true +- name: Add deployment user to docker group + ansible.builtin.user: + name: deployment + groups: docker + append: true + become: true + - name: Create target for docker compose ansible.builtin.file: path: /opt/docker From ff9d9adc96663f52a0b4fec617136c279f9b9273 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Tue, 4 Aug 2026 19:49:02 +0200 Subject: [PATCH 06/18] fix: arch discovery refactor repository steps following official docker installation steps --- roles/docker/tasks/main.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 9ef5e46..0f0e5d7 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -22,15 +22,18 @@ mode: "0644" become: true +- name: Get system architecture + ansible.builtin.command: dpkg --print-architecture + register: dpkg_architecture + changed_when: false + become: true + - name: Add Docker official APT repository ansible.builtin.apt_repository: - repo: >- - deb [arch={{ ansible_pkg_arch }} signed-by=/etc/apt/keyrings/docker.asc] - https://download.docker.com/linux/{{ ansible_distribution | lower }} - {{ ansible_distribution_release }} stable + repo: "deb [arch={{ dpkg_architecture.stdout }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable" state: present filename: docker - update_cache: true + update_cache: yes become: true - name: Install modern Docker CE packages From b6d861a8d48e3e87358f52d98090e73f625a4b6f Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Thu, 23 Jul 2026 17:28:11 +0200 Subject: [PATCH 07/18] fix: add runtime.yml with minimum supported ansible version --- meta/runtime.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 meta/runtime.yml diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..ce6befd --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,2 @@ +--- +requires_ansible: ">=2.14.0" From f9e26303d053161d2947e13b7619e004737b6a6c Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Tue, 4 Aug 2026 20:30:17 +0200 Subject: [PATCH 08/18] fix: docker container execution user permissions do not add appuser to docker group (highly insecure) / hardcode GID:UID --- roles/docker/tasks/main.yml | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 0f0e5d7..11e0869 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -54,17 +54,6 @@ enabled: true become: true -- name: Create docker user for container execution - ansible.builtin.user: - name: docker - state: present - system: true - shell: /usr/sbin/nologin - groups: docker - append: true - create_home: false - become: true - - name: Add deployment user to docker group ansible.builtin.user: name: deployment @@ -72,7 +61,7 @@ append: true become: true -- name: Create target for docker compose +- name: Create target for docker compose files ansible.builtin.file: path: /opt/docker state: directory @@ -80,3 +69,21 @@ group: deployment mode: "0755" become: true + +- name: Create appuser group + ansible.builtin.group: + name: appuser + state: present + gid: 9999 + become: true + +- name: Create appuser user for secure container execution + ansible.builtin.user: + name: appuser + state: present + uid: 9999 + group: appuser + system: true + shell: /usr/sbin/nologin + create_home: false + become: true From f59952df2b0113aafcf178c2df2d2635ba85771a Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Tue, 4 Aug 2026 21:59:47 +0200 Subject: [PATCH 09/18] fix: update outdated ansible_facts notation --- roles/docker/tasks/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 11e0869..bafc708 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -17,7 +17,7 @@ - name: Download Docker official GPG key ansible.builtin.get_url: - url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg" + url: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg" dest: /etc/apt/keyrings/docker.asc mode: "0644" become: true @@ -30,7 +30,7 @@ - name: Add Docker official APT repository ansible.builtin.apt_repository: - repo: "deb [arch={{ dpkg_architecture.stdout }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable" + repo: "deb [arch={{ dpkg_architecture.stdout }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }} {{ ansible_facts['distribution_release'] }} stable" state: present filename: docker update_cache: yes From bdd1c07d76ff6fe1ee1c01ee89b55bab64d69eff Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Tue, 4 Aug 2026 22:12:05 +0200 Subject: [PATCH 10/18] refactor: use modern deb822 repository module --- meta/runtime.yml | 2 +- roles/docker/tasks/main.yml | 36 ++++++++++-------------------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/meta/runtime.yml b/meta/runtime.yml index ce6befd..1e85b01 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,2 +1,2 @@ --- -requires_ansible: ">=2.14.0" +requires_ansible: ">=2.15.0" diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index bafc708..1b20aea 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -4,36 +4,20 @@ name: - ca-certificates - curl + - python3-debian ## needed for the deb822 module state: present update_cache: true become: true -- name: Ensure APT keyrings directory exists - ansible.builtin.file: - path: /etc/apt/keyrings - state: directory - mode: "0755" - become: true - -- name: Download Docker official GPG key - ansible.builtin.get_url: - url: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg" - dest: /etc/apt/keyrings/docker.asc - mode: "0644" - become: true - -- name: Get system architecture - ansible.builtin.command: dpkg --print-architecture - register: dpkg_architecture - changed_when: false - become: true - -- name: Add Docker official APT repository - ansible.builtin.apt_repository: - repo: "deb [arch={{ dpkg_architecture.stdout }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }} {{ ansible_facts['distribution_release'] }} stable" +- name: Add Docker official APT repository (DEB822 format) + ansible.builtin.deb822_repository: + name: docker + types: deb + uris: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}" + suites: "{{ ansible_facts['distribution_release'] }}" + components: stable + signed_by: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg" state: present - filename: docker - update_cache: yes become: true - name: Install modern Docker CE packages @@ -77,7 +61,7 @@ gid: 9999 become: true -- name: Create appuser user for secure container execution +- name: Create app user for secure container execution ansible.builtin.user: name: appuser state: present From 7c9d7050e99cefab8385bb23a7206cc54f3eee92 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Wed, 5 Aug 2026 00:29:12 +0200 Subject: [PATCH 11/18] fix: refresh apt cache before installing docker packages --- roles/docker/tasks/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 1b20aea..47f10e7 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -20,7 +20,7 @@ state: present become: true -- name: Install modern Docker CE packages +- name: Install Docker CE packages ansible.builtin.apt: name: - docker-ce @@ -29,9 +29,10 @@ - docker-buildx-plugin - docker-compose-plugin state: present + update_cache: true become: true -- name: Ensure Docker services are running and enabled +- name: Ensure Docker service is running and enabled ansible.builtin.service: name: docker state: started From 1370cd2b8a8b4bb3858eed8d7981e0b16d8fee4e Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Wed, 5 Aug 2026 16:24:11 +0200 Subject: [PATCH 12/18] refactor: user creation (#7) parameterize sysadmin username --- roles/users/tasks/main.yml | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index 6628891..b5cffaa 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -1,27 +1,27 @@ --- -# - name: Create primary admin user -# ansible.builtin.user: -# name: sysadmin -# groups: sudo -# append: yes -# shell: /bin/bash -# create_home: yes -# become: true +- name: Create primary sysadmin user + ansible.builtin.user: + name: "{{ sysadmin_user }}" + groups: sudo + append: yes + shell: /bin/bash + create_home: yes + become: true -# - name: Add SSH public key for sysadmin -# ansible.posix.authorized_key: -# user: sysadmin -# state: present -# key: "{{ sysadmin_public_ssh_key }}" -# become: true +- name: Add SSH public key for sysadmin + ansible.posix.authorized_key: + user: "{{ sysadmin_user }}" + state: present + key: "{{ sysadmin_public_ssh_key }}" + become: true -# - name: Allow passwordless sudo to sysadmin user -# ansible.builtin.copy: -# dest: /etc/sudoers.d/sysadmin -# content: "sysadmin ALL=(ALL) NOPASSWD: ALL" -# mode: "0440" -# validate: /usr/sbin/visudo -cf %s -# become: true +- name: Allow passwordless sudo for sudo group + ansible.builtin.copy: + dest: /etc/sudoers.d/sudo-nopasswd + content: "%sudo ALL=(ALL) NOPASSWD: ALL" + mode: "0440" + validate: /usr/sbin/visudo -cf %s + become: true - name: Create deployment user for CI/CD ansible.builtin.user: From b031c08b66451b568f0df2373b7ab69abe06b818 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Thu, 6 Aug 2026 12:39:58 +0200 Subject: [PATCH 13/18] fix: SSH config validation remove validation step instead of running pointless validation on static template --- roles/security/tasks/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/security/tasks/main.yml b/roles/security/tasks/main.yml index 956a63f..9b9a0fe 100644 --- a/roles/security/tasks/main.yml +++ b/roles/security/tasks/main.yml @@ -15,7 +15,6 @@ owner: root group: root mode: "0644" - validate: "sshd -t -f %s" notify: Restart sshd become: true From 80a1ca06327cff44f5d3af105d58ce64619c9917 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Mon, 17 Aug 2026 00:05:30 +0200 Subject: [PATCH 14/18] chore --- roles/base/tasks/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 7849c82..9535d89 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -35,5 +35,3 @@ # APT::Periodic::Unattended-Upgrade "1"; # mode: "0644" # when: ansible_facts['os_family'] == "Debian" - -## SCHEDULE REBOOTS 4 kernel upgrades: with cron, unattended-upgrades or action workflows? From d1714e0fa44fd9b26fd074c143da9552773b1393 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Mon, 17 Aug 2026 00:08:39 +0200 Subject: [PATCH 15/18] feat: add conditionals to user creation --- roles/users/tasks/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index b5cffaa..1cc19d7 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -29,6 +29,7 @@ shell: /bin/bash create_home: yes become: true + when: deployment_public_ssh_key is defined - name: Add SSH public key for deployment user ansible.posix.authorized_key: @@ -36,3 +37,4 @@ state: present key: "{{ deployment_public_ssh_key }}" become: true + when: deployment_public_ssh_key is defined From 6e4bd8eb925e3f07d74ac3e78629b80c0fc1725e Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Mon, 17 Aug 2026 14:18:24 +0200 Subject: [PATCH 16/18] feat: group tasks into logical blocks --- roles/docker/tasks/main.yml | 127 +++++++++++++++++----------------- roles/security/tasks/main.yml | 57 +++++++-------- roles/users/tasks/main.yml | 74 ++++++++++---------- 3 files changed, 132 insertions(+), 126 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 47f10e7..db50312 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -1,74 +1,75 @@ --- -- name: Install Docker prerequisites - ansible.builtin.apt: - name: - - ca-certificates - - curl - - python3-debian ## needed for the deb822 module - state: present - update_cache: true +- name: Install Docker become: true + block: + - name: Install Docker prerequisites + ansible.builtin.apt: + name: + - ca-certificates + - curl + - python3-debian ## needed for the deb822 module + state: present + update_cache: true -- name: Add Docker official APT repository (DEB822 format) - ansible.builtin.deb822_repository: - name: docker - types: deb - uris: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}" - suites: "{{ ansible_facts['distribution_release'] }}" - components: stable - signed_by: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg" - state: present - become: true + - name: Add Docker official APT repository (DEB822 format) + ansible.builtin.deb822_repository: + name: docker + types: deb + uris: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}" + suites: "{{ ansible_facts['distribution_release'] }}" + components: stable + signed_by: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg" + state: present -- name: Install Docker CE packages - ansible.builtin.apt: - name: - - docker-ce - - docker-ce-cli - - containerd.io - - docker-buildx-plugin - - docker-compose-plugin - state: present - update_cache: true - become: true + - name: Install Docker CE packages + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + state: present + update_cache: true -- name: Ensure Docker service is running and enabled - ansible.builtin.service: - name: docker - state: started - enabled: true - become: true + - name: Ensure Docker service is running and enabled + ansible.builtin.service: + name: docker + state: started + enabled: true -- name: Add deployment user to docker group - ansible.builtin.user: - name: deployment - groups: docker - append: true +- name: Configure deployment user for Docker become: true + block: + - name: Add user to docker group + ansible.builtin.user: + name: deployment + groups: docker + append: true -- name: Create target for docker compose files - ansible.builtin.file: - path: /opt/docker - state: directory - owner: deployment - group: deployment - mode: "0755" - become: true - -- name: Create appuser group - ansible.builtin.group: - name: appuser - state: present - gid: 9999 - become: true + - name: Create target for docker compose + ansible.builtin.file: + path: /opt/docker + state: directory + owner: deployment + group: deployment + mode: "0755" - name: Create app user for secure container execution - ansible.builtin.user: - name: appuser - state: present - uid: 9999 - group: appuser - system: true - shell: /usr/sbin/nologin - create_home: false become: true + block: + - name: Create appuser group + ansible.builtin.group: + name: appuser + state: present + gid: 9999 + + - name: Create user + ansible.builtin.user: + name: appuser + state: present + uid: 9999 + group: appuser + system: true + shell: /usr/sbin/nologin + create_home: false diff --git a/roles/security/tasks/main.yml b/roles/security/tasks/main.yml index 9b9a0fe..5d4c252 100644 --- a/roles/security/tasks/main.yml +++ b/roles/security/tasks/main.yml @@ -1,32 +1,35 @@ --- -- name: Create SSH drop-in directory - ansible.builtin.file: - path: /etc/ssh/sshd_config.d - state: directory - owner: root - group: root - mode: "0755" +- name: Harden SSH configuration become: true + block: + - name: Create SSH drop-in directory + ansible.builtin.file: + path: /etc/ssh/sshd_config.d + state: directory + owner: root + group: root + mode: "0755" -- name: Harden SSH configuration with drop-in template - ansible.builtin.template: - src: sshd.conf.j2 - dest: /etc/ssh/sshd_config.d/sshd.conf - owner: root - group: root - mode: "0644" - notify: Restart sshd + - name: Harden SSH configuration with drop-in template + ansible.builtin.template: + src: sshd.conf.j2 + dest: /etc/ssh/sshd_config.d/sshd.conf + owner: root + group: root + mode: "0644" + notify: Restart sshd + +- name: Enable fail2ban become: true + block: + - name: Install fail2ban + ansible.builtin.apt: + name: + - fail2ban + state: present -- name: Install fail2ban - ansible.builtin.apt: - name: - - fail2ban - state: present - become: true - -- name: Start fail2ban - ansible.builtin.service: - name: fail2ban - state: started - enabled: yes + - name: Start fail2ban + ansible.builtin.service: + name: fail2ban + state: started + enabled: yes diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index 1cc19d7..2d58ad4 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -1,40 +1,42 @@ --- -- name: Create primary sysadmin user - ansible.builtin.user: - name: "{{ sysadmin_user }}" - groups: sudo - append: yes - shell: /bin/bash - create_home: yes - become: true - -- name: Add SSH public key for sysadmin - ansible.posix.authorized_key: - user: "{{ sysadmin_user }}" - state: present - key: "{{ sysadmin_public_ssh_key }}" - become: true - -- name: Allow passwordless sudo for sudo group - ansible.builtin.copy: - dest: /etc/sudoers.d/sudo-nopasswd - content: "%sudo ALL=(ALL) NOPASSWD: ALL" - mode: "0440" - validate: /usr/sbin/visudo -cf %s - become: true - -- name: Create deployment user for CI/CD - ansible.builtin.user: - name: deployment - shell: /bin/bash - create_home: yes - become: true +- name: Set up primary sysadmin user when: deployment_public_ssh_key is defined - -- name: Add SSH public key for deployment user - ansible.posix.authorized_key: - user: deployment - state: present - key: "{{ deployment_public_ssh_key }}" become: true + block: + - name: Create user + ansible.builtin.user: + name: "{{ sysadmin_user }}" + groups: sudo + append: yes + shell: /bin/bash + create_home: yes + + - name: Add SSH public key + ansible.posix.authorized_key: + user: "{{ sysadmin_user }}" + state: present + key: "{{ sysadmin_public_ssh_key }}" + + - name: Allow passwordless sudo + ansible.builtin.copy: + dest: /etc/sudoers.d/sudo-nopasswd + content: "%sudo ALL=(ALL) NOPASSWD: ALL" + mode: "0440" + validate: /usr/sbin/visudo -cf %s + + +- name: Set up deployment user for CI/CD when: deployment_public_ssh_key is defined + become: true + block: + - name: Create user + ansible.builtin.user: + name: deployment + shell: /bin/bash + create_home: yes + + - name: Add SSH public key + ansible.posix.authorized_key: + user: deployment + state: present + key: "{{ deployment_public_ssh_key }}" From 0974c13751e3d6d6eafd77702a1dc30372223fe0 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Mon, 17 Aug 2026 17:43:05 +0200 Subject: [PATCH 17/18] chore: add conditional to user configuration --- roles/docker/tasks/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index db50312..09229d9 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -39,6 +39,7 @@ enabled: true - name: Configure deployment user for Docker + when: deployment_public_ssh_key is defined become: true block: - name: Add user to docker group From cba507b077d21b35fa6a15f10abe43d590c663c7 Mon Sep 17 00:00:00 2001 From: alessandrovitali Date: Wed, 19 Aug 2026 19:42:55 +0200 Subject: [PATCH 18/18] fix: user creation --- roles/docker/tasks/main.yml | 4 ++-- roles/users/tasks/main.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index 09229d9..a1f1d05 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -42,7 +42,7 @@ when: deployment_public_ssh_key is defined become: true block: - - name: Add user to docker group + - name: Add deployment user to docker group ansible.builtin.user: name: deployment groups: docker @@ -65,7 +65,7 @@ state: present gid: 9999 - - name: Create user + - name: Create appuser ansible.builtin.user: name: appuser state: present diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index 2d58ad4..e416529 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -1,9 +1,9 @@ --- - name: Set up primary sysadmin user - when: deployment_public_ssh_key is defined + when: sysadmin_public_ssh_key is defined become: true block: - - name: Create user + - name: Create sysadmin user ansible.builtin.user: name: "{{ sysadmin_user }}" groups: sudo @@ -29,7 +29,7 @@ when: deployment_public_ssh_key is defined become: true block: - - name: Create user + - name: Create deployment user ansible.builtin.user: name: deployment shell: /bin/bash