Merge branch 'master' into add-kuma

This commit is contained in:
Sergey Malyuk
2025-12-18 15:32:56 +03:00
89 changed files with 1827 additions and 5 deletions

View File

@@ -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: genlab.common.docker_ubuntu
```
License
-------
BSD
Author Information
------------------
corvus-migratorius@proton.me

View File

@@ -0,0 +1,5 @@
---
docker_ubuntu_version: "27.3.1"
docker_ubuntu_registry_mirrors:
- https://docker.io
- https://mirror.gcr.io

View File

@@ -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"

View File

@@ -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: []

View File

@@ -0,0 +1,9 @@
---
- name: Converge
hosts: all
roles:
- role: genlab.common.docker_ubuntu
docker_ubuntu_version: "27.3.1"
docker_ubuntu_registry_mirrors:
- https://docker.io
- https://mirror.gcr.io

View File

@@ -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 .

View File

@@ -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: "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: "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 }})"

View File

@@ -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={{ docker_ubuntu_version_full }}
- docker-ce-cli={{ docker_ubuntu_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

View File

@@ -0,0 +1,3 @@
{
"registry-mirrors": {{ docker_ubuntu_registry_mirrors | to_json }}
}

View File

@@ -0,0 +1,2 @@
---
docker_ubuntu_version_full: "5:{{ docker_ubuntu_version }}-1~ubuntu.{{ ansible_lsb.release }}~{{ ansible_lsb.codename }}"

40
roles/mkfs/README.md Normal file
View File

@@ -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

View File

@@ -0,0 +1,5 @@
---
# defaults file for mkfs
mkfs_opts: ""
mkfs_state: present
mkfs_force: false

View File

@@ -0,0 +1 @@
---

17
roles/mkfs/meta/main.yml Normal file
View File

@@ -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: []

View File

@@ -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: 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 mkfs_disk_file.stat.exists
register: mkfs_dd_output
changed_when: mkfs_dd_output.rc != 0
roles:
- role: genlab.common.mkfs
mkfs_device: "/tmp/test_file"
mkfs_type: ext3
mkfs_state: present

View File

@@ -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 .

View File

@@ -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: mkfs_loop_device
changed_when: mkfs_loop_device.rc != 0
- name: "Verify the filesystem type"
ansible.builtin.command: blkid /dev/loop3
register: mkfs_blkid_output
changed_when: mkfs_blkid_output.rc != 0
- name: "Ensure filesystem is ext3"
ansible.builtin.assert:
that:
- "'TYPE=\"ext3\"' in mkfs_blkid_output.stdout"
- name: "Explore stdout"
ansible.builtin.debug:
var: mkfs_blkid_output.stdout

View File

@@ -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 }}"

1
roles/mkfs/vars/main.yml Normal file
View File

@@ -0,0 +1 @@
---

View File

@@ -1,3 +0,0 @@
# requirements file
---
collections: []

View File

@@ -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

View File

@@ -0,0 +1,2 @@
---
node_exporter_bin_path: /usr/bin

View File

@@ -0,0 +1 @@
---

View File

@@ -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: []

View File

@@ -0,0 +1,6 @@
---
- name: Converge
hosts: all
roles:
- role: genlab.common.node_exporter
node_exporter_ver: "1.10.1"

View File

@@ -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 .

View File

@@ -0,0 +1,16 @@
# 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
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

View File

@@ -0,0 +1,37 @@
---
- name: "Install | Check if node_exporter is accessible from $PATH"
changed_when: false
failed_when: false
register: node_exporter_cmd
ansible.builtin.command:
cmd: node_exporter --version
- name: "Install | Build the exporter name"
when: "node_exporter_ver not in node_exporter_cmd.stdout"
ansible.builtin.set_fact:
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 node_exporter_cmd.stdout"
ansible.builtin.unarchive:
src: "\
https://github.com/prometheus/node_exporter/releases/download/\
v{{ node_exporter_ver }}\
{{ 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 node_exporter_cmd.stdout"
changed_when: false
ansible.builtin.command:
cmd: install /tmp/{{ node_exporter_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"

View File

@@ -0,0 +1,6 @@
---
- name: "Include installation tasks"
ansible.builtin.include_tasks: "install.yml"
- name: "Including run tasks"
ansible.builtin.include_tasks: "run.yml"

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1 @@
---

44
roles/promtail/README.md Normal file
View File

@@ -0,0 +1,44 @@
promtail
=========
Installs promtail as systemd service.
Requirements
------------
Role Variables
--------------
(all optional)
`promtail_version`: version to install
`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
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

