Compare commits

...
12 Commits
Author SHA1 Message Date
volker.raschekandCopilot 89e36b89d6 fix(ssh): remove authorized_keys filename for 2.1.0
Molecule / Molecule (push) Successful in 5m8s
Lint Markdown files / markdown-lint (push) Successful in 8s
Ansible Linter / ansible-lint (push) Successful in 2m9s
Release Ansible Role / Release Ansible Role (push) Successful in 3m17s
Co-authored-by: Copilot <copilot@github.com>
2026-10-17 22:21:39 +02:00
volker.raschekandCopilot de280ef620 feat(ssh)!: configure keys with filename and content
Lint Markdown files / markdown-lint (push) Successful in 7s
Ansible Linter / ansible-lint (push) Successful in 2m15s
Molecule / Molecule (push) Successful in 5m45s
Release Ansible Role / Release Ansible Role (push) Successful in 1m35s
Accept SSH key content directly so callers no longer need role-specific
source directories. Update Molecule coverage and documentation.

BREAKING CHANGE: private_keys entries must be objects with filename and
content instead of file names. authorized_keys entries must provide
content alongside filename. Public keys are also written to ~/.ssh.

Co-authored-by: Copilot <copilot@github.com>
2026-10-17 21:41:41 +02:00
CSRBot 6d42e7cc41 Merge pull request 'chore(deps): update ansible/ansible-lint action to v26.10.1' (#65) from renovate/ansible-ansible-lint-26.x into master
Ansible Linter / ansible-lint (push) Successful in 5m4s
Lint Markdown files / markdown-lint (push) Successful in 16s
Molecule / Molecule (push) Successful in 6m42s
2026-10-13 13:49:07 +00:00
CSRBot 0de969302c chore(deps): update ansible/ansible-lint action to v26.10.1
Ansible Linter / ansible-lint (push) Successful in 4m15s
Ansible Linter / ansible-lint (pull_request) Successful in 4m45s
Lint Markdown files / markdown-lint (pull_request) Successful in 16s
Molecule / Molecule (push) Successful in 9m10s
Molecule / Molecule (pull_request) Successful in 6m59s
Lint Markdown files / markdown-lint (push) Successful in 14s
2026-10-13 13:15:01 +00:00
CSRBot b6e5adfbbe Merge pull request 'chore(deps): update ansible/ansible-lint action to v26.10.0' (#64) from renovate/ansible-ansible-lint-26.x into master
Ansible Linter / ansible-lint (push) Successful in 7m5s
Lint Markdown files / markdown-lint (push) Successful in 14s
Molecule / Molecule (push) Successful in 11m32s
2026-10-12 10:42:28 +00:00
CSRBot 95192b7690 chore(deps): update ansible/ansible-lint action to v26.10.0
Ansible Linter / ansible-lint (pull_request) Successful in 4m51s
Ansible Linter / ansible-lint (push) Successful in 3m50s
Lint Markdown files / markdown-lint (pull_request) Successful in 14s
Lint Markdown files / markdown-lint (push) Successful in 13s
Molecule / Molecule (pull_request) Successful in 8m16s
Molecule / Molecule (push) Successful in 7m43s
2026-10-12 10:11:13 +00:00
volker.raschek 4cd30fc1f9 fix(tasks): select the btrfs filesystem by uuid instead of device
Lint Markdown files / markdown-lint (push) Successful in 7s
Ansible Linter / ansible-lint (push) Successful in 5m6s
Molecule / Molecule (push) Successful in 7m19s
Release Ansible Role / Release Ansible Role (push) Successful in 3m17s
2026-09-29 15:38:57 +02:00
CSRBot fb39904bab Merge pull request 'chore(deps): update ansible/ansible-lint action to v26.9.0' (#63) from renovate/ansible-ansible-lint-26.x into master
Ansible Linter / ansible-lint (push) Successful in 1m57s
Lint Markdown files / markdown-lint (push) Successful in 12s
Molecule / Molecule (push) Successful in 5m59s
2026-09-22 19:40:01 +00:00
CSRBot a391fa1c2b chore(deps): update ansible/ansible-lint action to v26.9.0
Ansible Linter / ansible-lint (pull_request) Successful in 1m30s
Lint Markdown files / markdown-lint (pull_request) Successful in 7s
Lint Markdown files / markdown-lint (push) Successful in 6s
Ansible Linter / ansible-lint (push) Successful in 2m21s
Molecule / Molecule (pull_request) Successful in 5m10s
Molecule / Molecule (push) Successful in 5m7s
2026-09-22 19:19:53 +00:00
CSRBot 545e56bad5 Merge pull request 'chore(deps): update actions/checkout action to v7' (#62) from renovate/actions-checkout-7.x into master
Lint Markdown files / markdown-lint (push) Successful in 6s
Ansible Linter / ansible-lint (push) Successful in 5m52s
Molecule / Molecule (push) Successful in 7m45s
2026-09-15 15:54:42 +00:00
CSRBot 8d54768c1b chore(deps): update actions/checkout action to v7
Ansible Linter / ansible-lint (push) Successful in 5m46s
Lint Markdown files / markdown-lint (push) Successful in 15s
Ansible Linter / ansible-lint (pull_request) Successful in 5m43s
Lint Markdown files / markdown-lint (pull_request) Successful in 11s
Molecule / Molecule (push) Successful in 10m16s
Molecule / Molecule (pull_request) Successful in 7m7s
2026-09-15 15:21:57 +00:00
volker.raschekandCopilot afd4288ebd refactor: use the yml extension for every yaml file
Lint Markdown files / markdown-lint (push) Successful in 14s
Molecule / Molecule (push) Successful in 5m19s
Ansible Linter / ansible-lint (push) Successful in 4m22s
The repository mixed both extensions, the molecule scenario used yml while the role itself used yaml. The yml
extension is the one ansible-galaxy and molecule generate, so it is applied throughout and across the sibling roles.

The include_tasks calls in tasks/main.yml and tasks/create_unix_user.yml name their target explicitly and were
adjusted accordingly, otherwise the role would no longer find the included task files.

Co-authored-by: Copilot <copilot@github.com>
2026-09-10 22:06:43 +02:00
19 changed files with 67 additions and 45 deletions

No files matched your search

@@ -12,9 +12,9 @@ jobs:
runs-on: runs-on:
- ubuntu-latest - ubuntu-latest
steps: steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run ansible-lint - name: Run ansible-lint
uses: ansible/ansible-lint@665d9e07a1943254d2910faffc106adaf7ea7294 # v26.8.0 uses: ansible/ansible-lint@9bce2bfb8f4a984e6b13767bc0d6bf0a49492164 # v26.10.1
with: with:
args: "--config-file .ansible-lint" args: "--config-file .ansible-lint"
# The molecule scenario is linted as well, so its collections are required beside the ones of the role. # The molecule scenario is linted as well, so its collections are required beside the ones of the role.
@@ -12,7 +12,7 @@ jobs:
runs-on: runs-on:
- ubuntu-latest - ubuntu-latest
steps: steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23.2.0 - uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23.2.0
with: with:
globs: '**/*.md' globs: '**/*.md'
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest-amd64 runs-on: ubuntu-latest-amd64
steps: steps:
# The scenario includes the role by its directory name, whose parent is used as roles path. # The scenario includes the role by its directory name, whose parent is used as roles path.
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: with:
path: unix-users path: unix-users
- name: Install molecule - name: Install molecule
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
+11 -6
View File
@@ -138,10 +138,11 @@ environment variables can be espcilitly defined, to consume it during execution
> [!IMPORTANT] > [!IMPORTANT]
> To allow consuming environment variables must be set `PermitUserEnvironment yes` in `/etc/ssh/sshd_config`. > To allow consuming environment variables must be set `PermitUserEnvironment yes` in `/etc/ssh/sshd_config`.
The private key `toor@toor-pc.ed25519.key` must be stored in `ssh/private_keys`. The public key will be automatically Private keys are configured using `filename` and `content`; the role writes them to `~/.ssh/<filename>` and extracts
extracted from the private key. their public keys to `~/.ssh/<filename>.pub`. Store private key content encrypted with Ansible Vault.
The public keys `claire@claire-pc.pub` as well as `bob@bob-pc.pub` must be stored in `ssh/authorized_keys`. Entries in `authorized_keys` are also added to `~/.ssh/authorized_keys`, including their optional command and environment
settings.
```yaml ```yaml
unix_users: unix_users:
@@ -156,14 +157,18 @@ unix_users:
StrictHostKeyChecking: "no" StrictHostKeyChecking: "no"
UserKnownHostFile: /dev/null UserKnownHostFile: /dev/null
authorized_keys: authorized_keys:
- filename: claire@claire-pc.pub - content: ssh-ed25519 AAAAC3NzaC... claire
- command: /usr/local/bin/upload-file.sh - command: /usr/local/bin/upload-file.sh
envs: envs:
- key: SSH_KEY_NAME - key: SSH_KEY_NAME
value: bob@bob-pc value: bob@bob-pc
filename: bob@bob-pc.pub content: ssh-ed25519 AAAAC3NzaC... bob
private_keys: private_keys:
- toor@toor-pc.ed25519.key - filename: toor@toor-pc.ed25519.key
content: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
shell: /bin/bash shell: /bin/bash
password: toor password: toor
group: toor group: toor
+19 -15
View File
@@ -8,21 +8,25 @@ unix_groups: {}
# gid: 1001 # gid: 1001
# state: absent # state: absent
unix_users: {} unix_users:
# alice: alice:
# state: "present" state: "present"
# name: "Alice" name: "Alice"
# uid: "1000" uid: "1000"
# ssh: ssh:
# config: config:
# - Host: "*" - Host: "*"
# StrictHostKeyChecking: "no" StrictHostKeyChecking: "no"
# UserKnownHostFile: /dev/null UserKnownHostFile: /dev/null
# authorized_keys: authorized_keys:
# - filename: alice@alice-pc.pub - content: ssh-ed25519 AAAAC3NzaC... alice
# # command: "" # optional # command: "" # optional
# private_keys: private_keys:
# - alice@alice-pc.ed25519.key - filename: alice@alice-pc.ed25519.key
content: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
# home: /home/alice # home: /home/alice
# btrfs: false # btrfs: false
# netrc: # netrc:
View File
File renamed without changes.
+4 -2
View File
@@ -28,13 +28,15 @@
- Host: "*" - Host: "*"
StrictHostKeyChecking: "no" StrictHostKeyChecking: "no"
authorized_keys: authorized_keys:
- filename: molecule.pub - content: "{{ lookup('ansible.builtin.file', molecule_scenario_directory + '/files/ssh/authorized_keys/molecule.pub', rstrip=false) }}"
command: "/usr/bin/true" command: "/usr/bin/true"
envs: envs:
- key: EDITOR - key: EDITOR
value: vi value: vi
- content: "{{ lookup('ansible.builtin.file', molecule_scenario_directory + '/files/ssh/authorized_keys/molecule.pub', rstrip=false) }}"
private_keys: private_keys:
- molecule.ed25519.key - filename: alice.ed25519.key
content: "{{ lookup('ansible.builtin.file', molecule_scenario_directory + '/files/ssh/private_keys/molecule.ed25519.key', rstrip=false) }}"
netrc: netrc:
- machine: hostname.local - machine: hostname.local
login: alice login: alice
+4 -3
View File
@@ -9,8 +9,8 @@
/home/molecule-alice/.ssh: "0700" /home/molecule-alice/.ssh: "0700"
/home/molecule-alice/.ssh/authorized_keys: "0600" /home/molecule-alice/.ssh/authorized_keys: "0600"
/home/molecule-alice/.ssh/config: "0644" /home/molecule-alice/.ssh/config: "0644"
/home/molecule-alice/.ssh/molecule.ed25519.key: "0600" /home/molecule-alice/.ssh/alice.ed25519.key: "0600"
/home/molecule-alice/.ssh/molecule.ed25519.key.pub: "0644" /home/molecule-alice/.ssh/alice.ed25519.key.pub: "0644"
/home/molecule-alice/.forward: "0644" /home/molecule-alice/.forward: "0644"
/home/molecule-alice/.netrc: "0600" /home/molecule-alice/.netrc: "0600"
/home/molecule-alice/.bashrc.d: "0755" /home/molecule-alice/.bashrc.d: "0755"
@@ -72,7 +72,7 @@
register: _alice_keys register: _alice_keys
loop: loop:
- "{{ _alice_home }}/.ssh/authorized_keys" - "{{ _alice_home }}/.ssh/authorized_keys"
- "{{ _alice_home }}/.ssh/molecule.ed25519.key.pub" - "{{ _alice_home }}/.ssh/alice.ed25519.key.pub"
- name: Assert that the authorized key carries its options and matches the extracted public key - name: Assert that the authorized key carries its options and matches the extracted public key
vars: vars:
@@ -83,6 +83,7 @@
- _authorized_key.startswith('command="/usr/bin/true",environment="EDITOR=vi" ') - _authorized_key.startswith('command="/usr/bin/true",environment="EDITOR=vi" ')
- _public_key.startswith('ssh-ed25519 ') - _public_key.startswith('ssh-ed25519 ')
- _public_key.split()[1] == _authorized_key.split()[2] - _public_key.split()[1] == _authorized_key.split()[2]
- (_alice_keys.results[0].content | b64decode).splitlines() | select('match', '^ssh-ed25519 ') | list | length == 1
fail_msg: "the authorized key of molecule-alice does not match the key extracted from its private key" fail_msg: "the authorized key of molecule-alice does not match the key extracted from its private key"
- name: Read the user and group database - name: Read the user and group database
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -23,10 +23,17 @@
msg: "Determined device {{ _unix_users_btrfs_device.stdout }} does not have a btrfs filesystem" msg: "Determined device {{ _unix_users_btrfs_device.stdout }} does not have a btrfs filesystem"
when: _unix_users_device_filesystem != 'btrfs' when: _unix_users_device_filesystem != 'btrfs'
# Select by UUID, device matching fails for symlinked devices like /dev/mapper/* (community.general#11331).
- name: "Find btrfs filesystem UUID"
ansible.builtin.command:
cmd: "findmnt -no UUID -T {{ _unix_users_home }}"
register: _unix_users_btrfs_uuid
changed_when: false
# The subvolume stays root owned until the user exists. It is chowned further below, after the user was created. # The subvolume stays root owned until the user exists. It is chowned further below, after the user was created.
- name: "Create btrfs volume for unix user: {{ unix_user.key }}" - name: "Create btrfs volume for unix user: {{ unix_user.key }}"
community.general.btrfs_subvolume: community.general.btrfs_subvolume:
filesystem_device: "{{ _unix_users_btrfs_device.stdout }}" filesystem_uuid: "{{ _unix_users_btrfs_uuid.stdout }}"
name: "{{ _unix_users_home }}" name: "{{ _unix_users_home }}"
state: present state: present
@@ -49,7 +56,7 @@
unix_user.value.btrfs unix_user.value.btrfs
block: block:
- name: "Copy skel files" - name: "Copy skel files"
ansible.builtin.include_tasks: copy_skel_file.yaml ansible.builtin.include_tasks: copy_skel_file.yml
loop_control: loop_control:
loop_var: skel_file loop_var: skel_file
with_items: with_items:
@@ -88,11 +95,10 @@
state: absent state: absent
when: unix_user.value.ssh.authorized_keys is not defined or unix_user.value.ssh.authorized_keys | length <= 0 when: unix_user.value.ssh.authorized_keys is not defined or unix_user.value.ssh.authorized_keys | length <= 0
# The relative source is resolved against the files directory of the playbook, like the lookup in authorized_keys.j2.
- name: "Create private SSH keys for unix user: {{ unix_user.key }}" - name: "Create private SSH keys for unix user: {{ unix_user.key }}"
ansible.builtin.copy: ansible.builtin.copy:
src: "ssh/private_keys/{{ item }}" content: "{{ item.content }}"
dest: "{{ _unix_users_home }}/.ssh/{{ item }}" dest: "{{ _unix_users_home }}/.ssh/{{ item.filename }}"
owner: "{{ unix_user.key }}" owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}" group: "{{ unix_user.value.group | default('users') }}"
mode: "0600" mode: "0600"
@@ -103,18 +109,22 @@
- name: "Extract public SSH keys from private keys for unix user: {{ unix_user.key }}" - name: "Extract public SSH keys from private keys for unix user: {{ unix_user.key }}"
ansible.builtin.shell: ansible.builtin.shell:
cmd: "ssh-keygen -y -f {{ _unix_users_home }}/.ssh/{{ item }} > {{ _unix_users_home }}/.ssh/{{ item }}.pub" cmd: "ssh-keygen -y -f {{ (_unix_users_home + '/.ssh/' + item.filename) | quote }} > {{ (_unix_users_home + '/.ssh/' + item.filename + '.pub') | quote }}"
creates: "{{ _unix_users_home }}/.ssh/{{ item }}.pub" creates: "{{ _unix_users_home }}/.ssh/{{ item.filename }}.pub"
loop_control:
label: "{{ item.filename }}"
with_items: with_items:
- "{{ unix_user.value.ssh.private_keys }}" - "{{ unix_user.value.ssh.private_keys }}"
when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0 when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0
- name: "Correct permissions of public SSH keys for unix user: {{ unix_user.key }}" - name: "Correct permissions of public SSH keys for unix user: {{ unix_user.key }}"
ansible.builtin.file: ansible.builtin.file:
path: "{{ _unix_users_home }}/.ssh/{{ item }}.pub" path: "{{ _unix_users_home }}/.ssh/{{ item.filename }}.pub"
owner: "{{ unix_user.key }}" owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}" group: "{{ unix_user.value.group | default('users') }}"
mode: "0644" mode: "0644"
loop_control:
label: "{{ item.filename }}"
with_items: with_items:
- "{{ unix_user.value.ssh.private_keys }}" - "{{ unix_user.value.ssh.private_keys }}"
when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0 when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0
@@ -164,7 +174,7 @@
- name: "Create shell rc files" - name: "Create shell rc files"
when: unix_user.value.shell_rc_files is defined when: unix_user.value.shell_rc_files is defined
ansible.builtin.include_tasks: create_shell_rc_file.yaml ansible.builtin.include_tasks: create_shell_rc_file.yml
with_items: with_items:
- "{{ unix_user.value.shell_rc_files }}" - "{{ unix_user.value.shell_rc_files }}"
loop_control: loop_control:
+4 -4
View File
@@ -1,7 +1,7 @@
--- ---
- name: Remove unix user - name: Remove unix user
ansible.builtin.include_tasks: remove_unix_user.yaml ansible.builtin.include_tasks: remove_unix_user.yml
with_dict: "{{ unix_users }}" with_dict: "{{ unix_users }}"
loop_control: loop_control:
loop_var: unix_user loop_var: unix_user
@@ -11,7 +11,7 @@
unix_user.value.state == 'absent' unix_user.value.state == 'absent'
- name: Remove unix groups - name: Remove unix groups
ansible.builtin.include_tasks: remove_unix_group.yaml ansible.builtin.include_tasks: remove_unix_group.yml
with_dict: "{{ unix_groups }}" with_dict: "{{ unix_groups }}"
loop_control: loop_control:
loop_var: unix_group loop_var: unix_group
@@ -21,7 +21,7 @@
unix_group.value.state == 'absent' unix_group.value.state == 'absent'
- name: Create unix groups - name: Create unix groups
ansible.builtin.include_tasks: create_unix_group.yaml ansible.builtin.include_tasks: create_unix_group.yml
with_dict: "{{ unix_groups }}" with_dict: "{{ unix_groups }}"
loop_control: loop_control:
loop_var: unix_group loop_var: unix_group
@@ -33,7 +33,7 @@
) )
- name: Create unix users - name: Create unix users
ansible.builtin.include_tasks: create_unix_user.yaml ansible.builtin.include_tasks: create_unix_user.yml
no_log: true no_log: true
with_dict: "{{ unix_users }}" with_dict: "{{ unix_users }}"
loop_control: loop_control:
File renamed without changes.
File renamed without changes.
+2 -2
View File
@@ -21,8 +21,8 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if _args | length > 0 %} {% if _args | length > 0 %}
{{ _args | join(',') }} {{ lookup('file', 'ssh/authorized_keys/' + authorized_key.filename ) }} {{ _args | join(',') }} {{ authorized_key.content | trim }}
{% else %} {% else %}
{{ lookup('file', 'ssh/authorized_keys/' + authorized_key.filename ) }} {{ authorized_key.content | trim }}
{% endif %} {% endif %}
{% endfor %} {% endfor %}