Merge branch 'master' into add-kuma
This commit is contained in:
@@ -9,17 +9,24 @@
|
||||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
- [smartctl_exporter](roles/smartctl_exporter/README.md)
|
||||
- [sshd](roles/sshd/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)
|
||||
- [wg_spoke](roles/wg_spoke/README.md)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
ansible-core==2.18.11
|
||||
passlib==1.7.4
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
namespace: genlab
|
||||
name: common
|
||||
version: 0.18.0
|
||||
version: 0.25.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Alexander Gorelyshev (corvus-migratorius@proton.me)
|
||||
|
||||
41
roles/docker_ubuntu/README.md
Normal file
41
roles/docker_ubuntu/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
docker_ubuntu
|
||||
=========
|
||||
|
||||
Install Docker ecosystem on Ubuntu.
|
||||
|
||||
[](https://github.com/corvus-migratorius/ansible-docker-ubuntu/actions/workflows/lint.yaml)
|
||||
|
||||
[](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
|
||||
5
roles/docker_ubuntu/defaults/main.yml
Normal file
5
roles/docker_ubuntu/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
docker_ubuntu_version: "27.3.1"
|
||||
docker_ubuntu_registry_mirrors:
|
||||
- https://docker.io
|
||||
- https://mirror.gcr.io
|
||||
11
roles/docker_ubuntu/handlers/main.yml
Normal file
11
roles/docker_ubuntu/handlers/main.yml
Normal 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"
|
||||
17
roles/docker_ubuntu/meta/main.yml
Normal file
17
roles/docker_ubuntu/meta/main.yml
Normal 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: []
|
||||
9
roles/docker_ubuntu/molecule/default/converge.yml
Normal file
9
roles/docker_ubuntu/molecule/default/converge.yml
Normal 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
|
||||
27
roles/docker_ubuntu/molecule/default/molecule.yml
Normal file
27
roles/docker_ubuntu/molecule/default/molecule.yml
Normal 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 .
|
||||
31
roles/docker_ubuntu/molecule/default/verify.yml
Normal file
31
roles/docker_ubuntu/molecule/default/verify.yml
Normal 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 }})"
|
||||
71
roles/docker_ubuntu/tasks/main.yml
Normal file
71
roles/docker_ubuntu/tasks/main.yml
Normal 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
|
||||
3
roles/docker_ubuntu/templates/daemon.json.j2
Normal file
3
roles/docker_ubuntu/templates/daemon.json.j2
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"registry-mirrors": {{ docker_ubuntu_registry_mirrors | to_json }}
|
||||
}
|
||||
2
roles/docker_ubuntu/vars/main.yml
Normal file
2
roles/docker_ubuntu/vars/main.yml
Normal 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
40
roles/mkfs/README.md
Normal 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
|
||||
5
roles/mkfs/defaults/main.yml
Normal file
5
roles/mkfs/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
# defaults file for mkfs
|
||||
mkfs_opts: ""
|
||||
mkfs_state: present
|
||||
mkfs_force: false
|
||||
1
roles/mkfs/handlers/main.yml
Normal file
1
roles/mkfs/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/mkfs/meta/main.yml
Normal file
17
roles/mkfs/meta/main.yml
Normal 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: []
|
||||
22
roles/mkfs/molecule/default/converge.yml
Normal file
22
roles/mkfs/molecule/default/converge.yml
Normal 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
|
||||
27
roles/mkfs/molecule/default/molecule.yml
Normal file
27
roles/mkfs/molecule/default/molecule.yml
Normal 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 .
|
||||
25
roles/mkfs/molecule/default/verify.yml
Normal file
25
roles/mkfs/molecule/default/verify.yml
Normal 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
|
||||
8
roles/mkfs/tasks/main.yml
Normal file
8
roles/mkfs/tasks/main.yml
Normal 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
1
roles/mkfs/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
@@ -1,3 +0,0 @@
|
||||
# requirements file
|
||||
---
|
||||
collections: []
|
||||
34
roles/node_exporter/README.md
Normal file
34
roles/node_exporter/README.md
Normal 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
|
||||
2
roles/node_exporter/defaults/main.yml
Normal file
2
roles/node_exporter/defaults/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
node_exporter_bin_path: /usr/bin
|
||||
1
roles/node_exporter/handlers/main.yml
Normal file
1
roles/node_exporter/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/node_exporter/meta/main.yml
Normal file
17
roles/node_exporter/meta/main.yml
Normal 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: []
|
||||
6
roles/node_exporter/molecule/default/converge.yml
Normal file
6
roles/node_exporter/molecule/default/converge.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: genlab.common.node_exporter
|
||||
node_exporter_ver: "1.10.1"
|
||||
27
roles/node_exporter/molecule/default/molecule.yml
Normal file
27
roles/node_exporter/molecule/default/molecule.yml
Normal 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 .
|
||||
16
roles/node_exporter/molecule/default/verify.yml
Normal file
16
roles/node_exporter/molecule/default/verify.yml
Normal 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
|
||||
37
roles/node_exporter/tasks/install.yml
Normal file
37
roles/node_exporter/tasks/install.yml
Normal 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"
|
||||
6
roles/node_exporter/tasks/main.yml
Normal file
6
roles/node_exporter/tasks/main.yml
Normal 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"
|
||||
8
roles/node_exporter/tasks/run.yml
Normal file
8
roles/node_exporter/tasks/run.yml
Normal 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
|
||||
12
roles/node_exporter/templates/node_exporter.service.j2
Normal file
12
roles/node_exporter/templates/node_exporter.service.j2
Normal 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
|
||||
1
roles/node_exporter/vars/main.yml
Normal file
1
roles/node_exporter/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
44
roles/promtail/README.md
Normal file
44
roles/promtail/README.md
Normal 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
|
||||
7
roles/promtail/defaults/main.yml
Normal file
7
roles/promtail/defaults/main.yml
Normal 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
|
||||
11
roles/promtail/handlers/main.yml
Normal file
11
roles/promtail/handlers/main.yml
Normal 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
|
||||
14
roles/promtail/meta/main.yml
Normal file
14
roles/promtail/meta/main.yml
Normal 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: []
|
||||
5
roles/promtail/molecule/default/converge.yml
Normal file
5
roles/promtail/molecule/default/converge.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: genlab.common.promtail
|
||||
24
roles/promtail/molecule/default/molecule.yml
Normal file
24
roles/promtail/molecule/default/molecule.yml
Normal 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 .
|
||||
20
roles/promtail/molecule/default/verify.yml
Normal file
20
roles/promtail/molecule/default/verify.yml
Normal 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
|
||||
107
roles/promtail/tasks/main.yml
Normal file
107
roles/promtail/tasks/main.yml
Normal 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
|
||||
11
roles/promtail/templates/promtail.service.j2
Normal file
11
roles/promtail/templates/promtail.service.j2
Normal 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
|
||||
28
roles/promtail/templates/promtail.yml.j2
Normal file
28
roles/promtail/templates/promtail.yml.j2
Normal 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 %}
|
||||
1
roles/promtail/vars/main.yml
Normal file
1
roles/promtail/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
37
roles/sshd/README.md
Normal file
37
roles/sshd/README.md
Normal 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
|
||||
5
roles/sshd/defaults/main.yml
Normal file
5
roles/sshd/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
sshd_disable_pam: false
|
||||
sshd_password_auth: false
|
||||
sshd_challenge_response_auth: false
|
||||
sshd_gss_api_auth: false
|
||||
7
roles/sshd/handlers/main.yml
Normal file
7
roles/sshd/handlers/main.yml
Normal 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
17
roles/sshd/meta/main.yml
Normal 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: []
|
||||
5
roles/sshd/molecule/default/converge.yml
Normal file
5
roles/sshd/molecule/default/converge.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: "genlab.common.sshd"
|
||||
27
roles/sshd/molecule/default/molecule.yml
Normal file
27
roles/sshd/molecule/default/molecule.yml
Normal 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 .
|
||||
76
roles/sshd/molecule/default/verify.yml
Normal file
76
roles/sshd/molecule/default/verify.yml
Normal 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
|
||||
42
roles/sshd/tasks/algorithms.yml
Normal file
42
roles/sshd/tasks/algorithms.yml
Normal 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
|
||||
31
roles/sshd/tasks/authentication.yml
Normal file
31
roles/sshd/tasks/authentication.yml
Normal 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") }}'
|
||||
11
roles/sshd/tasks/encryption.yml
Normal file
11
roles/sshd/tasks/encryption.yml
Normal 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
|
||||
13
roles/sshd/tasks/install.yml
Normal file
13
roles/sshd/tasks/install.yml
Normal 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
23
roles/sshd/tasks/main.yml
Normal 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
|
||||
30
roles/sshd/tasks/restrictions.yml
Normal file
30
roles/sshd/tasks/restrictions.yml
Normal 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
1
roles/sshd/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
39
roles/swapfile/README.md
Normal file
39
roles/swapfile/README.md
Normal 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
|
||||
8
roles/swapfile/defaults/main.yml
Normal file
8
roles/swapfile/defaults/main.yml
Normal 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
|
||||
1
roles/swapfile/handlers/main.yml
Normal file
1
roles/swapfile/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/swapfile/meta/main.yml
Normal file
17
roles/swapfile/meta/main.yml
Normal 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: []
|
||||
19
roles/swapfile/molecule/default/converge.yml
Normal file
19
roles/swapfile/molecule/default/converge.yml
Normal 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
|
||||
29
roles/swapfile/molecule/default/molecule.yml
Normal file
29
roles/swapfile/molecule/default/molecule.yml
Normal 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 .
|
||||
26
roles/swapfile/molecule/default/verify.yml
Normal file
26
roles/swapfile/molecule/default/verify.yml
Normal 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 }}')"
|
||||
11
roles/swapfile/tasks/disable.yml
Normal file
11
roles/swapfile/tasks/disable.yml
Normal 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
|
||||
47
roles/swapfile/tasks/enable.yml
Normal file
47
roles/swapfile/tasks/enable.yml
Normal 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
|
||||
18
roles/swapfile/tasks/main.yml
Normal file
18
roles/swapfile/tasks/main.yml
Normal 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
|
||||
1
roles/swapfile/vars/main.yml
Normal file
1
roles/swapfile/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
61
roles/users/README.md
Normal file
61
roles/users/README.md
Normal 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
|
||||
2
roles/users/defaults/main.yml
Normal file
2
roles/users/defaults/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
users_default_home_root: /home
|
||||
1
roles/users/handlers/main.yml
Normal file
1
roles/users/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
16
roles/users/meta/main.yml
Normal file
16
roles/users/meta/main.yml
Normal 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: []
|
||||
22
roles/users/molecule/default/configuration/users.yml
Normal file
22
roles/users/molecule/default/configuration/users.yml
Normal 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
|
||||
10
roles/users/molecule/default/converge.yml
Normal file
10
roles/users/molecule/default/converge.yml
Normal 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%"
|
||||
27
roles/users/molecule/default/molecule.yml
Normal file
27
roles/users/molecule/default/molecule.yml
Normal 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 .
|
||||
1
roles/users/molecule/default/user0.keys
Normal file
1
roles/users/molecule/default/user0.keys
Normal file
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname
|
||||
64
roles/users/molecule/default/verify.yml
Normal file
64
roles/users/molecule/default/verify.yml
Normal 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"
|
||||
31
roles/users/molecule/users_from_var/converge.yml
Normal file
31
roles/users/molecule/users_from_var/converge.yml
Normal 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
|
||||
27
roles/users/molecule/users_from_var/molecule.yml
Normal file
27
roles/users/molecule/users_from_var/molecule.yml
Normal 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 .
|
||||
1
roles/users/molecule/users_from_var/user0.keys
Normal file
1
roles/users/molecule/users_from_var/user0.keys
Normal file
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMh9Y+wR4LH8lWJjJXqHn76kSoTRujkab+PYwD3IReFh user0@hostname
|
||||
64
roles/users/molecule/users_from_var/verify.yml
Normal file
64
roles/users/molecule/users_from_var/verify.yml
Normal 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"
|
||||
106
roles/users/tasks/create.yml
Normal file
106
roles/users/tasks/create.yml
Normal 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
|
||||
14
roles/users/tasks/main.yml
Normal file
14
roles/users/tasks/main.yml
Normal 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
|
||||
14
roles/users/tasks/openssh.yml
Normal file
14
roles/users/tasks/openssh.yml
Normal 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
|
||||
12
roles/users/templates/slice.j2
Normal file
12
roles/users/templates/slice.j2
Normal 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 %}
|
||||
1
roles/users/vars/main.yml
Normal file
1
roles/users/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
Reference in New Issue
Block a user