View File

@@ -0,0 +1,7 @@
---
promtail_version: 2.7.3
promtail_positions_path: /home/promtail/positions.yaml
promtail_http_port: 9080
promtail_loki_server: localhost
promtail_loki_port: 3100
promtail_add_var_logs: true

View File

@@ -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

View File

@@ -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: []

View File

@@ -0,0 +1,5 @@
---
- name: Converge
hosts: all
roles:
- role: genlab.common.promtail

View File

@@ -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 .

View File

@@ -0,0 +1,20 @@
---
- 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']
# 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
register: promtail_this
failed_when: "promtail_this.content != 'Ready'"
ansible.builtin.uri:
url: "http://localhost:{{ promtail_http_port }}/ready"
return_content: true

View File

@@ -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

View File

@@ -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

View File

@@ -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://{{ promtail_loki_server }}:{{ promtail_loki_port }}/loki/api/v1/push"
scrape_configs:
{% if promtail_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 %}

View File

@@ -0,0 +1 @@
---

37
roles/sshd/README.md Normal file
View File

@@ -0,0 +1,37 @@
ansible-sshd
=========
Deploy a hardened sshd server
Requirements
------------
None
Role Variables
--------------
None
Dependencies
------------
None
Example Playbook
----------------
```yaml
roles:
- role: genlab.common.sshd
```
License
-------
BSD
Author Information
------------------
corvus-migratorius@proton.me

View File

@@ -0,0 +1,5 @@
---
sshd_disable_pam: false
sshd_password_auth: false
sshd_challenge_response_auth: false
sshd_gss_api_auth: false

View File

@@ -0,0 +1,7 @@
---
- name: "Restart sshd"
ansible.builtin.service:
name: ssh
state: restarted
enabled: true
daemon_reload: true

17
roles/sshd/meta/main.yml Normal file
View File

@@ -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: ["jammy", "noble"]
galaxy_tags: []
dependencies: []

View File

@@ -0,0 +1,5 @@
---
- name: Converge
hosts: all
roles:
- role: "genlab.common.sshd"

View File

@@ -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 .

View File

@@ -0,0 +1,76 @@
---
- name: Verify
hosts: all
gather_facts: false
any_errors_fatal: true
tasks:
- 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'
- 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

View File

@@ -0,0 +1,42 @@
---
# 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
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

View File

