From 8cc746fbfae96553d0800fc5b089cd0ddef5bfee Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Mon, 8 Dec 2025 12:20:01 +0400 Subject: [PATCH 01/37] Migrate the `genlab.mkfs` role --- roles/mkfs/README.md | 40 ++++++++++++++++++++++++ roles/mkfs/defaults/main.yml | 5 +++ roles/mkfs/handlers/main.yml | 1 + roles/mkfs/meta/main.yml | 17 ++++++++++ roles/mkfs/molecule/default/converge.yml | 22 +++++++++++++ roles/mkfs/molecule/default/molecule.yml | 27 ++++++++++++++++ roles/mkfs/molecule/default/verify.yml | 25 +++++++++++++++ roles/mkfs/tasks/main.yml | 8 +++++ roles/mkfs/vars/main.yml | 1 + 9 files changed, 146 insertions(+) create mode 100644 roles/mkfs/README.md create mode 100644 roles/mkfs/defaults/main.yml create mode 100644 roles/mkfs/handlers/main.yml create mode 100644 roles/mkfs/meta/main.yml create mode 100644 roles/mkfs/molecule/default/converge.yml create mode 100644 roles/mkfs/molecule/default/molecule.yml create mode 100644 roles/mkfs/molecule/default/verify.yml create mode 100644 roles/mkfs/tasks/main.yml create mode 100644 roles/mkfs/vars/main.yml diff --git a/roles/mkfs/README.md b/roles/mkfs/README.md new file mode 100644 index 0000000..cfa7a9b --- /dev/null +++ b/roles/mkfs/README.md @@ -0,0 +1,40 @@ +Role Name +========= + +Create a filesystem on the target device (thinly wraps `community.general.filesystem` module). + +Requirements +------------ + +Uses specific tools related to the fstype for creating or resizing a filesystem (`e2fsprogs`, `xfsprogs`, etc.). + +Uses generic tools mostly related to the OS, like `blkid`. + +Role Variables +-------------- + +- `mkfs_device`: (string) device that the filesystem should be created on +- `mkfs_type`: (string) type of the filesystem to be created +- `mkfs_opts`: (string) a list of options to be passed to `mkfs` (*not* a YAML array) +- `mkfs_state`: (string) `absent` or `present` +- `mkfs_force`: (boolean) overwrite an existing fs if there is one (default: `false`) + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See: [converge.yml](molecule/default/converge.yml) + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/mkfs/defaults/main.yml b/roles/mkfs/defaults/main.yml new file mode 100644 index 0000000..3525a8d --- /dev/null +++ b/roles/mkfs/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for mkfs +mkfs_opts: "" +mkfs_state: present +mkfs_force: false diff --git a/roles/mkfs/handlers/main.yml b/roles/mkfs/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/mkfs/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/mkfs/meta/main.yml b/roles/mkfs/meta/main.yml new file mode 100644 index 0000000..002fbb6 --- /dev/null +++ b/roles/mkfs/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "mkfs" + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + namespace: genlab + description: "Create a filesystem on a target device" + license: "GPL-2.0-or-later" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "jammy", "noble" ] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/mkfs/molecule/default/converge.yml b/roles/mkfs/molecule/default/converge.yml new file mode 100644 index 0000000..aeadc57 --- /dev/null +++ b/roles/mkfs/molecule/default/converge.yml @@ -0,0 +1,22 @@ +--- +- name: Converge + hosts: all + + pre_tasks: + + - name: "Check if the disk file already exists" + ansible.builtin.stat: + path: /tmp/test_file + register: disk_file + + - name: "Create a file to act as a disk if it doesn't exist" + ansible.builtin.command: dd if=/dev/zero of=/tmp/test_file bs=1000000 count=100 + when: not disk_file.stat.exists + register: dd_output + changed_when: dd_output.rc != 0 + + roles: + - role: genlab.mkfs + mkfs_device: "/tmp/test_file" + mkfs_type: ext3 + mkfs_state: present diff --git a/roles/mkfs/molecule/default/molecule.yml b/roles/mkfs/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/mkfs/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/mkfs/molecule/default/verify.yml b/roles/mkfs/molecule/default/verify.yml new file mode 100644 index 0000000..000a70d --- /dev/null +++ b/roles/mkfs/molecule/default/verify.yml @@ -0,0 +1,25 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Attach the file to a loop device" + ansible.builtin.command: losetup /dev/loop3 /tmp/test_file + register: loop_device + changed_when: loop_device.rc != 0 + + - name: "Verify the filesystem type" + ansible.builtin.command: blkid /dev/loop3 + register: blkid_output + changed_when: blkid_output.rc != 0 + + - name: "Ensure filesystem is ext3" + ansible.builtin.assert: + that: + - "'TYPE=\"ext3\"' in blkid_output.stdout" + + - name: "Explore stdout" + ansible.builtin.debug: + var: blkid_output.stdout diff --git a/roles/mkfs/tasks/main.yml b/roles/mkfs/tasks/main.yml new file mode 100644 index 0000000..d3c468f --- /dev/null +++ b/roles/mkfs/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- name: "Create a filesystem on the given device" + community.general.filesystem: + fstype: "{{ mkfs_type }}" + dev: "{{ mkfs_device }}" + state: "{{ mkfs_state }}" + opts: "{{ mkfs_opts }}" + force: "{{ mkfs_force }}" diff --git a/roles/mkfs/vars/main.yml b/roles/mkfs/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/mkfs/vars/main.yml @@ -0,0 +1 @@ +--- From 60569899490e83dacc9e2dc7b61145ca558aea22 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Mon, 8 Dec 2025 12:24:35 +0400 Subject: [PATCH 02/37] Touch another role to test Molecule workflow behavior with multiple roles --- roles/mount_device/requirements.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 roles/mount_device/requirements.yml diff --git a/roles/mount_device/requirements.yml b/roles/mount_device/requirements.yml deleted file mode 100644 index 3d5f1cd..0000000 --- a/roles/mount_device/requirements.yml +++ /dev/null @@ -1,3 +0,0 @@ -# requirements file ---- -collections: [] From dce2de214599c7e3c7d725e0c69e80db993a8fc9 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:26:46 +0400 Subject: [PATCH 03/37] Add the `node_exporter` role --- roles/node_exporter/README.md | 34 +++++++++++++++++ roles/node_exporter/defaults/main.yml | 2 + roles/node_exporter/handlers/main.yml | 1 + roles/node_exporter/meta/main.yml | 17 +++++++++ .../molecule/default/converge.yml | 6 +++ .../molecule/default/molecule.yml | 27 ++++++++++++++ .../node_exporter/molecule/default/verify.yml | 15 ++++++++ roles/node_exporter/tasks/install.yml | 37 +++++++++++++++++++ roles/node_exporter/tasks/main.yml | 6 +++ roles/node_exporter/tasks/run.yml | 8 ++++ .../templates/node_exporter.service.j2 | 12 ++++++ roles/node_exporter/vars/main.yml | 1 + 12 files changed, 166 insertions(+) create mode 100644 roles/node_exporter/README.md create mode 100644 roles/node_exporter/defaults/main.yml create mode 100644 roles/node_exporter/handlers/main.yml create mode 100644 roles/node_exporter/meta/main.yml create mode 100644 roles/node_exporter/molecule/default/converge.yml create mode 100644 roles/node_exporter/molecule/default/molecule.yml create mode 100644 roles/node_exporter/molecule/default/verify.yml create mode 100644 roles/node_exporter/tasks/install.yml create mode 100644 roles/node_exporter/tasks/main.yml create mode 100644 roles/node_exporter/tasks/run.yml create mode 100644 roles/node_exporter/templates/node_exporter.service.j2 create mode 100644 roles/node_exporter/vars/main.yml diff --git a/roles/node_exporter/README.md b/roles/node_exporter/README.md new file mode 100644 index 0000000..0ff7a86 --- /dev/null +++ b/roles/node_exporter/README.md @@ -0,0 +1,34 @@ +Role Name +========= + +Deploy `prometheus/node_exporter` binary as a systemd unit. + +Requirements +------------ + +None + +Role Variables +-------------- + +`node_exporter_bin_path`: where to install the binary (default: `/usr/bin`). + +Dependencies +------------ + +None + +Example Playbook +---------------- + +See: [converge.yml](molecule/default/converge.yml) + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/node_exporter/defaults/main.yml b/roles/node_exporter/defaults/main.yml new file mode 100644 index 0000000..daf4778 --- /dev/null +++ b/roles/node_exporter/defaults/main.yml @@ -0,0 +1,2 @@ +--- +node_exporter_bin_path: /usr/bin diff --git a/roles/node_exporter/handlers/main.yml b/roles/node_exporter/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/node_exporter/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/node_exporter/meta/main.yml b/roles/node_exporter/meta/main.yml new file mode 100644 index 0000000..885b94c --- /dev/null +++ b/roles/node_exporter/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: node_exporter + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Deploy prometheus/node_exporter binary as a systemd unit" + license: "GPL-2.0-or-later" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy"] + + galaxy_tags: ["prometheus"] + +dependencies: [] diff --git a/roles/node_exporter/molecule/default/converge.yml b/roles/node_exporter/molecule/default/converge.yml new file mode 100644 index 0000000..e0158f0 --- /dev/null +++ b/roles/node_exporter/molecule/default/converge.yml @@ -0,0 +1,6 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.node_exporter + node_exporter_ver: "1.10.1" diff --git a/roles/node_exporter/molecule/default/molecule.yml b/roles/node_exporter/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/node_exporter/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml new file mode 100644 index 0000000..b923a42 --- /dev/null +++ b/roles/node_exporter/molecule/default/verify.yml @@ -0,0 +1,15 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + tasks: + + - name: "Sanity check the node exporter" + retries: 5 + delay: 1 + register: result + failed_when: '"node_exporter_build_info" not in result.content' + ansible.builtin.uri: + url: "http://localhost:9100/metrics" + return_content: true diff --git a/roles/node_exporter/tasks/install.yml b/roles/node_exporter/tasks/install.yml new file mode 100644 index 0000000..7a0ab83 --- /dev/null +++ b/roles/node_exporter/tasks/install.yml @@ -0,0 +1,37 @@ +--- +- name: "Install | Check if node_exporter is accessible from $PATH" + changed_when: false + failed_when: false + register: cmd_node_exporter + ansible.builtin.command: + cmd: node_exporter --version + +- name: "Install | Build the exporter name" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + ansible.builtin.set_fact: + prom_exp_name: "/node_exporter-{{ node_exporter_ver }}\ + .{{ ansible_system | lower }}-\ + {{ (ansible_architecture == 'x86_64') | ternary('amd64', ansible_architecture) }}" + +- name: "Install | Download the node_exporter binary (release {{ node_exporter_ver }})" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + ansible.builtin.unarchive: + src: "\ + https://github.com/prometheus/node_exporter/releases/download/\ + v{{ node_exporter_ver }}\ + {{ prom_exp_name }}\ + .tar.gz" + dest: /tmp + remote_src: true + +- name: "Install | Install the downloaded binary to '{{ node_exporter_bin_path }}'" + when: "node_exporter_ver not in cmd_node_exporter.stdout" + changed_when: false + ansible.builtin.command: + cmd: install /tmp/{{ prom_exp_name }}/node_exporter -t {{ node_exporter_bin_path }} + +- name: "Install | Push the service file template" + ansible.builtin.template: + src: node_exporter.service.j2 + dest: /usr/lib/systemd/system/node_exporter.service + mode: "0660" diff --git a/roles/node_exporter/tasks/main.yml b/roles/node_exporter/tasks/main.yml new file mode 100644 index 0000000..b63f5a3 --- /dev/null +++ b/roles/node_exporter/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: "Include installation tasks" + ansible.builtin.include_tasks: "install.yml" + +- name: "Including run tasks" + ansible.builtin.include_tasks: "run.yml" diff --git a/roles/node_exporter/tasks/run.yml b/roles/node_exporter/tasks/run.yml new file mode 100644 index 0000000..2d71c2e --- /dev/null +++ b/roles/node_exporter/tasks/run.yml @@ -0,0 +1,8 @@ +--- +- name: "Enable and start the node_exporter service" + changed_when: false + ansible.builtin.systemd: + name: node_exporter + enabled: true + daemon_reload: true + state: restarted diff --git a/roles/node_exporter/templates/node_exporter.service.j2 b/roles/node_exporter/templates/node_exporter.service.j2 new file mode 100644 index 0000000..414c5c6 --- /dev/null +++ b/roles/node_exporter/templates/node_exporter.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Node Exporter +After=network.target + +[Service] +Type=simple +ExecStart={{ node_exporter_bin_path }}/node_exporter \ + --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/) \ + --collector.systemd + +[Install] +WantedBy=multi-user.target diff --git a/roles/node_exporter/vars/main.yml b/roles/node_exporter/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/node_exporter/vars/main.yml @@ -0,0 +1 @@ +--- From 29ca89ed93ec834589b0ea66c3d44e077910e0c1 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:38:30 +0400 Subject: [PATCH 04/37] Disable an irrelevant KICS warning about unsafe URLs --- roles/node_exporter/molecule/default/verify.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index b923a42..e04b1a0 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -1,10 +1,11 @@ +# kics-scan-disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 --- - name: Verify hosts: all gather_facts: false any_errors_fatal: true + tasks: - - name: "Sanity check the node exporter" retries: 5 delay: 1 From 3ea70738962425ec345b4189735a6199512031bb Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 12:38:44 +0400 Subject: [PATCH 05/37] Rename variables according to the convention --- roles/node_exporter/tasks/install.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/node_exporter/tasks/install.yml b/roles/node_exporter/tasks/install.yml index 7a0ab83..aaff138 100644 --- a/roles/node_exporter/tasks/install.yml +++ b/roles/node_exporter/tasks/install.yml @@ -2,33 +2,33 @@ - name: "Install | Check if node_exporter is accessible from $PATH" changed_when: false failed_when: false - register: cmd_node_exporter + register: node_exporter_cmd ansible.builtin.command: cmd: node_exporter --version - name: "Install | Build the exporter name" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" ansible.builtin.set_fact: - prom_exp_name: "/node_exporter-{{ node_exporter_ver }}\ + node_exporter_name: "/node_exporter-{{ node_exporter_ver }}\ .{{ ansible_system | lower }}-\ {{ (ansible_architecture == 'x86_64') | ternary('amd64', ansible_architecture) }}" - name: "Install | Download the node_exporter binary (release {{ node_exporter_ver }})" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" ansible.builtin.unarchive: src: "\ https://github.com/prometheus/node_exporter/releases/download/\ v{{ node_exporter_ver }}\ - {{ prom_exp_name }}\ + {{ node_exporter_name }}\ .tar.gz" dest: /tmp remote_src: true - name: "Install | Install the downloaded binary to '{{ node_exporter_bin_path }}'" - when: "node_exporter_ver not in cmd_node_exporter.stdout" + when: "node_exporter_ver not in node_exporter_cmd.stdout" changed_when: false ansible.builtin.command: - cmd: install /tmp/{{ prom_exp_name }}/node_exporter -t {{ node_exporter_bin_path }} + cmd: install /tmp/{{ node_exporter_name }}/node_exporter -t {{ node_exporter_bin_path }} - name: "Install | Push the service file template" ansible.builtin.template: From c6f604fcc65a58c0cb4c377002c71430645d2e26 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 13:47:47 +0400 Subject: [PATCH 06/37] Add more linting fixes --- roles/node_exporter/molecule/default/verify.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index e04b1a0..5687a6d 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -4,13 +4,13 @@ hosts: all gather_facts: false any_errors_fatal: true - + tasks: - name: "Sanity check the node exporter" retries: 5 delay: 1 - register: result - failed_when: '"node_exporter_build_info" not in result.content' + register: node_exporter_check + failed_when: '"node_exporter_build_info" not in node_exporter_check.content' ansible.builtin.uri: url: "http://localhost:9100/metrics" return_content: true From a5b6f0ff75520b115f11cdf329c469552bfef326 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Wed, 17 Dec 2025 13:48:15 +0400 Subject: [PATCH 07/37] Fix KICS rule disabling syntax --- roles/node_exporter/molecule/default/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/node_exporter/molecule/default/verify.yml b/roles/node_exporter/molecule/default/verify.yml index 5687a6d..3f1bb0b 100644 --- a/roles/node_exporter/molecule/default/verify.yml +++ b/roles/node_exporter/molecule/default/verify.yml @@ -1,4 +1,4 @@ -# kics-scan-disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 +# kics-scan disable=2e8d4922-8362-4606-8c14-aa10466a1ce3 --- - name: Verify hosts: all From 5b6ecbd32d2a1aeaca788e2ec4f86ef14632d9b2 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 17:58:14 +0300 Subject: [PATCH 08/37] add promtail role --- roles/promtail/.ansible-lint | 16 +++ roles/promtail/.gitignore | 2 + roles/promtail/.yamllint | 8 ++ roles/promtail/README.md | 44 ++++++++ roles/promtail/defaults/main.yml | 7 ++ roles/promtail/handlers/main.yml | 11 ++ roles/promtail/meta/main.yml | 14 +++ roles/promtail/molecule/default/converge.yml | 5 + roles/promtail/molecule/default/molecule.yml | 24 +++++ roles/promtail/molecule/default/verify.yml | 19 ++++ roles/promtail/renovate.json | 6 ++ roles/promtail/requirements.ci.txt | 5 + roles/promtail/tasks/main.yml | 107 +++++++++++++++++++ roles/promtail/templates/promtail.service.j2 | 11 ++ roles/promtail/templates/promtail.yml.j2 | 28 +++++ roles/promtail/vars/main.yml | 1 + 16 files changed, 308 insertions(+) create mode 100644 roles/promtail/.ansible-lint create mode 100644 roles/promtail/.gitignore create mode 100644 roles/promtail/.yamllint create mode 100644 roles/promtail/README.md create mode 100644 roles/promtail/defaults/main.yml create mode 100644 roles/promtail/handlers/main.yml create mode 100644 roles/promtail/meta/main.yml create mode 100644 roles/promtail/molecule/default/converge.yml create mode 100644 roles/promtail/molecule/default/molecule.yml create mode 100644 roles/promtail/molecule/default/verify.yml create mode 100644 roles/promtail/renovate.json create mode 100644 roles/promtail/requirements.ci.txt create mode 100644 roles/promtail/tasks/main.yml create mode 100644 roles/promtail/templates/promtail.service.j2 create mode 100644 roles/promtail/templates/promtail.yml.j2 create mode 100644 roles/promtail/vars/main.yml diff --git a/roles/promtail/.ansible-lint b/roles/promtail/.ansible-lint new file mode 100644 index 0000000..f865478 --- /dev/null +++ b/roles/promtail/.ansible-lint @@ -0,0 +1,16 @@ +--- +profile: production +strict: true + +# Enable checking of loop variable prefixes in roles +loop_var_prefix: "^(__|{role}_)" + +skip_list: + - var-naming[no-role-prefix] + +warn_list: + - role-name[path] + - var-naming[no-role-prefix] + +exclude_paths: + - .github/ diff --git a/roles/promtail/.gitignore b/roles/promtail/.gitignore new file mode 100644 index 0000000..8cd0df3 --- /dev/null +++ b/roles/promtail/.gitignore @@ -0,0 +1,2 @@ +.vscode +.idea \ No newline at end of file diff --git a/roles/promtail/.yamllint b/roles/promtail/.yamllint new file mode 100644 index 0000000..611db54 --- /dev/null +++ b/roles/promtail/.yamllint @@ -0,0 +1,8 @@ +--- +rules: + brackets: + forbid: false + min-spaces-inside: 0 + max-spaces-inside: 2 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: 2 diff --git a/roles/promtail/README.md b/roles/promtail/README.md new file mode 100644 index 0000000..ee40a4f --- /dev/null +++ b/roles/promtail/README.md @@ -0,0 +1,44 @@ +promtail +========= + +Installs promtail as systemd service. + +Requirements +------------ + +Role Variables +-------------- + +(all optional) +`promtail_version`: version to install +`loki_server`: set loki server +`loki_port`: set loki port +`custom_server_config`: path to custom config witch replace all config with your own +`custom_scrape_configs`: path to custom scrape configs + +Dependencies +------------ + +No + +Example Playbook +---------------- + + - hosts: servers + roles: + - role: promtail + promtail_version: 2.7.3 + +License +------- + +MIT + +Author Information +------------------ + +Alexander Gorelyshev and Danilkin Danila (MIPT) + +Genlab LLC + +corvus-migratorius@proton.me diff --git a/roles/promtail/defaults/main.yml b/roles/promtail/defaults/main.yml new file mode 100644 index 0000000..5a64778 --- /dev/null +++ b/roles/promtail/defaults/main.yml @@ -0,0 +1,7 @@ +--- +promtail_version: 2.7.3 +promtail_positions_path: /home/promtail/positions.yaml +promtail_http_port: 9080 +loki_server: localhost +loki_port: 3100 +add_var_logs: true diff --git a/roles/promtail/handlers/main.yml b/roles/promtail/handlers/main.yml new file mode 100644 index 0000000..d1c06ac --- /dev/null +++ b/roles/promtail/handlers/main.yml @@ -0,0 +1,11 @@ +--- + +- name: "Restart the Promtail daemon" + ansible.builtin.systemd: + name: promtail + state: restarted + + +- name: "Reload the Promtail daemon configuration" + ansible.builtin.systemd: + daemon_reload: true diff --git a/roles/promtail/meta/main.yml b/roles/promtail/meta/main.yml new file mode 100644 index 0000000..79d6be3 --- /dev/null +++ b/roles/promtail/meta/main.yml @@ -0,0 +1,14 @@ +galaxy_info: + role_name: promtail + namespace: genlab + author: Danilkin Danila + description: Installs Promtail + + company: Genlab LLC + license: MIT + + min_ansible_version: "2.1" + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/promtail/molecule/default/converge.yml b/roles/promtail/molecule/default/converge.yml new file mode 100644 index 0000000..ea072dd --- /dev/null +++ b/roles/promtail/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.promtail diff --git a/roles/promtail/molecule/default/molecule.yml b/roles/promtail/molecule/default/molecule.yml new file mode 100644 index 0000000..8d39b18 --- /dev/null +++ b/roles/promtail/molecule/default/molecule.yml @@ -0,0 +1,24 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: ubuntu + image: geerlingguy/docker-ubuntu2204-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true +provisioner: + name: ansible + playbooks: + converge: converge.yml +verifier: + name: ansible +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml new file mode 100644 index 0000000..c9ef143 --- /dev/null +++ b/roles/promtail/molecule/default/verify.yml @@ -0,0 +1,19 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # https://github.com/ansible/molecule/issues/3587#issuecomment-1158650179 + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Sanity check the Promtail daemon" + retries: 3 + delay: 1 + register: this + failed_when: "this.content != 'Ready'" + ansible.builtin.uri: + url: "http://localhost:{{ promtail_http_port }}/ready" + return_content: true diff --git a/roles/promtail/renovate.json b/roles/promtail/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/roles/promtail/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} diff --git a/roles/promtail/requirements.ci.txt b/roles/promtail/requirements.ci.txt new file mode 100644 index 0000000..5eabc75 --- /dev/null +++ b/roles/promtail/requirements.ci.txt @@ -0,0 +1,5 @@ +ansible +molecule +molecule-plugins[docker] +docker +requests==2.31.0 # pinned to the latest version not breaking Docker SDK \ No newline at end of file diff --git a/roles/promtail/tasks/main.yml b/roles/promtail/tasks/main.yml new file mode 100644 index 0000000..ca1458b --- /dev/null +++ b/roles/promtail/tasks/main.yml @@ -0,0 +1,107 @@ +--- +- name: "Install packages" + ansible.builtin.apt: + name: [ acl, unzip ] + state: present + update_cache: true + cache_valid_time: 3600 + + +- name: "Create the 'promtail' group" + ansible.builtin.group: + name: promtail + state: present + + +- name: "Create the 'promtail' user" + ansible.builtin.user: + name: promtail + groups: + - promtail + - systemd-journal + - adm + state: present + system: true + + +- name: "Set facl to allow promtail user to read '/var/log' contents" + ansible.posix.acl: + path: /var/log + entity: promtail + etype: user + permissions: rX + state: present + + +- name: "Make promtail user owner of '{{ promtail_positions_path }}'" + changed_when: false + ansible.builtin.file: + path: "{{ promtail_positions_path }}" + owner: promtail + group: promtail + state: touch + mode: "0750" + + +- name: "Install the requested Promtail version" + block: + - name: "Check Promtail version" + ansible.builtin.command: /usr/bin/promtail --version + register: promtail_version_check + changed_when: false + + - name: "Assert version correctness" + notify: "Restart the Promtail daemon" + ansible.builtin.assert: + that: "promtail_version in promtail_version_check.stdout" + success_msg: "Expected Promtail version available ({{ promtail_version }})" + fail_msg: "Expected version '{{ promtail_version }}'; available is '{{ promtail_version_check.stdout }}'" + + rescue: + - name: "Install Promtail if its not present" + ansible.builtin.unarchive: + src: "https://github.com/grafana/loki/releases/download/v\ + {{ promtail_version }}/promtail-linux-amd64.zip" + dest: /usr/bin/ + remote_src: true + + - name: "Rename and set permissions for the Promtail binary" + ansible.builtin.copy: + src: "/usr/bin/promtail-linux-amd64" + dest: "/usr/bin/promtail" + owner: root + group: root + mode: "0755" + remote_src: true + + - name: "Cleanup the downloaded file" + ansible.builtin.file: + path: "/usr/bin/promtail-linux-amd64" + state: absent + + +- name: "Template the systemd unit file" + notify: "Reload the Promtail daemon configuration" + ansible.builtin.template: + src: promtail.service.j2 + dest: /etc/systemd/system/promtail.service + owner: root + group: root + mode: "0644" + + +- name: "Template Promtail config file" + notify: "Restart the Promtail daemon" + ansible.builtin.template: + src: templates/promtail.yml.j2 + dest: /usr/local/bin/config-promtail.yml + owner: root + group: root + mode: "0644" + + +- name: "Enable and start Promtail daemon" + ansible.builtin.systemd: + name: promtail + state: started + enabled: true diff --git a/roles/promtail/templates/promtail.service.j2 b/roles/promtail/templates/promtail.service.j2 new file mode 100644 index 0000000..3b7b517 --- /dev/null +++ b/roles/promtail/templates/promtail.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Promtail service +After=network.target + +[Service] +Type=simple +User=promtail +ExecStart=/usr/bin/promtail -config.file /usr/local/bin/config-promtail.yml + +[Install] +WantedBy=multi-user.target diff --git a/roles/promtail/templates/promtail.yml.j2 b/roles/promtail/templates/promtail.yml.j2 new file mode 100644 index 0000000..2435643 --- /dev/null +++ b/roles/promtail/templates/promtail.yml.j2 @@ -0,0 +1,28 @@ +{% if custom_server_config is defined %} +{{ custom_server_config }} +{% else %} +server: + http_listen_port: {{ promtail_http_port }} + grpc_listen_port: 0 +{% endif %} +positions: + filename: {{ promtail_positions_path }} + +clients: + - url: "http://{{ loki_server }}:{{ loki_port }}/loki/api/v1/push" + +scrape_configs: +{% if add_var_logs %} + - job_name: system + static_configs: + - targets: + - localhost + labels: + job: varlogs + __path__: /var/log/*log + host: {{ ansible_hostname }} +{% endif %} + +{% if custom_scrape_configs is defined %} +{{ custom_scrape_configs }} +{% endif %} diff --git a/roles/promtail/vars/main.yml b/roles/promtail/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/promtail/vars/main.yml @@ -0,0 +1 @@ +--- From d89bc09b92fc74a43e4da4d409e4c2340c37eb04 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:06:01 +0300 Subject: [PATCH 09/37] fix ansible-lint errors --- roles/promtail/README.md | 4 ++-- roles/promtail/defaults/main.yml | 6 +++--- roles/promtail/molecule/default/verify.yml | 6 +++--- roles/promtail/tasks/main.yml | 2 +- roles/promtail/templates/promtail.yml.j2 | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/roles/promtail/README.md b/roles/promtail/README.md index ee40a4f..7dac0b4 100644 --- a/roles/promtail/README.md +++ b/roles/promtail/README.md @@ -11,8 +11,8 @@ Role Variables (all optional) `promtail_version`: version to install -`loki_server`: set loki server -`loki_port`: set loki port +`promtail_loki_server`: set loki server +`promtail_loki_port`: set loki port `custom_server_config`: path to custom config witch replace all config with your own `custom_scrape_configs`: path to custom scrape configs diff --git a/roles/promtail/defaults/main.yml b/roles/promtail/defaults/main.yml index 5a64778..8a8fa39 100644 --- a/roles/promtail/defaults/main.yml +++ b/roles/promtail/defaults/main.yml @@ -2,6 +2,6 @@ promtail_version: 2.7.3 promtail_positions_path: /home/promtail/positions.yaml promtail_http_port: 9080 -loki_server: localhost -loki_port: 3100 -add_var_logs: true +promtail_loki_server: localhost +promtail_loki_port: 3100 +promtail_add_var_logs: true diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml index c9ef143..e1e0984 100644 --- a/roles/promtail/molecule/default/verify.yml +++ b/roles/promtail/molecule/default/verify.yml @@ -7,13 +7,13 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Sanity check the Promtail daemon" retries: 3 delay: 1 - register: this - failed_when: "this.content != 'Ready'" + register: promtail_this + failed_when: "promtail_this.content != 'Ready'" ansible.builtin.uri: url: "http://localhost:{{ promtail_http_port }}/ready" return_content: true diff --git a/roles/promtail/tasks/main.yml b/roles/promtail/tasks/main.yml index ca1458b..127c10c 100644 --- a/roles/promtail/tasks/main.yml +++ b/roles/promtail/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Install packages" ansible.builtin.apt: - name: [ acl, unzip ] + name: [acl, unzip] state: present update_cache: true cache_valid_time: 3600 diff --git a/roles/promtail/templates/promtail.yml.j2 b/roles/promtail/templates/promtail.yml.j2 index 2435643..21dea49 100644 --- a/roles/promtail/templates/promtail.yml.j2 +++ b/roles/promtail/templates/promtail.yml.j2 @@ -9,10 +9,10 @@ positions: filename: {{ promtail_positions_path }} clients: - - url: "http://{{ loki_server }}:{{ loki_port }}/loki/api/v1/push" + - url: "http://{{ promtail_loki_server }}:{{ promtail_loki_port }}/loki/api/v1/push" scrape_configs: -{% if add_var_logs %} +{% if promtail_add_var_logs %} - job_name: system static_configs: - targets: From a683f014636140abe51aa44364865e577021295b Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:06:56 +0300 Subject: [PATCH 10/37] remove unneeded files --- roles/promtail/.ansible-lint | 16 ---------------- roles/promtail/.gitignore | 2 -- roles/promtail/.yamllint | 8 -------- roles/promtail/renovate.json | 6 ------ roles/promtail/requirements.ci.txt | 5 ----- 5 files changed, 37 deletions(-) delete mode 100644 roles/promtail/.ansible-lint delete mode 100644 roles/promtail/.gitignore delete mode 100644 roles/promtail/.yamllint delete mode 100644 roles/promtail/renovate.json delete mode 100644 roles/promtail/requirements.ci.txt diff --git a/roles/promtail/.ansible-lint b/roles/promtail/.ansible-lint deleted file mode 100644 index f865478..0000000 --- a/roles/promtail/.ansible-lint +++ /dev/null @@ -1,16 +0,0 @@ ---- -profile: production -strict: true - -# Enable checking of loop variable prefixes in roles -loop_var_prefix: "^(__|{role}_)" - -skip_list: - - var-naming[no-role-prefix] - -warn_list: - - role-name[path] - - var-naming[no-role-prefix] - -exclude_paths: - - .github/ diff --git a/roles/promtail/.gitignore b/roles/promtail/.gitignore deleted file mode 100644 index 8cd0df3..0000000 --- a/roles/promtail/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode -.idea \ No newline at end of file diff --git a/roles/promtail/.yamllint b/roles/promtail/.yamllint deleted file mode 100644 index 611db54..0000000 --- a/roles/promtail/.yamllint +++ /dev/null @@ -1,8 +0,0 @@ ---- -rules: - brackets: - forbid: false - min-spaces-inside: 0 - max-spaces-inside: 2 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: 2 diff --git a/roles/promtail/renovate.json b/roles/promtail/renovate.json deleted file mode 100644 index 5db72dd..0000000 --- a/roles/promtail/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended" - ] -} diff --git a/roles/promtail/requirements.ci.txt b/roles/promtail/requirements.ci.txt deleted file mode 100644 index 5eabc75..0000000 --- a/roles/promtail/requirements.ci.txt +++ /dev/null @@ -1,5 +0,0 @@ -ansible -molecule -molecule-plugins[docker] -docker -requests==2.31.0 # pinned to the latest version not breaking Docker SDK \ No newline at end of file From 471be205638537a7b8460a6aa9f19d758bcc2351 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:15:58 +0300 Subject: [PATCH 11/37] fix kics error --- roles/promtail/molecule/default/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/promtail/molecule/default/verify.yml b/roles/promtail/molecule/default/verify.yml index e1e0984..7e078c2 100644 --- a/roles/promtail/molecule/default/verify.yml +++ b/roles/promtail/molecule/default/verify.yml @@ -9,6 +9,7 @@ dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" extensions: ['yml'] + # kics-scan ignore-block - kics doesn't like http but it's localhost so it's not important - name: "Sanity check the Promtail daemon" retries: 3 delay: 1 From cadbc7398619e04427a9faa2886d769f5b71d752 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:21:17 +0300 Subject: [PATCH 12/37] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f383011..3aed186 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) - [prometheus](roles/prometheus/README.md) +- [promtail](roles/promtail/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) - [rustdesk](roles/rustdesk/README.md) - [sftp_share](roles/sftp_share/README.md) diff --git a/galaxy.yml b/galaxy.yml index a760250..a47d750 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.18.0 +version: 0.19.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 38796c1a0ddf6db3fab812ed368d2ed8b6784b64 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Wed, 17 Dec 2025 18:25:25 +0300 Subject: [PATCH 13/37] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3aed186..7217749 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [loki](roles/loki/README.md) - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) +- [node_exporter](roles/node_exporter/README.md) - [prometheus](roles/prometheus/README.md) - [promtail](roles/promtail/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) diff --git a/galaxy.yml b/galaxy.yml index a47d750..e8ba3ce 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.19.0 +version: 0.20.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From cb482a66350e3ea72e71a6b1d8547a6ea9ccf0f0 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 10:41:25 +0300 Subject: [PATCH 14/37] fix ansible-lint errors --- roles/mkfs/meta/main.yml | 2 +- roles/mkfs/molecule/default/converge.yml | 10 +++++----- roles/mkfs/molecule/default/verify.yml | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/roles/mkfs/meta/main.yml b/roles/mkfs/meta/main.yml index 002fbb6..1fff67d 100644 --- a/roles/mkfs/meta/main.yml +++ b/roles/mkfs/meta/main.yml @@ -10,7 +10,7 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "jammy", "noble" ] + versions: ["jammy", "noble"] galaxy_tags: [] diff --git a/roles/mkfs/molecule/default/converge.yml b/roles/mkfs/molecule/default/converge.yml index aeadc57..93e2908 100644 --- a/roles/mkfs/molecule/default/converge.yml +++ b/roles/mkfs/molecule/default/converge.yml @@ -7,16 +7,16 @@ - name: "Check if the disk file already exists" ansible.builtin.stat: path: /tmp/test_file - register: disk_file + register: mkfs_disk_file - name: "Create a file to act as a disk if it doesn't exist" ansible.builtin.command: dd if=/dev/zero of=/tmp/test_file bs=1000000 count=100 - when: not disk_file.stat.exists - register: dd_output - changed_when: dd_output.rc != 0 + when: not mkfs_disk_file.stat.exists + register: mkfs_dd_output + changed_when: mkfs_dd_output.rc != 0 roles: - - role: genlab.mkfs + - role: genlab.common.mkfs mkfs_device: "/tmp/test_file" mkfs_type: ext3 mkfs_state: present diff --git a/roles/mkfs/molecule/default/verify.yml b/roles/mkfs/molecule/default/verify.yml index 000a70d..b9498c4 100644 --- a/roles/mkfs/molecule/default/verify.yml +++ b/roles/mkfs/molecule/default/verify.yml @@ -7,19 +7,19 @@ tasks: - name: "Attach the file to a loop device" ansible.builtin.command: losetup /dev/loop3 /tmp/test_file - register: loop_device - changed_when: loop_device.rc != 0 + register: mkfs_loop_device + changed_when: mkfs_loop_device.rc != 0 - name: "Verify the filesystem type" ansible.builtin.command: blkid /dev/loop3 - register: blkid_output - changed_when: blkid_output.rc != 0 + register: mkfs_blkid_output + changed_when: mkfs_blkid_output.rc != 0 - name: "Ensure filesystem is ext3" ansible.builtin.assert: that: - - "'TYPE=\"ext3\"' in blkid_output.stdout" + - "'TYPE=\"ext3\"' in mkfs_blkid_output.stdout" - name: "Explore stdout" ansible.builtin.debug: - var: blkid_output.stdout + var: mkfs_blkid_output.stdout From 3662d2a4bf44bf44cc7b073c245c744bdb7dde1d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 10:42:57 +0300 Subject: [PATCH 15/37] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7217749..e0e7812 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [ipmi_exporter](roles/ipmi_exporter/README.md) - [karma](roles/karma/README.md) - [loki](roles/loki/README.md) +- [mkfs](roles/mkfs/README.md) - [mount_device](roles/mount_device/README.md) - [nginx](roles/nginx/README.md) - [node_exporter](roles/node_exporter/README.md) diff --git a/galaxy.yml b/galaxy.yml index e8ba3ce..ce5ff52 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.20.0 +version: 0.21.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From d27ba46d5b0cf448a03fa4be7ab1c548c0890a8c Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 10:54:07 +0300 Subject: [PATCH 16/37] add swapfile role --- roles/swapfile/README.md | 39 ++++++++++++++++ roles/swapfile/defaults/main.yml | 8 ++++ roles/swapfile/handlers/main.yml | 1 + roles/swapfile/meta/main.yml | 17 +++++++ roles/swapfile/molecule/default/converge.yml | 19 ++++++++ roles/swapfile/molecule/default/molecule.yml | 29 ++++++++++++ roles/swapfile/molecule/default/verify.yml | 26 +++++++++++ roles/swapfile/tasks/disable.yml | 11 +++++ roles/swapfile/tasks/enable.yml | 47 ++++++++++++++++++++ roles/swapfile/tasks/main.yml | 18 ++++++++ roles/swapfile/vars/main.yml | 1 + 11 files changed, 216 insertions(+) create mode 100644 roles/swapfile/README.md create mode 100644 roles/swapfile/defaults/main.yml create mode 100644 roles/swapfile/handlers/main.yml create mode 100644 roles/swapfile/meta/main.yml create mode 100644 roles/swapfile/molecule/default/converge.yml create mode 100644 roles/swapfile/molecule/default/molecule.yml create mode 100644 roles/swapfile/molecule/default/verify.yml create mode 100644 roles/swapfile/tasks/disable.yml create mode 100644 roles/swapfile/tasks/enable.yml create mode 100644 roles/swapfile/tasks/main.yml create mode 100644 roles/swapfile/vars/main.yml diff --git a/roles/swapfile/README.md b/roles/swapfile/README.md new file mode 100644 index 0000000..019f927 --- /dev/null +++ b/roles/swapfile/README.md @@ -0,0 +1,39 @@ +ansible-swapfile +========= + +Create/destroy a swapfile and run swapon/-off on it. + +Massively borrowing from https://github.com/geerlingguy/ansible-role-swap. + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.swapfile +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/swapfile/defaults/main.yml b/roles/swapfile/defaults/main.yml new file mode 100644 index 0000000..b76651e --- /dev/null +++ b/roles/swapfile/defaults/main.yml @@ -0,0 +1,8 @@ +--- +swap_file_path: /swapfile +swap_file_size_mb: '512' +swap_swappiness: '60' +swap_file_state: present +swap_file_create_command: "dd if=/dev/zero of={{ swap_file_path }} bs=1M count={{ swap_file_size_mb }}" + +swap_test_mode: false diff --git a/roles/swapfile/handlers/main.yml b/roles/swapfile/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/swapfile/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/swapfile/meta/main.yml b/roles/swapfile/meta/main.yml new file mode 100644 index 0000000..00f0b94 --- /dev/null +++ b/roles/swapfile/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "swapfile" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/swapfile/molecule/default/converge.yml b/roles/swapfile/molecule/default/converge.yml new file mode 100644 index 0000000..e702ede --- /dev/null +++ b/roles/swapfile/molecule/default/converge.yml @@ -0,0 +1,19 @@ +--- +- name: Converge + hosts: all + become: true + + pre_tasks: + - name: "Update apt cache" + when: ansible_os_family == 'Debian' + ansible.builtin.apt: + update_cache: true + cache_valid_time: 600 + + roles: + - role: genlab.swapfile + swap_file_path: /swapfile + swap_file_size_mb: 2000 + swap_swappiness: 30 + swap_file_state: present + swap_test_mode: true diff --git a/roles/swapfile/molecule/default/molecule.yml b/roles/swapfile/molecule/default/molecule.yml new file mode 100644 index 0000000..89df07d --- /dev/null +++ b/roles/swapfile/molecule/default/molecule.yml @@ -0,0 +1,29 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + pre_build_image: true + +provisioner: + name: ansible + playbooks: + converge: ${MOLECULE_PLAYBOOK:-converge.yml} + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/swapfile/molecule/default/verify.yml b/roles/swapfile/molecule/default/verify.yml new file mode 100644 index 0000000..c54ab18 --- /dev/null +++ b/roles/swapfile/molecule/default/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + swapfile_path: "/swapfile" + + pre_tasks: + - name: "Ensure 'file' exists" + ansible.builtin.package: + name: [file] + state: present + + tasks: + - name: "Check swapfile file type" + register: swapfile_info + changed_when: false + ansible.builtin.command: + cmd: "file {{ swapfile_path }}" + + - name: "Verify correct file type" + ansible.builtin.assert: + that: "'swap file' in swapfile_info.stdout" + success_msg: "{{ swapfile_path }}: Indeed, a Linux swap file" + fail_msg: "{{ swapfile_path }}: not a Linux swap file ('{{ swapfile_info.stdout }}')" diff --git a/roles/swapfile/tasks/disable.yml b/roles/swapfile/tasks/disable.yml new file mode 100644 index 0000000..302952b --- /dev/null +++ b/roles/swapfile/tasks/disable.yml @@ -0,0 +1,11 @@ +--- +- name: "Disable | disable swap" + changed_when: false + ansible.builtin.command: + cmd: swapoff -a + + +- name: "Disable | ensure swap file doesn't exist" + ansible.builtin.file: + path: "{{ swap_file_path }}" + state: absent diff --git a/roles/swapfile/tasks/enable.yml b/roles/swapfile/tasks/enable.yml new file mode 100644 index 0000000..e2170b8 --- /dev/null +++ b/roles/swapfile/tasks/enable.yml @@ -0,0 +1,47 @@ +--- +# https://justinmontgomery.com/speed-up-stat-command-in-ansible +- name: "Enable | create the swap file" + block: + - name: "Enable | stat the swap file" + register: this + failed_when: not this.stat.exists + ansible.builtin.stat: + path: "{{ swap_file_path }}" + get_checksum: false + get_mime: false + get_attributes: false + + rescue: + - name: "Enable | ensure swap file exists" + register: swap_file_create + ansible.builtin.command: + cmd: "{{ swap_file_create_command }}" + creates: "{{ swap_file_path }}" + + - name: "Enable | set permissions on swap file" + ansible.builtin.file: + path: "{{ swap_file_path }}" + owner: root + group: root + mode: "0600" + + - name: "Enable | make swap" # noqa: no-handler no-changed-when + register: mkswap_result + when: swap_file_create is changed + ansible.builtin.command: + cmd: mkswap {{ swap_file_path }} + + - name: "Enable | run swapon on the swap file => '{{ swap_file_path }}'" + when: + - mkswap_result is changed + - not swap_test_mode + changed_when: false + ansible.builtin.command: + cmd: swapon {{ swap_file_path }} + + +- name: "Set swappiness to '{{ swap_swappiness }}'" + ansible.posix.sysctl: + name: vm.swappiness + value: "{{ swap_swappiness }}" + state: present diff --git a/roles/swapfile/tasks/main.yml b/roles/swapfile/tasks/main.yml new file mode 100644 index 0000000..177dd24 --- /dev/null +++ b/roles/swapfile/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: "Manage swap file entry in fstab" + ansible.posix.mount: + name: none + src: "{{ swap_file_path }}" + fstype: swap + opts: sw + state: "{{ swap_file_state }}" + + +- name: "Include disable swap tasks" + when: swap_file_state == 'absent' + ansible.builtin.include_tasks: disable.yml + + +- name: "Include enable swap tasks" + when: swap_file_state == 'present' + ansible.builtin.include_tasks: enable.yml diff --git a/roles/swapfile/vars/main.yml b/roles/swapfile/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/swapfile/vars/main.yml @@ -0,0 +1 @@ +--- From ef524d700627080a499f43aa889ee65de99f59fb Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:29:03 +0300 Subject: [PATCH 17/37] fix ansible-lint errors --- roles/swapfile/defaults/main.yml | 12 ++++---- roles/swapfile/meta/main.yml | 4 +-- roles/swapfile/molecule/default/converge.yml | 12 ++++---- roles/swapfile/tasks/disable.yml | 2 +- roles/swapfile/tasks/enable.yml | 32 ++++++++++---------- roles/swapfile/tasks/main.yml | 8 ++--- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/roles/swapfile/defaults/main.yml b/roles/swapfile/defaults/main.yml index b76651e..e5e29fe 100644 --- a/roles/swapfile/defaults/main.yml +++ b/roles/swapfile/defaults/main.yml @@ -1,8 +1,8 @@ --- -swap_file_path: /swapfile -swap_file_size_mb: '512' -swap_swappiness: '60' -swap_file_state: present -swap_file_create_command: "dd if=/dev/zero of={{ swap_file_path }} bs=1M count={{ swap_file_size_mb }}" +swapfile_path: /swapfile +swapfile_size_mb: '512' +swapfile_swappiness: '60' +swapfile_state: present +swapfile_create_command: "dd if=/dev/zero of={{ swapfile_path }} bs=1M count={{ swapfile_size_mb }}" -swap_test_mode: false +swapfile_test_mode: false diff --git a/roles/swapfile/meta/main.yml b/roles/swapfile/meta/main.yml index 00f0b94..fde50d8 100644 --- a/roles/swapfile/meta/main.yml +++ b/roles/swapfile/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy" ] + versions: ["focal", "jammy"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/swapfile/molecule/default/converge.yml b/roles/swapfile/molecule/default/converge.yml index e702ede..459c5ad 100644 --- a/roles/swapfile/molecule/default/converge.yml +++ b/roles/swapfile/molecule/default/converge.yml @@ -11,9 +11,9 @@ cache_valid_time: 600 roles: - - role: genlab.swapfile - swap_file_path: /swapfile - swap_file_size_mb: 2000 - swap_swappiness: 30 - swap_file_state: present - swap_test_mode: true + - role: genlab.common.swapfile + swapfile_path: /swapfile + swapfile_size_mb: 2000 + swapfile_swappiness: 30 + swapfile_state: present + swapfile_test_mode: true diff --git a/roles/swapfile/tasks/disable.yml b/roles/swapfile/tasks/disable.yml index 302952b..ba04c8a 100644 --- a/roles/swapfile/tasks/disable.yml +++ b/roles/swapfile/tasks/disable.yml @@ -7,5 +7,5 @@ - name: "Disable | ensure swap file doesn't exist" ansible.builtin.file: - path: "{{ swap_file_path }}" + path: "{{ swapfile_path }}" state: absent diff --git a/roles/swapfile/tasks/enable.yml b/roles/swapfile/tasks/enable.yml index e2170b8..e433f73 100644 --- a/roles/swapfile/tasks/enable.yml +++ b/roles/swapfile/tasks/enable.yml @@ -3,45 +3,45 @@ - name: "Enable | create the swap file" block: - name: "Enable | stat the swap file" - register: this - failed_when: not this.stat.exists + register: swapfile_this + failed_when: not swapfile_this.stat.exists ansible.builtin.stat: - path: "{{ swap_file_path }}" + path: "{{ swapfile_path }}" get_checksum: false get_mime: false get_attributes: false rescue: - name: "Enable | ensure swap file exists" - register: swap_file_create + register: swapfile_create ansible.builtin.command: - cmd: "{{ swap_file_create_command }}" - creates: "{{ swap_file_path }}" + cmd: "{{ swapfile_create_command }}" + creates: "{{ swapfile_path }}" - name: "Enable | set permissions on swap file" ansible.builtin.file: - path: "{{ swap_file_path }}" + path: "{{ swapfile_path }}" owner: root group: root mode: "0600" - name: "Enable | make swap" # noqa: no-handler no-changed-when - register: mkswap_result - when: swap_file_create is changed + register: swapfile_mkswap_result + when: swapfile_create is changed ansible.builtin.command: - cmd: mkswap {{ swap_file_path }} + cmd: mkswap {{ swapfile_path }} - - name: "Enable | run swapon on the swap file => '{{ swap_file_path }}'" + - name: "Enable | run swapon on the swap file => '{{ swapfile_path }}'" when: - - mkswap_result is changed - - not swap_test_mode + - swapfile_mkswap_result is changed + - not swapfile_test_mode changed_when: false ansible.builtin.command: - cmd: swapon {{ swap_file_path }} + cmd: swapon {{ swapfile_path }} -- name: "Set swappiness to '{{ swap_swappiness }}'" +- name: "Set swappiness to '{{ swapfile_swappiness }}'" ansible.posix.sysctl: name: vm.swappiness - value: "{{ swap_swappiness }}" + value: "{{ swapfile_swappiness }}" state: present diff --git a/roles/swapfile/tasks/main.yml b/roles/swapfile/tasks/main.yml index 177dd24..639505c 100644 --- a/roles/swapfile/tasks/main.yml +++ b/roles/swapfile/tasks/main.yml @@ -2,17 +2,17 @@ - name: "Manage swap file entry in fstab" ansible.posix.mount: name: none - src: "{{ swap_file_path }}" + src: "{{ swapfile_path }}" fstype: swap opts: sw - state: "{{ swap_file_state }}" + state: "{{ swapfile_state }}" - name: "Include disable swap tasks" - when: swap_file_state == 'absent' + when: swapfile_state == 'absent' ansible.builtin.include_tasks: disable.yml - name: "Include enable swap tasks" - when: swap_file_state == 'present' + when: swapfile_state == 'present' ansible.builtin.include_tasks: enable.yml From c1142c568951aafbcb0827a79f7805e526a17565 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:30:15 +0300 Subject: [PATCH 18/37] udpate meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e0e7812..bd37b07 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [rustdesk](roles/rustdesk/README.md) - [sftp_share](roles/sftp_share/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) +- [swapfile](roles/swapfile/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) - [wg_spoke](roles/wg_spoke/README.md) \ No newline at end of file diff --git a/galaxy.yml b/galaxy.yml index ce5ff52..c26b9f9 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.21.0 +version: 0.22.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 30945d5a2f101822c8c4ef062c6b759cb114287b Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:39:09 +0300 Subject: [PATCH 19/37] add sshd role --- roles/sshd/README.md | 37 +++++++++++++++++++++++++++++ roles/sshd/defaults/main.yml | 5 ++++ roles/sshd/handlers/main.yml | 7 ++++++ roles/sshd/meta/main.yml | 17 +++++++++++++ roles/sshd/tasks/algorithms.yml | 33 +++++++++++++++++++++++++ roles/sshd/tasks/authentication.yml | 31 ++++++++++++++++++++++++ roles/sshd/tasks/encryption.yml | 11 +++++++++ roles/sshd/tasks/install.yml | 12 ++++++++++ roles/sshd/tasks/main.yml | 23 ++++++++++++++++++ roles/sshd/tasks/restrictions.yml | 30 +++++++++++++++++++++++ roles/sshd/vars/main.yml | 1 + 11 files changed, 207 insertions(+) create mode 100644 roles/sshd/README.md create mode 100644 roles/sshd/defaults/main.yml create mode 100644 roles/sshd/handlers/main.yml create mode 100644 roles/sshd/meta/main.yml create mode 100644 roles/sshd/tasks/algorithms.yml create mode 100644 roles/sshd/tasks/authentication.yml create mode 100644 roles/sshd/tasks/encryption.yml create mode 100644 roles/sshd/tasks/install.yml create mode 100644 roles/sshd/tasks/main.yml create mode 100644 roles/sshd/tasks/restrictions.yml create mode 100644 roles/sshd/vars/main.yml diff --git a/roles/sshd/README.md b/roles/sshd/README.md new file mode 100644 index 0000000..1c4d1d9 --- /dev/null +++ b/roles/sshd/README.md @@ -0,0 +1,37 @@ +ansible-sshd +========= + +Deploy a hardened sshd server + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.sshd +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/sshd/defaults/main.yml b/roles/sshd/defaults/main.yml new file mode 100644 index 0000000..7f8dce0 --- /dev/null +++ b/roles/sshd/defaults/main.yml @@ -0,0 +1,5 @@ +--- +disable_pam: false +password_auth: false +challenge_response_auth: false +gss_api_auth: false diff --git a/roles/sshd/handlers/main.yml b/roles/sshd/handlers/main.yml new file mode 100644 index 0000000..9f08055 --- /dev/null +++ b/roles/sshd/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Restart sshd" + ansible.builtin.service: + name: ssh + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/sshd/meta/main.yml b/roles/sshd/meta/main.yml new file mode 100644 index 0000000..45b38d2 --- /dev/null +++ b/roles/sshd/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: sshd + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Deploy a hardened sshd server" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy" ] + + galaxy_tags: [ ] + +dependencies: [] diff --git a/roles/sshd/tasks/algorithms.yml b/roles/sshd/tasks/algorithms.yml new file mode 100644 index 0000000..3eb0150 --- /dev/null +++ b/roles/sshd/tasks/algorithms.yml @@ -0,0 +1,33 @@ +--- +# NOTE: order of preference for openssh-server ed25519 -> rsa +- name: "Algorithms | enable ed25519 authentication algorithm" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^HostKey /etc/ssh/ssh_host_ed25519_key' + line: 'HostKey /etc/ssh/ssh_host_ed25519_key' + validate: sshd -f %s -t + +- name: "Algorithms | enable the RSA authentication algorithm" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^HostKey /etc/ssh/ssh_host_rsa_key' + line: 'HostKey /etc/ssh/ssh_host_rsa_key' + validate: sshd -f %s -t + +- name: "Algorithms | disable the ECDSA algorithm (deemed to be less safe)" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^HostKey /etc/ssh/ssh_host_ecdsa_key' + state: absent + validate: sshd -f %s -t + +- name: "Algorithms | disable the DSA algorithm (considered to be defunct)" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^HostKey /etc/ssh/ssh_host_dsa_key' + state: absent + validate: sshd -f %s -t diff --git a/roles/sshd/tasks/authentication.yml b/roles/sshd/tasks/authentication.yml new file mode 100644 index 0000000..26b690a --- /dev/null +++ b/roles/sshd/tasks/authentication.yml @@ -0,0 +1,31 @@ +--- +- name: "Authentication | Configure SSH authentication settings" + notify: Restart sshd + loop: + - { regexp: '^#?\s*PubkeyAuthentication\s+', line: 'PubkeyAuthentication yes' } + - { regexp: '^#?\s*PasswordAuthentication\s+', line: 'PasswordAuthentication {{ password_auth | ternary("yes", "no") }}' } + - { regexp: '^#?\s*PermitEmptyPasswords\s+', line: 'PermitEmptyPasswords no' } + - { regexp: '^#?\s*ChallengeResponseAuthentication\s+', line: 'ChallengeResponseAuthentication {{ challenge_response_auth | ternary("yes", "no") }}' } + - { regexp: '^#?\s*GSSAPIAuthentication\s+', line: 'GSSAPIAuthentication {{ gss_api_auth | ternary("yes", "no") }}' } + - { + regexp: '^#?\s*AuthenticationMethods\s+', + line: "{{ 'AuthenticationMethods publickey password' if password_auth else 'AuthenticationMethods publickey' }}" + } + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + validate: /usr/sbin/sshd -t -f %s + +- name: "Check if there is an SSH config forced by cloud-init" + register: sshd_cloud_init + ansible.builtin.stat: + path: "/etc/ssh/sshd_config.d/50-cloud-init.conf" + +- name: "Authentication | override password authentication by cloud-init to '{{ password_auth | ternary('yes', 'no') }}'" + when: sshd_cloud_init.stat.exists + notify: Restart sshd + ansible.builtin.lineinfile: + path: "/etc/ssh/sshd_config.d/50-cloud-init.conf" + regexp: '^#?PasswordAuthentication' + line: 'PasswordAuthentication {{ password_auth | ternary("yes", "no") }}' diff --git a/roles/sshd/tasks/encryption.yml b/roles/sshd/tasks/encryption.yml new file mode 100644 index 0000000..660a369 --- /dev/null +++ b/roles/sshd/tasks/encryption.yml @@ -0,0 +1,11 @@ +--- +- name: "Encryption | remove unsafe host keys" + loop: + - /etc/ssh/ssh_host_ecdsa_key + - /etc/ssh/ssh_host_ecdsa_key.pub + - /etc/ssh/ssh_host_dsa_key + - /etc/ssh/ssh_host_dsa_key.pub + notify: Restart sshd + ansible.builtin.file: + path: "{{ item }}" + state: absent diff --git a/roles/sshd/tasks/install.yml b/roles/sshd/tasks/install.yml new file mode 100644 index 0000000..1940c90 --- /dev/null +++ b/roles/sshd/tasks/install.yml @@ -0,0 +1,12 @@ +--- +- name: "Install | install OpenSSH (RHEL flavours)" + when: ansible_os_family == "RHEL" + ansible.builtin.dnf: + name: openssh + state: installed + +- name: "Install | Install OpenSSH (Debian flavours)" + when: ansible_os_family == "Debian" + ansible.builtin.apt: + name: openssh-server + state: present diff --git a/roles/sshd/tasks/main.yml b/roles/sshd/tasks/main.yml new file mode 100644 index 0000000..d35222a --- /dev/null +++ b/roles/sshd/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: "Install an OpenSSH server" + ansible.builtin.include_tasks: "install.yml" + +- name: "Configure SSH algorithms" + ansible.builtin.include_tasks: "algorithms.yml" + +- name: "Configure authentication methods" + ansible.builtin.include_tasks: "authentication.yml" + +- name: "Configure SSH encryption keys" + ansible.builtin.include_tasks: "encryption.yml" + +- name: "Configure additional restrictions" + ansible.builtin.include_tasks: "restrictions.yml" + +- name: "Log at VERBOSE level" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^#?LogLevel' + line: 'LogLevel VERBOSE' + validate: sshd -f %s -t diff --git a/roles/sshd/tasks/restrictions.yml b/roles/sshd/tasks/restrictions.yml new file mode 100644 index 0000000..723a007 --- /dev/null +++ b/roles/sshd/tasks/restrictions.yml @@ -0,0 +1,30 @@ +--- +- name: "Restrictions | Configure SSH security restrictions" + loop: + - { regexp: '^#?Protocol\s+', line: 'Protocol 2' } + - { regexp: '^PermitRootLogin yes', line: 'PermitRootLogin no' } + - { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' } + - { regexp: '^#?IgnoreRhosts', line: 'IgnoreRhosts yes' } + - { regexp: '^#?DebianBanner\s+', line: 'DebianBanner no' } + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + validate: /usr/sbin/sshd -t -f %s + +- name: "Restrictions | toggle PAM" + notify: Restart sshd + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: '^#?UsePAM' + line: "UsePAM {{ disable_pam | ternary('no', 'yes') }}" + validate: sshd -f %s -t + +- name: "Restrictions | ensure the SSHD config is restricted to the root user" + notify: Restart sshd + ansible.builtin.file: + path: /etc/ssh/sshd_config + owner: root + group: root + mode: "0600" diff --git a/roles/sshd/vars/main.yml b/roles/sshd/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/sshd/vars/main.yml @@ -0,0 +1 @@ +--- From 2c4b30314b66b22205ab2f5eb86efe7132464b7c Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:39:21 +0300 Subject: [PATCH 20/37] add molecule files --- roles/sshd/molecule/default/converge.yml | 5 +++++ roles/sshd/molecule/default/molecule.yml | 27 ++++++++++++++++++++++++ roles/sshd/molecule/default/verify.yml | 10 +++++++++ 3 files changed, 42 insertions(+) create mode 100644 roles/sshd/molecule/default/converge.yml create mode 100644 roles/sshd/molecule/default/molecule.yml create mode 100644 roles/sshd/molecule/default/verify.yml diff --git a/roles/sshd/molecule/default/converge.yml b/roles/sshd/molecule/default/converge.yml new file mode 100644 index 0000000..bea06f8 --- /dev/null +++ b/roles/sshd/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: "genlab.common.sshd" diff --git a/roles/sshd/molecule/default/molecule.yml b/roles/sshd/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/sshd/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/sshd/molecule/default/verify.yml b/roles/sshd/molecule/default/verify.yml new file mode 100644 index 0000000..50508d7 --- /dev/null +++ b/roles/sshd/molecule/default/verify.yml @@ -0,0 +1,10 @@ +--- + +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: blank + ansible.builtin.command: echo From 14bcc89e5d5052f2e56ac71611dafd7ce8b3d6ca Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:54:32 +0300 Subject: [PATCH 21/37] fix role and ansible-lint errors --- roles/sshd/defaults/main.yml | 8 ++++---- roles/sshd/meta/main.yml | 4 ++-- roles/sshd/molecule/default/verify.yml | 3 ++- roles/sshd/tasks/algorithms.yml | 9 +++++++++ roles/sshd/tasks/authentication.yml | 12 ++++++------ roles/sshd/tasks/install.yml | 1 + roles/sshd/tasks/restrictions.yml | 2 +- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/roles/sshd/defaults/main.yml b/roles/sshd/defaults/main.yml index 7f8dce0..5354282 100644 --- a/roles/sshd/defaults/main.yml +++ b/roles/sshd/defaults/main.yml @@ -1,5 +1,5 @@ --- -disable_pam: false -password_auth: false -challenge_response_auth: false -gss_api_auth: false +sshd_disable_pam: false +sshd_password_auth: false +sshd_challenge_response_auth: false +sshd_gss_api_auth: false diff --git a/roles/sshd/meta/main.yml b/roles/sshd/meta/main.yml index 45b38d2..7158cf1 100644 --- a/roles/sshd/meta/main.yml +++ b/roles/sshd/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy" ] + versions: ["focal", "jammy"] - galaxy_tags: [ ] + galaxy_tags: [] dependencies: [] diff --git a/roles/sshd/molecule/default/verify.yml b/roles/sshd/molecule/default/verify.yml index 50508d7..6287e65 100644 --- a/roles/sshd/molecule/default/verify.yml +++ b/roles/sshd/molecule/default/verify.yml @@ -6,5 +6,6 @@ any_errors_fatal: true tasks: - - name: blank + - name: Blank ansible.builtin.command: echo + changed_when: false diff --git a/roles/sshd/tasks/algorithms.yml b/roles/sshd/tasks/algorithms.yml index 3eb0150..a43581d 100644 --- a/roles/sshd/tasks/algorithms.yml +++ b/roles/sshd/tasks/algorithms.yml @@ -1,4 +1,13 @@ --- +# next task requires this directory to exist for sshd -t flag +- name: Ensure /run/sshd exists + ansible.builtin.file: + path: /run/sshd + state: directory + owner: root + group: root + mode: '0755' + # NOTE: order of preference for openssh-server ed25519 -> rsa - name: "Algorithms | enable ed25519 authentication algorithm" notify: Restart sshd diff --git a/roles/sshd/tasks/authentication.yml b/roles/sshd/tasks/authentication.yml index 26b690a..8b4e63b 100644 --- a/roles/sshd/tasks/authentication.yml +++ b/roles/sshd/tasks/authentication.yml @@ -3,13 +3,13 @@ notify: Restart sshd loop: - { regexp: '^#?\s*PubkeyAuthentication\s+', line: 'PubkeyAuthentication yes' } - - { regexp: '^#?\s*PasswordAuthentication\s+', line: 'PasswordAuthentication {{ password_auth | ternary("yes", "no") }}' } + - { regexp: '^#?\s*PasswordAuthentication\s+', line: 'PasswordAuthentication {{ sshd_password_auth | ternary("yes", "no") }}' } - { regexp: '^#?\s*PermitEmptyPasswords\s+', line: 'PermitEmptyPasswords no' } - - { regexp: '^#?\s*ChallengeResponseAuthentication\s+', line: 'ChallengeResponseAuthentication {{ challenge_response_auth | ternary("yes", "no") }}' } - - { regexp: '^#?\s*GSSAPIAuthentication\s+', line: 'GSSAPIAuthentication {{ gss_api_auth | ternary("yes", "no") }}' } + - { regexp: '^#?\s*ChallengeResponseAuthentication\s+', line: 'ChallengeResponseAuthentication {{ sshd_challenge_response_auth | ternary("yes", "no") }}' } + - { regexp: '^#?\s*GSSAPIAuthentication\s+', line: 'GSSAPIAuthentication {{ sshd_gss_api_auth | ternary("yes", "no") }}' } - { regexp: '^#?\s*AuthenticationMethods\s+', - line: "{{ 'AuthenticationMethods publickey password' if password_auth else 'AuthenticationMethods publickey' }}" + line: "{{ 'AuthenticationMethods publickey password' if sshd_password_auth else 'AuthenticationMethods publickey' }}" } ansible.builtin.lineinfile: path: /etc/ssh/sshd_config @@ -22,10 +22,10 @@ ansible.builtin.stat: path: "/etc/ssh/sshd_config.d/50-cloud-init.conf" -- name: "Authentication | override password authentication by cloud-init to '{{ password_auth | ternary('yes', 'no') }}'" +- name: "Authentication | override password authentication by cloud-init to '{{ sshd_password_auth | ternary('yes', 'no') }}'" when: sshd_cloud_init.stat.exists notify: Restart sshd ansible.builtin.lineinfile: path: "/etc/ssh/sshd_config.d/50-cloud-init.conf" regexp: '^#?PasswordAuthentication' - line: 'PasswordAuthentication {{ password_auth | ternary("yes", "no") }}' + line: 'PasswordAuthentication {{ sshd_password_auth | ternary("yes", "no") }}' diff --git a/roles/sshd/tasks/install.yml b/roles/sshd/tasks/install.yml index 1940c90..ff03595 100644 --- a/roles/sshd/tasks/install.yml +++ b/roles/sshd/tasks/install.yml @@ -10,3 +10,4 @@ ansible.builtin.apt: name: openssh-server state: present + update_cache: true diff --git a/roles/sshd/tasks/restrictions.yml b/roles/sshd/tasks/restrictions.yml index 723a007..6d14920 100644 --- a/roles/sshd/tasks/restrictions.yml +++ b/roles/sshd/tasks/restrictions.yml @@ -18,7 +18,7 @@ ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^#?UsePAM' - line: "UsePAM {{ disable_pam | ternary('no', 'yes') }}" + line: "UsePAM {{ sshd_disable_pam | ternary('no', 'yes') }}" validate: sshd -f %s -t - name: "Restrictions | ensure the SSHD config is restricted to the root user" From 30436311aa8f96586b4c21b4abdc9cd1fec0e62f Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 11:55:29 +0300 Subject: [PATCH 22/37] update meta --- README.md | 1 + galaxy.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd37b07..6386f43 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [rustdesk](roles/rustdesk/README.md) - [sftp_share](roles/sftp_share/README.md) - [smartctl_exporter](roles/smartctl_exporter/README.md) +- [sshd](roles/sshd/README.md) - [swapfile](roles/swapfile/README.md) - [ufw](roles/ufw/README.md) - [wg_hub](roles/wg_hub/README.md) diff --git a/galaxy.yml b/galaxy.yml index c26b9f9..8c51504 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.22.0 +version: 0.23.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 14e6bf12fdbaa2fd056227466c13b69ac3c0fc00 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:34:32 +0400 Subject: [PATCH 23/37] Migrate the `users` role --- roles/users/README.md | 55 +++++++++ roles/users/defaults/main.yml | 2 + roles/users/handlers/main.yml | 1 + roles/users/meta/main.yml | 16 +++ roles/users/molecule/default/converge.yml | 10 ++ roles/users/molecule/default/molecule.yml | 27 +++++ roles/users/molecule/default/user0.keys | 1 + roles/users/molecule/default/users.yml | 22 ++++ roles/users/molecule/default/verify.yml | 64 +++++++++++ .../molecule/users_from_var/converge.yml | 31 +++++ .../molecule/users_from_var/molecule.yml | 27 +++++ .../users/molecule/users_from_var/user0.keys | 1 + .../users/molecule/users_from_var/verify.yml | 64 +++++++++++ roles/users/tasks/create.yml | 106 ++++++++++++++++++ roles/users/tasks/main.yml | 14 +++ roles/users/tasks/openssh.yml | 14 +++ roles/users/templates/slice.j2 | 12 ++ roles/users/vars/main.yml | 4 + 18 files changed, 471 insertions(+) create mode 100644 roles/users/README.md create mode 100644 roles/users/defaults/main.yml create mode 100644 roles/users/handlers/main.yml create mode 100644 roles/users/meta/main.yml create mode 100644 roles/users/molecule/default/converge.yml create mode 100644 roles/users/molecule/default/molecule.yml create mode 100644 roles/users/molecule/default/user0.keys create mode 100644 roles/users/molecule/default/users.yml create mode 100644 roles/users/molecule/default/verify.yml create mode 100644 roles/users/molecule/users_from_var/converge.yml create mode 100644 roles/users/molecule/users_from_var/molecule.yml create mode 100644 roles/users/molecule/users_from_var/user0.keys create mode 100644 roles/users/molecule/users_from_var/verify.yml create mode 100644 roles/users/tasks/create.yml create mode 100644 roles/users/tasks/main.yml create mode 100644 roles/users/tasks/openssh.yml create mode 100644 roles/users/templates/slice.j2 create mode 100644 roles/users/vars/main.yml diff --git a/roles/users/README.md b/roles/users/README.md new file mode 100644 index 0000000..90f8563 --- /dev/null +++ b/roles/users/README.md @@ -0,0 +1,55 @@ +ansible-users +========= + +Create user accounts according to a YAML manifest. + +Controls: + - username and UID + - groupname and GID + - homedir creation + - GECOS field (typically full user name) + - shell + - SSH public key deployment to `.ssh/authorized_keys` + - account expiration + - memory, swap and CPU allocation limits via user slices + +Check out [corvus-migratorius/ansible-disk-quotas](https://github.com/corvus-migratorius/ansible-disk-quotas) for controlling non-root filesystem quotas. + +Requirements +------------ + +- systemd +- openssh + +Role Variables +-------------- + +`manifest_path`: a YAML file containing user definitions (see `molecule/default/users.yml` for an example) + +`users`: a list of objects mirroring the YAML structure expected by `manifest_path`; takes precedence over it + +`common_memory_max`: e.g. `"500M"`, optional + +`common_swap_max`: e.g. `"2G"`, optional + +`common_cpu_quota`: e.g. `"100%"`, optional + +Dependencies +------------ + +Example Playbook +---------------- + +- Configuring users in a YAML manifest: [converge.yml](molecule/default/converge.yml) +- Configuring user list as a variable: [converge.yml](molecule/users_from_var/converge.yml) + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me +masayganova@gmail.com diff --git a/roles/users/defaults/main.yml b/roles/users/defaults/main.yml new file mode 100644 index 0000000..cce8b27 --- /dev/null +++ b/roles/users/defaults/main.yml @@ -0,0 +1,2 @@ +--- +default_home_root: /home diff --git a/roles/users/handlers/main.yml b/roles/users/handlers/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/users/handlers/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/users/meta/main.yml b/roles/users/meta/main.yml new file mode 100644 index 0000000..06fac82 --- /dev/null +++ b/roles/users/meta/main.yml @@ -0,0 +1,16 @@ +galaxy_info: + role_name: users + namespace: genlab + author: Alexander Gorelyshev + company: Genlab, LLC + description: Deploy user accounts + license: GPL-2.0-or-later + min_ansible_version: "2.1" + + platforms: + - name: Ubuntu + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/users/molecule/default/converge.yml b/roles/users/molecule/default/converge.yml new file mode 100644 index 0000000..38fd392 --- /dev/null +++ b/roles/users/molecule/default/converge.yml @@ -0,0 +1,10 @@ +--- +- name: Converge + hosts: all + + roles: + - role: genlab.users + manifest_path: "users.yml" + common_memory_max: "500M" + common_swap_max: "2G" + common_cpu_quota: "100%" diff --git a/roles/users/molecule/default/molecule.yml b/roles/users/molecule/default/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/users/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/users/molecule/default/user0.keys b/roles/users/molecule/default/user0.keys new file mode 100644 index 0000000..5a478b1 --- /dev/null +++ b/roles/users/molecule/default/user0.keys @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname diff --git a/roles/users/molecule/default/users.yml b/roles/users/molecule/default/users.yml new file mode 100644 index 0000000..39687a1 --- /dev/null +++ b/roles/users/molecule/default/users.yml @@ -0,0 +1,22 @@ +--- +users: + - name: "user0" + uid: 1001 + gid: 1004 + full_name: "User Zero" + state: "present" + groups: [sudo] + create_home: true + home_root: "/tmp/someplace" + shell: "/bin/bash" + pubkeys_file: "user0.keys" + + - name: "user1" + uid: 1002 + full_name: "User One" + state: "present" + groups: [] + create_home: true + shell: "/bin/sh" + expires: "2024-07-23 12:00:00" + generate_ssh_key: true diff --git a/roles/users/molecule/default/verify.yml b/roles/users/molecule/default/verify.yml new file mode 100644 index 0000000..a911e9d --- /dev/null +++ b/roles/users/molecule/default/verify.yml @@ -0,0 +1,64 @@ +--- +- name: Verify + hosts: all + gather_facts: true + any_errors_fatal: true + + tasks: + - name: "Get user info from /etc/passwd" + register: etc_passwd + changed_when: false + ansible.builtin.shell: + cmd: set -o pipefail; cat /etc/passwd | grep user + executable: /bin/bash + + - name: "Get user info from /etc/shadow" + register: etc_shadow + changed_when: false + ansible.builtin.shell: + cmd: set -o pipefail; cat /etc/shadow | grep user + executable: /bin/bash + + # using a hack since here the date of last password change == date of account creation + - name: "Verify expected account configuration" + vars: + expected_shadow: + - "user0:!:{{ (ansible_date_time.epoch | int) // 86400 }}:0:99999:7:::" + - "user1:!:{{ (ansible_date_time.epoch | int) // 86400 }}:0:99999:7::19927:" + expected_passwd: + - "user0:x:1001:1004:User Zero:/tmp/someplace/user0:/bin/bash" + - "user1:x:1002:1002:User One:/home/user1:/bin/sh" + ansible.builtin.assert: + that: + - etc_passwd.stdout_lines == expected_passwd + - etc_shadow.stdout_lines == expected_shadow + + - name: "Get SSH key contents for user1" + register: user1_ssh_key + changed_when: false + ansible.builtin.command: + cmd: cat /home/user1/.ssh/id_ed25519.pub + + - name: "Verify the SSH key contents for user1" + ansible.builtin.assert: + that: "'user1@ubuntu' in user1_ssh_key.stdout" + + - name: "Get the user slice drop-in for user0" + register: user0_slice_dropin + changed_when: false + ansible.builtin.command: + cmd: cat /etc/systemd/system/user-1001.slice.d/50-limits.conf + + - name: "Verify drop-in contens" + vars: + expected: + - "[Slice]" + - "MemoryAccounting=1" + - "MemoryMax=500M" + - "MemorySwapMax=2G" + - "CPUAccounting=1" + - "CPUQuota=100%" + ansible.builtin.assert: + that: "user0_slice_dropin.stdout_lines == expected" + success_msg: "Got the expected limits slice drop-in" + fail_msg: "Unexpected limits slice drop-in contents" diff --git a/roles/users/molecule/users_from_var/converge.yml b/roles/users/molecule/users_from_var/converge.yml new file mode 100644 index 0000000..a6e93e9 --- /dev/null +++ b/roles/users/molecule/users_from_var/converge.yml @@ -0,0 +1,31 @@ +--- +- name: Converge + hosts: all + + roles: + - role: genlab.users + manifest_path: "some-none-existent-file" # the 'users' variable is supposed to take precedence + common_memory_max: "500M" + common_swap_max: "2G" + common_cpu_quota: "100%" + users: + - name: "user0" + uid: 1001 + gid: 1004 + full_name: "User Zero" + state: "present" + groups: [sudo] + create_home: true + home_root: "/tmp/someplace" + shell: "/bin/bash" + pubkeys_file: "user0.keys" + + - name: "user1" + uid: 1002 + full_name: "User One" + state: "present" + groups: [] + create_home: true + shell: "/bin/sh" + expires: "2024-07-23 12:00:00" + generate_ssh_key: true diff --git a/roles/users/molecule/users_from_var/molecule.yml b/roles/users/molecule/users_from_var/molecule.yml new file mode 100644 index 0000000..d82158e --- /dev/null +++ b/roles/users/molecule/users_from_var/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/users/molecule/users_from_var/user0.keys b/roles/users/molecule/users_from_var/user0.keys new file mode 100644 index 0000000..5a478b1 --- /dev/null +++ b/roles/users/molecule/users_from_var/user0.keys @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname diff --git a/roles/users/molecule/users_from_var/verify.yml b/roles/users/molecule/users_from_var/verify.yml new file mode 100644 index 0000000..a911e9d --- /dev/null +++ b/roles/users/molecule/users_from_var/verify.yml @@ -0,0 +1,64 @@ +--- +- name: Verify + hosts: all + gather_facts: true + any_errors_fatal: true + + tasks: + - name: "Get user info from /etc/passwd" + register: etc_passwd + changed_when: false + ansible.builtin.shell: + cmd: set -o pipefail; cat /etc/passwd | grep user + executable: /bin/bash + + - name: "Get user info from /etc/shadow" + register: etc_shadow + changed_when: false + ansible.builtin.shell: + cmd: set -o pipefail; cat /etc/shadow | grep user + executable: /bin/bash + + # using a hack since here the date of last password change == date of account creation + - name: "Verify expected account configuration" + vars: + expected_shadow: + - "user0:!:{{ (ansible_date_time.epoch | int) // 86400 }}:0:99999:7:::" + - "user1:!:{{ (ansible_date_time.epoch | int) // 86400 }}:0:99999:7::19927:" + expected_passwd: + - "user0:x:1001:1004:User Zero:/tmp/someplace/user0:/bin/bash" + - "user1:x:1002:1002:User One:/home/user1:/bin/sh" + ansible.builtin.assert: + that: + - etc_passwd.stdout_lines == expected_passwd + - etc_shadow.stdout_lines == expected_shadow + + - name: "Get SSH key contents for user1" + register: user1_ssh_key + changed_when: false + ansible.builtin.command: + cmd: cat /home/user1/.ssh/id_ed25519.pub + + - name: "Verify the SSH key contents for user1" + ansible.builtin.assert: + that: "'user1@ubuntu' in user1_ssh_key.stdout" + + - name: "Get the user slice drop-in for user0" + register: user0_slice_dropin + changed_when: false + ansible.builtin.command: + cmd: cat /etc/systemd/system/user-1001.slice.d/50-limits.conf + + - name: "Verify drop-in contens" + vars: + expected: + - "[Slice]" + - "MemoryAccounting=1" + - "MemoryMax=500M" + - "MemorySwapMax=2G" + - "CPUAccounting=1" + - "CPUQuota=100%" + ansible.builtin.assert: + that: "user0_slice_dropin.stdout_lines == expected" + success_msg: "Got the expected limits slice drop-in" + fail_msg: "Unexpected limits slice drop-in contents" diff --git a/roles/users/tasks/create.yml b/roles/users/tasks/create.yml new file mode 100644 index 0000000..309a9c5 --- /dev/null +++ b/roles/users/tasks/create.yml @@ -0,0 +1,106 @@ +--- +- name: "Create | Get current user's group entity info ({{ user.name }})" + failed_when: false + ansible.builtin.getent: + database: group + key: "{{ user.name }}" + split: ":" + +- name: "Create | Create user group ('{{ user.name }}')" # to handle cases where GID!=UID + when: + - getent_group[user.name] is not defined + ansible.builtin.group: + name: "{{ user.name }}" + gid: "{{ user.gid is defined | ternary(user.gid, user.uid) }}" + state: "{{ user.state }}" + +- name: "Create | Create user account ('{{ user.name }}')" + vars: + home_root: "{{ user.home_root is defined | ternary(user.home_root, default_home_root) }}" + ansible.builtin.user: + name: "{{ user.name }}" + state: "{{ user.state }}" + uid: "{{ user.uid }}" + group: "{{ user.name }}" + create_home: "{{ user.create_home | default(true) }}" + home: "{{ home_root }}/{{ user.name }}" + comment: "{{ user.full_name }}" + shell: "{{ user.shell | default('/bin/bash') }}" + groups: "{{ user.groups | default([]) }}" + append: false + generate_ssh_key: "{{ user.generate_ssh_key | default(false) }}" + ssh_key_type: "{{ user.ssh_key_type | default('ed25519') }}" + ssh_key_comment: "{{ user.name }}@{{ ansible_nodename }}" + # password_lock: "{{ user.password_lock | default('false') }}" + +- name: "Create | Set user account expiration date where defined ('{{ user.name }}')" + when: user.expires is defined + ansible.builtin.user: + name: "{{ user.name }}" + state: "{{ user.state }}" + expires: "{{ (user.expires | to_datetime).strftime('%s') }}" + +- name: "Create | Ensure no user account expiration date where undefined ('{{ user.name }}')" + when: user.expires is undefined + ansible.builtin.user: + name: "{{ user.name }}" + state: "{{ user.state }}" + expires: "-1" + +- name: "Create | Deploy SSH public key to 'authorized_keys' files ('{{ user.name }}')" + when: (user.pubkeys_file is defined) and (user.pubkeys_file != "") + ansible.posix.authorized_key: + user: "{{ user.name }}" + key: "{{ lookup('file', user.pubkeys_file) }}" + key_options: "{{ user.options | default('') }}" + exclusive: true + +- name: "Create | Create a systemd slice directory ('{{ user.name }}')" + ansible.builtin.file: + path: /etc/systemd/system/user-{{ user.uid }}.slice.d + state: directory + owner: root + group: root + mode: '0750' + +# Set user's maximum memory limit to 'memory_limit' defined in the user manifest_path. +# If 'memory_limit' is undefined there, use 'common_memory_max' as fallback. +# If 'common_memory_max' is also undefined, set to "", which should be ignored by the template. +# - 'memory_max:' may use postfix like K, M, G. +# - 'cpu_quota': "100%" for 1 full core. +# REF https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html +# REF https://serverfault.com/a/1092803 +- name: "Create | Create/update a systemd slice limits config ('{{ user.name }}')" + when: user.uid is defined + register: users_limit_state + vars: + memory_max: "{{ (user.memory_max is defined and user.memory_max != '') | ternary(user.memory_max, common_memory_max) | default('') }}" + swap_max: "{{ (user.swap_max is defined and user.swap_max != '') | ternary(user.swap_max, common_swap_max) | default('') }}" + cpu_quota: "{{ (user.cpu_quota is defined and user.cpu_quota != '') | ternary(user.cpu_quota, common_cpu_quota) | default('') }}" + ansible.builtin.template: + src: slice.j2 + dest: "/etc/systemd/system/user-{{ user.uid }}.slice.d/50-limits.conf" + owner: root + group: root + mode: '0750' + +- name: "Create | Enable user systemd service ('{{ user.name }}')" # noqa: no-handler + when: users_limit_state.changed + ansible.builtin.systemd_service: + name: user@{{ user.uid }}.service + enabled: true + +# User service cannot be restarted if: +# - the account is expired (we check for that) +# - password change was enforced +# Sometimes restarting fails with obscure 'status=219/CGROUP', but works fine after a retry +# Didn't debug this yet, sorry ;( +- name: "Restart user service (systemd limits) ('{{ user.name }}')" + when: + - users_limit_state.changed + - user.expires is undefined or (user.expires | to_datetime).strftime('%s') > now(fmt='%s') + retries: 3 + ansible.builtin.systemd_service: + name: user@{{ user.uid }}.service + state: restarted + daemon_reload: true diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml new file mode 100644 index 0000000..6d3f166 --- /dev/null +++ b/roles/users/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: "Include OpenSSH client installation tasks" + ansible.builtin.include_tasks: openssh.yml + +- name: "Include user definition vars" + when: (manifest_path is defined) and (manifest_path != "") and users is not defined + ansible.builtin.include_vars: + file: "{{ manifest_path }}" + +- name: "Create user accounts" + loop: "{{ users }}" + loop_control: + loop_var: user + ansible.builtin.include_tasks: create.yml diff --git a/roles/users/tasks/openssh.yml b/roles/users/tasks/openssh.yml new file mode 100644 index 0000000..753bf52 --- /dev/null +++ b/roles/users/tasks/openssh.yml @@ -0,0 +1,14 @@ +--- +- name: "OpenSSH | Ensure openssh client tools are installed" + when: ansible_os_family == "Debian" + ansible.builtin.apt: + name: openssh-client + state: present + cache_valid_time: 3000 + +- name: "OpenSSH | Ensure openssh client tools are installed" + when: ansible_os_family == "RedHat" + ansible.builtin.dnf: + name: openssh-clients + state: present + update_cache: true diff --git a/roles/users/templates/slice.j2 b/roles/users/templates/slice.j2 new file mode 100644 index 0000000..5a9b1f5 --- /dev/null +++ b/roles/users/templates/slice.j2 @@ -0,0 +1,12 @@ +[Slice] +{% if memory_max is defined and memory_max != "" %} +MemoryAccounting=1 +MemoryMax={{ memory_max }} +MemorySwapMax={{ swap_max }} +{% else %} +{% endif %} +{% if cpu_quota is defined and cpu_quota != "" %} +CPUAccounting=1 +CPUQuota={{ cpu_quota }} +{% else %} +{% endif %} diff --git a/roles/users/vars/main.yml b/roles/users/vars/main.yml new file mode 100644 index 0000000..4e004e9 --- /dev/null +++ b/roles/users/vars/main.yml @@ -0,0 +1,4 @@ +--- +# vars file for user +inv_groupname: "{{ hostvars[inventory_hostname].group_names[0] }}" +inv_hostname: "{{ inventory_hostname }}" From 7056bfe278640f7dafe2a49fbacf325c8ec4de0b Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:39:05 +0400 Subject: [PATCH 24/37] Fix role name --- roles/users/molecule/default/converge.yml | 2 +- roles/users/molecule/users_from_var/converge.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/users/molecule/default/converge.yml b/roles/users/molecule/default/converge.yml index 38fd392..7cf7ea1 100644 --- a/roles/users/molecule/default/converge.yml +++ b/roles/users/molecule/default/converge.yml @@ -3,7 +3,7 @@ hosts: all roles: - - role: genlab.users + - role: genlab.common.users manifest_path: "users.yml" common_memory_max: "500M" common_swap_max: "2G" diff --git a/roles/users/molecule/users_from_var/converge.yml b/roles/users/molecule/users_from_var/converge.yml index a6e93e9..4993887 100644 --- a/roles/users/molecule/users_from_var/converge.yml +++ b/roles/users/molecule/users_from_var/converge.yml @@ -3,7 +3,7 @@ hosts: all roles: - - role: genlab.users + - role: genlab.common.users manifest_path: "some-none-existent-file" # the 'users' variable is supposed to take precedence common_memory_max: "500M" common_swap_max: "2G" From 1a9cb69d58c3d1c97e501cce8b7bebcdf7ef84f8 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:39:17 +0400 Subject: [PATCH 25/37] Update metadata --- roles/users/meta/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/users/meta/main.yml b/roles/users/meta/main.yml index 06fac82..03efaab 100644 --- a/roles/users/meta/main.yml +++ b/roles/users/meta/main.yml @@ -3,13 +3,13 @@ galaxy_info: namespace: genlab author: Alexander Gorelyshev company: Genlab, LLC - description: Deploy user accounts + description: Configure user accounts license: GPL-2.0-or-later min_ansible_version: "2.1" platforms: - name: Ubuntu - versions: [ "focal", "jammy", "noble" ] + versions: ["jammy", "noble"] galaxy_tags: [] From 0c46455a638e0b6669e0a34ff12cbef0d36f8a9f Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:44:02 +0400 Subject: [PATCH 26/37] Fix linting issues --- roles/users/defaults/main.yml | 2 +- roles/users/molecule/default/verify.yml | 16 ++++++++-------- roles/users/molecule/users_from_var/verify.yml | 16 ++++++++-------- roles/users/tasks/create.yml | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/roles/users/defaults/main.yml b/roles/users/defaults/main.yml index cce8b27..755fb15 100644 --- a/roles/users/defaults/main.yml +++ b/roles/users/defaults/main.yml @@ -1,2 +1,2 @@ --- -default_home_root: /home +users_default_home_root: /home diff --git a/roles/users/molecule/default/verify.yml b/roles/users/molecule/default/verify.yml index a911e9d..da1af66 100644 --- a/roles/users/molecule/default/verify.yml +++ b/roles/users/molecule/default/verify.yml @@ -6,14 +6,14 @@ tasks: - name: "Get user info from /etc/passwd" - register: etc_passwd + register: users_etc_passwd changed_when: false ansible.builtin.shell: cmd: set -o pipefail; cat /etc/passwd | grep user executable: /bin/bash - name: "Get user info from /etc/shadow" - register: etc_shadow + register: users_etc_shadow changed_when: false ansible.builtin.shell: cmd: set -o pipefail; cat /etc/shadow | grep user @@ -30,21 +30,21 @@ - "user1:x:1002:1002:User One:/home/user1:/bin/sh" ansible.builtin.assert: that: - - etc_passwd.stdout_lines == expected_passwd - - etc_shadow.stdout_lines == expected_shadow + - users_etc_passwd.stdout_lines == expected_passwd + - users_etc_shadow.stdout_lines == expected_shadow - name: "Get SSH key contents for user1" - register: user1_ssh_key + register: users_user1_ssh_key changed_when: false ansible.builtin.command: cmd: cat /home/user1/.ssh/id_ed25519.pub - name: "Verify the SSH key contents for user1" ansible.builtin.assert: - that: "'user1@ubuntu' in user1_ssh_key.stdout" + that: "'user1@ubuntu' in users_user1_ssh_key.stdout" - name: "Get the user slice drop-in for user0" - register: user0_slice_dropin + register: users_user0_slice_dropin changed_when: false ansible.builtin.command: cmd: cat /etc/systemd/system/user-1001.slice.d/50-limits.conf @@ -59,6 +59,6 @@ - "CPUAccounting=1" - "CPUQuota=100%" ansible.builtin.assert: - that: "user0_slice_dropin.stdout_lines == expected" + that: "users_user0_slice_dropin.stdout_lines == expected" success_msg: "Got the expected limits slice drop-in" fail_msg: "Unexpected limits slice drop-in contents" diff --git a/roles/users/molecule/users_from_var/verify.yml b/roles/users/molecule/users_from_var/verify.yml index a911e9d..da1af66 100644 --- a/roles/users/molecule/users_from_var/verify.yml +++ b/roles/users/molecule/users_from_var/verify.yml @@ -6,14 +6,14 @@ tasks: - name: "Get user info from /etc/passwd" - register: etc_passwd + register: users_etc_passwd changed_when: false ansible.builtin.shell: cmd: set -o pipefail; cat /etc/passwd | grep user executable: /bin/bash - name: "Get user info from /etc/shadow" - register: etc_shadow + register: users_etc_shadow changed_when: false ansible.builtin.shell: cmd: set -o pipefail; cat /etc/shadow | grep user @@ -30,21 +30,21 @@ - "user1:x:1002:1002:User One:/home/user1:/bin/sh" ansible.builtin.assert: that: - - etc_passwd.stdout_lines == expected_passwd - - etc_shadow.stdout_lines == expected_shadow + - users_etc_passwd.stdout_lines == expected_passwd + - users_etc_shadow.stdout_lines == expected_shadow - name: "Get SSH key contents for user1" - register: user1_ssh_key + register: users_user1_ssh_key changed_when: false ansible.builtin.command: cmd: cat /home/user1/.ssh/id_ed25519.pub - name: "Verify the SSH key contents for user1" ansible.builtin.assert: - that: "'user1@ubuntu' in user1_ssh_key.stdout" + that: "'user1@ubuntu' in users_user1_ssh_key.stdout" - name: "Get the user slice drop-in for user0" - register: user0_slice_dropin + register: users_user0_slice_dropin changed_when: false ansible.builtin.command: cmd: cat /etc/systemd/system/user-1001.slice.d/50-limits.conf @@ -59,6 +59,6 @@ - "CPUAccounting=1" - "CPUQuota=100%" ansible.builtin.assert: - that: "user0_slice_dropin.stdout_lines == expected" + that: "users_user0_slice_dropin.stdout_lines == expected" success_msg: "Got the expected limits slice drop-in" fail_msg: "Unexpected limits slice drop-in contents" diff --git a/roles/users/tasks/create.yml b/roles/users/tasks/create.yml index 309a9c5..f952d08 100644 --- a/roles/users/tasks/create.yml +++ b/roles/users/tasks/create.yml @@ -16,7 +16,7 @@ - name: "Create | Create user account ('{{ user.name }}')" vars: - home_root: "{{ user.home_root is defined | ternary(user.home_root, default_home_root) }}" + home_root: "{{ user.home_root is defined | ternary(user.home_root, users_default_home_root) }}" ansible.builtin.user: name: "{{ user.name }}" state: "{{ user.state }}" From b0fbb7f17c4c44072581be9fc2f798816d865344 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:44:17 +0400 Subject: [PATCH 27/37] Drop unused variables --- roles/users/vars/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/roles/users/vars/main.yml b/roles/users/vars/main.yml index 4e004e9..ed97d53 100644 --- a/roles/users/vars/main.yml +++ b/roles/users/vars/main.yml @@ -1,4 +1 @@ --- -# vars file for user -inv_groupname: "{{ hostvars[inventory_hostname].group_names[0] }}" -inv_hostname: "{{ inventory_hostname }}" From 4a167c47985cf3265a14ec6d2c8b22cd0775e603 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:48:41 +0400 Subject: [PATCH 28/37] Move the user manifest into a subfolder to work around `ansible-lint` --- roles/users/molecule/default/{ => configuration}/users.yml | 0 roles/users/molecule/default/converge.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename roles/users/molecule/default/{ => configuration}/users.yml (100%) diff --git a/roles/users/molecule/default/users.yml b/roles/users/molecule/default/configuration/users.yml similarity index 100% rename from roles/users/molecule/default/users.yml rename to roles/users/molecule/default/configuration/users.yml diff --git a/roles/users/molecule/default/converge.yml b/roles/users/molecule/default/converge.yml index 7cf7ea1..0327060 100644 --- a/roles/users/molecule/default/converge.yml +++ b/roles/users/molecule/default/converge.yml @@ -4,7 +4,7 @@ roles: - role: genlab.common.users - manifest_path: "users.yml" + manifest_path: "configuration/users.yml" common_memory_max: "500M" common_swap_max: "2G" common_cpu_quota: "100%" From 794d0e1dc499e666e272e5e45bf2f4ada8e2ff0f Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:56:10 +0400 Subject: [PATCH 29/37] Bump Galaxy collection version --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index c26b9f9..8c51504 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.22.0 +version: 0.23.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) From 4beafb13f137b54693df22d7c0be88ce09cedcdd Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 13:56:23 +0400 Subject: [PATCH 30/37] Add a link to the role's README file --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd37b07..5d75026 100644 --- a/README.md +++ b/README.md @@ -25,5 +25,6 @@ - [smartctl_exporter](roles/smartctl_exporter/README.md) - [swapfile](roles/swapfile/README.md) - [ufw](roles/ufw/README.md) +- [users](roles/users/README.md) - [wg_hub](roles/wg_hub/README.md) -- [wg_spoke](roles/wg_spoke/README.md) \ No newline at end of file +- [wg_spoke](roles/wg_spoke/README.md) From 3f7602cc704308cb71d47e7293fa372079294533 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 13:09:02 +0300 Subject: [PATCH 31/37] add verify tasks --- roles/sshd/molecule/default/verify.yml | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/roles/sshd/molecule/default/verify.yml b/roles/sshd/molecule/default/verify.yml index 6287e65..5e1bb57 100644 --- a/roles/sshd/molecule/default/verify.yml +++ b/roles/sshd/molecule/default/verify.yml @@ -6,6 +6,39 @@ any_errors_fatal: true tasks: - - name: Blank - ansible.builtin.command: echo + - name: Gather service facts + ansible.builtin.service_facts: + + - name: Ensure sshd is running + ansible.builtin.assert: + that: + - ansible_facts.services['ssh.service'].state == 'running' + + - name: Ensure sshd_config syntax is OK + ansible.builtin.command: sshd -t -f /etc/ssh/sshd_config changed_when: false + + - name: Ensure main parameteres are applied + ansible.builtin.shell: | + set -o pipefail ; + sshd -T | egrep -i ' + ^protocol 2| + ^permitrootlogin no| + ^passwordauthentication no| + ^pubkeyauthentication yes + ' + args: + executable: /bin/bash + changed_when: false + + - name: Get sshd_config stats + ansible.builtin.stat: + path: /etc/ssh/sshd_config + register: sshd_conf + + - name: Ensure sshd_config file is secure + ansible.builtin.assert: + that: + - sshd_conf.stat.uid == 0 + - sshd_conf.stat.gid == 0 + - sshd_conf.stat.mode == '0600' From a27c38e712d4d7e962eb6d88004d8d1c0fb7fc6d Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 13:26:56 +0300 Subject: [PATCH 32/37] update meta --- galaxy.yml | 2 +- roles/sshd/README.md | 2 +- roles/sshd/meta/main.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/galaxy.yml b/galaxy.yml index 8c51504..a18146b 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.23.0 +version: 0.24.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/roles/sshd/README.md b/roles/sshd/README.md index 1c4d1d9..3f3962d 100644 --- a/roles/sshd/README.md +++ b/roles/sshd/README.md @@ -23,7 +23,7 @@ Example Playbook ```yaml roles: - - role: genlab.sshd + - role: genlab.common.sshd ``` License diff --git a/roles/sshd/meta/main.yml b/roles/sshd/meta/main.yml index 7158cf1..a365305 100644 --- a/roles/sshd/meta/main.yml +++ b/roles/sshd/meta/main.yml @@ -10,7 +10,7 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: ["focal", "jammy"] + versions: ["jammy", "noble"] galaxy_tags: [] From 02178246c9bef8390da5f7e54a6894f32752dcb6 Mon Sep 17 00:00:00 2001 From: Alexander Gorelyshev Date: Thu, 18 Dec 2025 14:35:42 +0400 Subject: [PATCH 33/37] Add `passlib` - a missing `users` role control node dependency --- ci/requirements.txt | 1 + roles/users/README.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/ci/requirements.txt b/ci/requirements.txt index 456b2b7..fcd67d5 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -1 +1,2 @@ ansible-core==2.18.11 +passlib==1.7.4 diff --git a/roles/users/README.md b/roles/users/README.md index 90f8563..30fd61c 100644 --- a/roles/users/README.md +++ b/roles/users/README.md @@ -18,9 +18,15 @@ Check out [corvus-migratorius/ansible-disk-quotas](https://github.com/corvus-mig Requirements ------------ +Target node: + - systemd - openssh +Controller: + +- `passlib` (for working with user passwords) + Role Variables -------------- From ee4cec563a9ff77901c3547c0d9519c867acab87 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 14:59:07 +0300 Subject: [PATCH 34/37] add ssh connection test --- roles/sshd/molecule/default/verify.yml | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/roles/sshd/molecule/default/verify.yml b/roles/sshd/molecule/default/verify.yml index 5e1bb57..29b09d7 100644 --- a/roles/sshd/molecule/default/verify.yml +++ b/roles/sshd/molecule/default/verify.yml @@ -42,3 +42,35 @@ - sshd_conf.stat.uid == 0 - sshd_conf.stat.gid == 0 - sshd_conf.stat.mode == '0600' + + - name: Create test user + ansible.builtin.user: + name: test + create_home: true + shell: /bin/bash + + - name: Create .ssh directory + ansible.builtin.file: + path: /home/test/.ssh/ + state: directory + mode: '0700' + owner: test + group: test + + - name: Generate ssh keys + community.crypto.openssh_keypair: + path: /home/test/.ssh/id_rsa + owner: test + group: test + mode: '0600' + register: sshd_key_result + + - name: Put public key to test user + ansible.posix.authorized_key: + user: test + key: "{{ sshd_key_result.public_key }}" + state: present + + - name: Test ssh connection + ansible.builtin.command: ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /home/test/.ssh/id_rsa test@localhost hostname + changed_when: false From 2c0224566e634ea424e309a948d75d582b815afc Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 15:09:10 +0300 Subject: [PATCH 35/37] add docker_ubuntu role --- roles/docker_ubuntu/README.md | 41 +++++++++++ roles/docker_ubuntu/defaults/main.yml | 5 ++ roles/docker_ubuntu/handlers/main.yml | 11 +++ roles/docker_ubuntu/meta/main.yml | 17 +++++ .../molecule/default/converge.yml | 9 +++ .../molecule/default/molecule.yml | 27 +++++++ .../docker_ubuntu/molecule/default/verify.yml | 31 ++++++++ roles/docker_ubuntu/tasks/main.yml | 71 +++++++++++++++++++ roles/docker_ubuntu/templates/daemon.json.j2 | 3 + roles/docker_ubuntu/vars/main.yml | 2 + 10 files changed, 217 insertions(+) create mode 100644 roles/docker_ubuntu/README.md create mode 100644 roles/docker_ubuntu/defaults/main.yml create mode 100644 roles/docker_ubuntu/handlers/main.yml create mode 100644 roles/docker_ubuntu/meta/main.yml create mode 100644 roles/docker_ubuntu/molecule/default/converge.yml create mode 100644 roles/docker_ubuntu/molecule/default/molecule.yml create mode 100644 roles/docker_ubuntu/molecule/default/verify.yml create mode 100644 roles/docker_ubuntu/tasks/main.yml create mode 100644 roles/docker_ubuntu/templates/daemon.json.j2 create mode 100644 roles/docker_ubuntu/vars/main.yml diff --git a/roles/docker_ubuntu/README.md b/roles/docker_ubuntu/README.md new file mode 100644 index 0000000..b196083 --- /dev/null +++ b/roles/docker_ubuntu/README.md @@ -0,0 +1,41 @@ +docker_ubuntu +========= + +Install Docker ecosystem on Ubuntu. + +[![lint](https://github.com/corvus-migratorius/ansible-docker-ubuntu/actions/workflows/lint.yaml/badge.svg)](https://github.com/corvus-migratorius/ansible-docker-ubuntu/actions/workflows/lint.yaml) + +[![molecule](https://github.com/corvus-migratorius/ansible-docker-ubuntu/actions/workflows/molecule.yaml/badge.svg)](https://github.com/corvus-migratorius/ansible-docker-ubuntu/actions/workflows/molecule.yaml) + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: docker_ubuntu +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/docker_ubuntu/defaults/main.yml b/roles/docker_ubuntu/defaults/main.yml new file mode 100644 index 0000000..b9fac9f --- /dev/null +++ b/roles/docker_ubuntu/defaults/main.yml @@ -0,0 +1,5 @@ +--- +du_docker_version: "27.3.1" +du_registry_mirrors: + - https://docker.io + - https://mirror.gcr.io diff --git a/roles/docker_ubuntu/handlers/main.yml b/roles/docker_ubuntu/handlers/main.yml new file mode 100644 index 0000000..73b8719 --- /dev/null +++ b/roles/docker_ubuntu/handlers/main.yml @@ -0,0 +1,11 @@ +--- +# handlers file for playbooks/roles/docker_ubuntu +- name: "Restart Docker daemon" + ansible.builtin.systemd_service: + name: docker + state: restarted + daemon_reload: true + +- name: "Sanity check" # noqa: no-changed-when + ansible.builtin.command: + cmd: "docker run --rm --pull=always hello-world" diff --git a/roles/docker_ubuntu/meta/main.yml b/roles/docker_ubuntu/meta/main.yml new file mode 100644 index 0000000..332cab7 --- /dev/null +++ b/roles/docker_ubuntu/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: docker_ubuntu + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Install Docker ecosystem on Ubuntu" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: [ "focal", "jammy", "noble" ] + + galaxy_tags: [ "docker" ] + +dependencies: [] diff --git a/roles/docker_ubuntu/molecule/default/converge.yml b/roles/docker_ubuntu/molecule/default/converge.yml new file mode 100644 index 0000000..b67f3d0 --- /dev/null +++ b/roles/docker_ubuntu/molecule/default/converge.yml @@ -0,0 +1,9 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.docker_ubuntu + du_docker_version: "27.3.1" + du_registry_mirrors: + - https://docker.io + - https://mirror.gcr.io diff --git a/roles/docker_ubuntu/molecule/default/molecule.yml b/roles/docker_ubuntu/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/docker_ubuntu/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/docker_ubuntu/molecule/default/verify.yml b/roles/docker_ubuntu/molecule/default/verify.yml new file mode 100644 index 0000000..ccc781d --- /dev/null +++ b/roles/docker_ubuntu/molecule/default/verify.yml @@ -0,0 +1,31 @@ +--- +- name: Verify + hosts: all + gather_facts: true + any_errors_fatal: true + + tasks: + # https://github.com/ansible/molecule/issues/3587#issuecomment-1158650179 + - name: "Include default vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: [ 'yml' ] + + - name: "Sanity check Docker by running a `hello-world` container" # noqa: no-changed-when + ansible.builtin.command: + cmd: "docker run --rm hello-world" + + - name: "Check if `Compose` is available from PATH" # noqa: no-changed-when + ansible.builtin.command: + cmd: "docker compose version" + + - name: "Check if Docker has the expected version" # noqa: no-changed-when + register: docker_version + ansible.builtin.command: + cmd: "docker version --format '{{ '{{' }} .Server.Version {{ '}}' }}'" + + - name: "Check if Docker has the expected version" # noqa: no-changed-when + ansible.builtin.assert: + that: "du_docker_version in docker_version.stdout" + success_msg: "Docker has the expected version ({{ docker_version.stdout }} )" + fail_msg: "Unexpected Docker version ({{ docker_version.stdout }})" diff --git a/roles/docker_ubuntu/tasks/main.yml b/roles/docker_ubuntu/tasks/main.yml new file mode 100644 index 0000000..9d0fa75 --- /dev/null +++ b/roles/docker_ubuntu/tasks/main.yml @@ -0,0 +1,71 @@ +--- +# REF: https://www.digitalocean.com/community/tutorials/how-to-use-ansible-to-install-and-set-up-docker-on-ubuntu-20-04 + +- name: "Install aptitude" + ansible.builtin.apt: + name: aptitude + state: present + update_cache: true + +- name: "Install required system packages" + ansible.builtin.apt: + pkg: + - apt-transport-https + - ca-certificates + - curl + - gpg-agent + - python3-pip + - python3-setuptools + - software-properties-common + - virtualenv + state: present + update_cache: true + +- name: "Add Docker GPG apt Key" + ansible.builtin.apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + +- name: "Add Docker Repository" + ansible.builtin.apt_repository: + repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable" + state: present + +- name: "Install Docker ecosystem" + notify: "Sanity check" + ansible.builtin.apt: + pkg: + - docker-ce={{ du_docker_version_full }} + - docker-ce-cli={{ du_docker_version_full }} + state: present + update_cache: true + +- name: "Ensure the 'docker' group exists" + ansible.builtin.group: + name: docker + state: present + system: true + +- name: "Install Python tools for Docker" + ansible.builtin.pip: + name: [ docker ] + break_system_packages: true + +- name: "Configure Docker daemon" + notify: + - "Restart Docker daemon" + - "Sanity check" + ansible.builtin.template: + src: daemon.json.j2 + dest: /etc/docker/daemon.json + owner: root + group: root + mode: "0664" + validate: "dockerd --validate --config-file=%s" + +- name: "Spin up the Docker system service" + notify: "Sanity check" + ansible.builtin.systemd: + name: docker + state: started + enabled: true diff --git a/roles/docker_ubuntu/templates/daemon.json.j2 b/roles/docker_ubuntu/templates/daemon.json.j2 new file mode 100644 index 0000000..b557ee6 --- /dev/null +++ b/roles/docker_ubuntu/templates/daemon.json.j2 @@ -0,0 +1,3 @@ +{ + "registry-mirrors": {{ du_registry_mirrors | to_json }} +} diff --git a/roles/docker_ubuntu/vars/main.yml b/roles/docker_ubuntu/vars/main.yml new file mode 100644 index 0000000..1455687 --- /dev/null +++ b/roles/docker_ubuntu/vars/main.yml @@ -0,0 +1,2 @@ +--- +du_docker_version_full: "5:{{ du_docker_version }}-1~ubuntu.{{ ansible_lsb.release }}~{{ ansible_lsb.codename }}" From 236c4cf189a11015a8b228e4f34975862f43bcf0 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 15:26:34 +0300 Subject: [PATCH 36/37] fix ansible-lint errors --- roles/docker_ubuntu/defaults/main.yml | 4 ++-- roles/docker_ubuntu/meta/main.yml | 4 ++-- roles/docker_ubuntu/molecule/default/converge.yml | 6 +++--- roles/docker_ubuntu/molecule/default/verify.yml | 12 ++++++------ roles/docker_ubuntu/tasks/main.yml | 6 +++--- roles/docker_ubuntu/templates/daemon.json.j2 | 2 +- roles/docker_ubuntu/vars/main.yml | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/roles/docker_ubuntu/defaults/main.yml b/roles/docker_ubuntu/defaults/main.yml index b9fac9f..85de886 100644 --- a/roles/docker_ubuntu/defaults/main.yml +++ b/roles/docker_ubuntu/defaults/main.yml @@ -1,5 +1,5 @@ --- -du_docker_version: "27.3.1" -du_registry_mirrors: +docker_ubuntu_version: "27.3.1" +docker_ubuntu_registry_mirrors: - https://docker.io - https://mirror.gcr.io diff --git a/roles/docker_ubuntu/meta/main.yml b/roles/docker_ubuntu/meta/main.yml index 332cab7..f6eca81 100644 --- a/roles/docker_ubuntu/meta/main.yml +++ b/roles/docker_ubuntu/meta/main.yml @@ -10,8 +10,8 @@ galaxy_info: platforms: - name: "Ubuntu" - versions: [ "focal", "jammy", "noble" ] + versions: ["focal", "jammy", "noble"] - galaxy_tags: [ "docker" ] + galaxy_tags: ["docker"] dependencies: [] diff --git a/roles/docker_ubuntu/molecule/default/converge.yml b/roles/docker_ubuntu/molecule/default/converge.yml index b67f3d0..d3e24f2 100644 --- a/roles/docker_ubuntu/molecule/default/converge.yml +++ b/roles/docker_ubuntu/molecule/default/converge.yml @@ -2,8 +2,8 @@ - name: Converge hosts: all roles: - - role: genlab.docker_ubuntu - du_docker_version: "27.3.1" - du_registry_mirrors: + - role: genlab.common.docker_ubuntu + docker_ubuntu_version: "27.3.1" + docker_ubuntu_registry_mirrors: - https://docker.io - https://mirror.gcr.io diff --git a/roles/docker_ubuntu/molecule/default/verify.yml b/roles/docker_ubuntu/molecule/default/verify.yml index ccc781d..a384184 100644 --- a/roles/docker_ubuntu/molecule/default/verify.yml +++ b/roles/docker_ubuntu/molecule/default/verify.yml @@ -9,7 +9,7 @@ - name: "Include default vars" ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" - extensions: [ 'yml' ] + extensions: ['yml'] - name: "Sanity check Docker by running a `hello-world` container" # noqa: no-changed-when ansible.builtin.command: @@ -19,13 +19,13 @@ ansible.builtin.command: cmd: "docker compose version" - - name: "Check if Docker has the expected version" # noqa: no-changed-when - register: docker_version + - name: "Get docker version" # noqa: no-changed-when + register: docker_ubuntu_current_version ansible.builtin.command: cmd: "docker version --format '{{ '{{' }} .Server.Version {{ '}}' }}'" - name: "Check if Docker has the expected version" # noqa: no-changed-when ansible.builtin.assert: - that: "du_docker_version in docker_version.stdout" - success_msg: "Docker has the expected version ({{ docker_version.stdout }} )" - fail_msg: "Unexpected Docker version ({{ docker_version.stdout }})" + that: "docker_ubuntu_version in docker_ubuntu_current_version.stdout" + success_msg: "Docker has the expected version ({{ docker_ubuntu_current_version.stdout }} )" + fail_msg: "Unexpected Docker version ({{ docker_ubuntu_current_version.stdout }})" diff --git a/roles/docker_ubuntu/tasks/main.yml b/roles/docker_ubuntu/tasks/main.yml index 9d0fa75..ec14880 100644 --- a/roles/docker_ubuntu/tasks/main.yml +++ b/roles/docker_ubuntu/tasks/main.yml @@ -35,8 +35,8 @@ notify: "Sanity check" ansible.builtin.apt: pkg: - - docker-ce={{ du_docker_version_full }} - - docker-ce-cli={{ du_docker_version_full }} + - docker-ce={{ docker_ubuntu_version_full }} + - docker-ce-cli={{ docker_ubuntu_version_full }} state: present update_cache: true @@ -48,7 +48,7 @@ - name: "Install Python tools for Docker" ansible.builtin.pip: - name: [ docker ] + name: [docker] break_system_packages: true - name: "Configure Docker daemon" diff --git a/roles/docker_ubuntu/templates/daemon.json.j2 b/roles/docker_ubuntu/templates/daemon.json.j2 index b557ee6..d128e9a 100644 --- a/roles/docker_ubuntu/templates/daemon.json.j2 +++ b/roles/docker_ubuntu/templates/daemon.json.j2 @@ -1,3 +1,3 @@ { - "registry-mirrors": {{ du_registry_mirrors | to_json }} + "registry-mirrors": {{ docker_ubuntu_registry_mirrors | to_json }} } diff --git a/roles/docker_ubuntu/vars/main.yml b/roles/docker_ubuntu/vars/main.yml index 1455687..3075048 100644 --- a/roles/docker_ubuntu/vars/main.yml +++ b/roles/docker_ubuntu/vars/main.yml @@ -1,2 +1,2 @@ --- -du_docker_version_full: "5:{{ du_docker_version }}-1~ubuntu.{{ ansible_lsb.release }}~{{ ansible_lsb.codename }}" +docker_ubuntu_version_full: "5:{{ docker_ubuntu_version }}-1~ubuntu.{{ ansible_lsb.release }}~{{ ansible_lsb.codename }}" From f541060cc13143addc588819b4f5b8b9075b8950 Mon Sep 17 00:00:00 2001 From: Sergey Malyuk Date: Thu, 18 Dec 2025 15:28:18 +0300 Subject: [PATCH 37/37] update meta --- README.md | 1 + galaxy.yml | 2 +- roles/docker_ubuntu/README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d75026..6322429 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ - [borgmatic](roles/borgmatic/README.md) - [curl_scheduled](roles/curl_scheduled/README.md) - [dnsmasq](roles/dnsmasq/README.md) +- [docker_ubuntu](roles/docker_ubuntu/README.md) - [grafana](roles/grafana/README.md) - [ipmi_exporter](roles/ipmi_exporter/README.md) - [karma](roles/karma/README.md) diff --git a/galaxy.yml b/galaxy.yml index 8c51504..dd3e469 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.23.0 +version: 0.25.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/roles/docker_ubuntu/README.md b/roles/docker_ubuntu/README.md index b196083..8760978 100644 --- a/roles/docker_ubuntu/README.md +++ b/roles/docker_ubuntu/README.md @@ -27,7 +27,7 @@ Example Playbook ```yaml roles: - - role: docker_ubuntu + - role: genlab.common.docker_ubuntu ``` License