@@ -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 {{ sshd_password_auth | ternary("yes", "no") }}' }
- { regexp: '^#?\s*PermitEmptyPasswords\s+', line: 'PermitEmptyPasswords 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 sshd_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 '{{ 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 {{ sshd_password_auth | ternary("yes", "no") }}'

View File

@@ -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

View File

@@ -0,0 +1,13 @@
---
- 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
update_cache: true

23
roles/sshd/tasks/main.yml Normal file
View File

@@ -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

View File

@@ -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 {{ sshd_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"

1
roles/sshd/vars/main.yml Normal file
View File

@@ -0,0 +1 @@
---

39
roles/swapfile/README.md Normal file
View File

@@ -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

View File

@@ -0,0 +1,8 @@
---
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 }}"
swapfile_test_mode: false

View File

@@ -0,0 +1 @@
---

View File

@@ -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: []

View File

@@ -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.common.swapfile
swapfile_path: /swapfile
swapfile_size_mb: 2000
swapfile_swappiness: 30
swapfile_state: present
swapfile_test_mode: true

View File

@@ -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 .

View File

@@ -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 }}')"

View File

@@ -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: "{{ swapfile_path }}"
state: absent

View File

@@ -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: swapfile_this
failed_when: not swapfile_this.stat.exists
ansible.builtin.stat:
path: "{{ swapfile_path }}"
get_checksum: false
get_mime: false
get_attributes: false
rescue:
- name: "Enable | ensure swap file exists"
register: swapfile_create
ansible.builtin.command:
cmd: "{{ swapfile_create_command }}"
creates: "{{ swapfile_path }}"
- name: "Enable | set permissions on swap file"
ansible.builtin.file:
path: "{{ swapfile_path }}"
owner: root
group: root
mode: "0600"
- name: "Enable | make swap" # noqa: no-handler no-changed-when
register: swapfile_mkswap_result
when: swapfile_create is changed
ansible.builtin.command:
cmd: mkswap {{ swapfile_path }}
- name: "Enable | run swapon on the swap file => '{{ swapfile_path }}'"
when:
- swapfile_mkswap_result is changed
- not swapfile_test_mode
changed_when: false
ansible.builtin.command:
cmd: swapon {{ swapfile_path }}
- name: "Set swappiness to '{{ swapfile_swappiness }}'"
ansible.posix.sysctl:
name: vm.swappiness
value: "{{ swapfile_swappiness }}"
state: present

View File

@@ -0,0 +1,18 @@
---
- name: "Manage swap file entry in fstab"
ansible.posix.mount:
name: none
src: "{{ swapfile_path }}"
fstype: swap
opts: sw
state: "{{ swapfile_state }}"
- name: "Include disable swap tasks"
when: swapfile_state == 'absent'
ansible.builtin.include_tasks: disable.yml
- name: "Include enable swap tasks"
when: swapfile_state == 'present'
ansible.builtin.include_tasks: enable.yml

View File

@@ -0,0 +1 @@
---

61
roles/users/README.md Normal file
View File

@@ -0,0 +1,61 @@
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
------------
Target node:
- systemd
- openssh
Controller:
- `passlib` (for working with user passwords)
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

View File

@@ -0,0 +1,2 @@
---
users_default_home_root: /home

View File

@@ -0,0 +1 @@
---

16
roles/users/meta/main.yml Normal file
View File

@@ -0,0 +1,16 @@
galaxy_info:
role_name: users
namespace: genlab
author: Alexander Gorelyshev
company: Genlab, LLC
description: Configure user accounts
license: GPL-2.0-or-later
min_ansible_version: "2.1"
platforms:
- name: Ubuntu
versions: ["jammy", "noble"]
galaxy_tags: []
dependencies: []

View File

@@ -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

View File

@@ -0,0 +1,10 @@
---
- name: Converge
hosts: all
roles:
- role: genlab.common.users
manifest_path: "configuration/users.yml"
common_memory_max: "500M"
common_swap_max: "2G"
common_cpu_quota: "100%"

View File

@@ -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 .

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname

View File

@@ -0,0 +1,64 @@
---
- name: Verify
hosts: all
gather_facts: true
any_errors_fatal: true
tasks:
- name: "Get user info from /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: users_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:
- users_etc_passwd.stdout_lines == expected_passwd
- users_etc_shadow.stdout_lines == expected_shadow
- name: "Get SSH key contents for user1"
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 users_user1_ssh_key.stdout"
- name: "Get the user slice drop-in for user0"
register: users_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: "users_user0_slice_dropin.stdout_lines == expected"
success_msg: "Got the expected limits slice drop-in"
fail_msg: "Unexpected limits slice drop-in contents"

View File

@@ -0,0 +1,31 @@
---
- name: Converge
hosts: all
roles:
- 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"
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

View File

@@ -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 .

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname

View File

@@ -0,0 +1,64 @@
---
- name: Verify
hosts: all
gather_facts: true
any_errors_fatal: true
tasks:
- name: "Get user info from /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: users_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:
- users_etc_passwd.stdout_lines == expected_passwd
- users_etc_shadow.stdout_lines == expected_shadow
- name: "Get SSH key contents for user1"
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 users_user1_ssh_key.stdout"
- name: "Get the user slice drop-in for user0"
register: users_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: "users_user0_slice_dropin.stdout_lines == expected"
success_msg: "Got the expected limits slice drop-in"
fail_msg: "Unexpected limits slice drop-in contents"

View File

@@ -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, users_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

View File

@@ -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

View File

@@ -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

View File

@@ -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 %}

View File

@@ -0,0 +1 @@
---