Files
ansible-role-unix-users/tasks/create_unix_user.yml
T
volker.raschekandCopilot 7683306d5f fix(ssh): refresh public keys after key rotation
Recompute public keys from the configured private keys so existing public key files cannot become stale.
Use copy to preserve idempotence, ownership and permissions.

Co-authored-by: Copilot <copilot@github.com>
2026-10-18 15:10:40 +02:00

193 lines
7.8 KiB
YAML

---
- name: "Define home directory for unix user: {{ unix_user.key }}"
ansible.builtin.set_fact:
_unix_users_home: "{{ unix_user.value.home | default('/home/' + unix_user.key) }}"
- name: "Create btrfs volume for unix user: {{ unix_user.key }}"
when: unix_user.value.btrfs is defined and
unix_user.value.btrfs
block:
- name: "Find btrfs device"
ansible.builtin.command:
cmd: /bin/bash -c "findmnt -no SOURCE -T {{ _unix_users_home }} | sed 's/\[.*\]//'"
register: _unix_users_btrfs_device
changed_when: false
- name: "Determine filesystem of device"
ansible.builtin.set_fact:
_unix_users_device_filesystem: "{{ ansible_facts['mounts'] | selectattr('device', 'equalto', _unix_users_btrfs_device.stdout) | map(attribute='fstype') | first }}"
- name: "Fail if device does not have a btrfs file system"
ansible.builtin.fail:
msg: "Determined device {{ _unix_users_btrfs_device.stdout }} does not have a btrfs filesystem"
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.
- name: "Create btrfs volume for unix user: {{ unix_user.key }}"
community.general.btrfs_subvolume:
filesystem_uuid: "{{ _unix_users_btrfs_uuid.stdout }}"
name: "{{ _unix_users_home }}"
state: present
- name: "Create unix user: {{ unix_user.key }}"
ansible.builtin.user:
name: "{{ unix_user.key }}"
uid: "{{ unix_user.value.uid | default(omit) }}"
group: "{{ unix_user.value.group | default('users') }}"
groups: "{{ unix_user.value.groups | default(omit) }}"
comment: "{{ unix_user.value.name | default(omit) }}"
create_home: "{{ unix_user.value.create_home | default(true) }}"
home: "{{ _unix_users_home }}"
shell: "{{ unix_user.value.shell | default('/bin/bash') }}"
# The salt is derived from the user name, a random one would produce a new hash and a change on every run.
password: "{{ unix_user.value.password | password_hash('sha512', unix_user.key | hash('sha512') | truncate(16, true, '')) if unix_user.value.password is defined and unix_user.value.password | length > 0 else '!' }}"
state: present
- name: "Adapt permissions and copy skel for unix user: {{ unix_user.key }}"
when: unix_user.value.btrfs is defined and
unix_user.value.btrfs
block:
- name: "Copy skel files"
ansible.builtin.include_tasks: copy_skel_file.yml
loop_control:
loop_var: skel_file
with_items:
- ".bash_logout"
- ".bash_profile"
- ".bashrc"
- name: "Change permission unix users home dir: {{ unix_user.key }}"
ansible.builtin.file:
path: "{{ _unix_users_home }}"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
state: directory
mode: "0755"
- name: "Create .ssh directory for unix user: {{ unix_user.key }}"
ansible.builtin.file:
path: "{{ _unix_users_home }}/.ssh"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0700"
state: directory
when: unix_user.value.ssh is defined
- name: "Create authorized_keys file for unix user: {{ unix_user.key }}"
ansible.builtin.template:
src: authorized_keys.j2
dest: "{{ _unix_users_home }}/.ssh/authorized_keys"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0600"
when: unix_user.value.ssh.authorized_keys is defined and unix_user.value.ssh.authorized_keys | length > 0
- name: "Remove authorized_keys file for unix user: {{ unix_user.key }}"
ansible.builtin.file:
path: "{{ _unix_users_home }}/.ssh/authorized_keys"
state: absent
when: unix_user.value.ssh.authorized_keys is not defined or unix_user.value.ssh.authorized_keys | length <= 0
- name: "Create private SSH keys for unix user: {{ unix_user.key }}"
ansible.builtin.copy:
content: "{{ item.content }}"
dest: "{{ _unix_users_home }}/.ssh/{{ item.filename }}"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0600"
no_log: true
with_items:
- "{{ unix_user.value.ssh.private_keys }}"
when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0
- name: "Extract public SSH keys from private keys for unix user: {{ unix_user.key }}"
ansible.builtin.command:
argv: [ "ssh-keygen", "-y", "-f", "{{ _unix_users_home }}/.ssh/{{ item.filename }}" ]
changed_when: false
register: _unix_users_ssh_pub_key_extraction
loop_control:
label: "{{ item.filename }}"
with_items:
- "{{ unix_user.value.ssh.private_keys }}"
when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0
- name: "Write derived public SSH keys for unix user: {{ unix_user.key }}"
ansible.builtin.copy:
content: "{{ item.stdout }}\n"
dest: "{{ _unix_users_home }}/.ssh/{{ item.item.filename }}.pub"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0644"
loop_control:
label: "{{ item.item.filename }}"
loop: "{{ _unix_users_ssh_pub_key_extraction.results }}"
when: unix_user.value.ssh.private_keys is defined and unix_user.value.ssh.private_keys | length > 0
- name: "Create custom SSH client config for unix user: {{ unix_user.key }}"
ansible.builtin.template:
src: config.j2
dest: "{{ _unix_users_home }}/.ssh/config"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0644"
when: unix_user.value.ssh.config is defined and unix_user.value.ssh.config | length > 0
- name: "Remove custom SSH client config for unix user: {{ unix_user.key }}"
ansible.builtin.file:
path: "{{ _unix_users_home }}/.ssh/config"
state: absent
when: unix_user.value.ssh.config is not defined or unix_user.value.ssh.config | length <= 0
- name: "Create .forward file to forward emails for unix user: {{ unix_user.key }}"
ansible.builtin.template:
src: forward.j2
dest: "{{ _unix_users_home }}/.forward"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0644"
when: unix_user.value.email is defined
- name: "Remove .forward file to forward emails for unix user: {{ unix_user.key }}"
ansible.builtin.file:
path: "{{ _unix_users_home }}/.forward"
state: absent
when: unix_user.value.email is not defined
- name: "Create XDG base directories"
ansible.builtin.file:
path: "{{ item }}"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0755"
state: "directory"
with_items:
- "{{ unix_user.value.xdg.dirs.cache | default(_unix_users_home + '/.cache') }}"
- "{{ unix_user.value.xdg.dirs.config | default(_unix_users_home + '/.config') }}"
- "{{ unix_user.value.xdg.dirs.data | default(_unix_users_home + '/.local/share') }}"
- "{{ unix_user.value.xdg.dirs.state | default(_unix_users_home + '/.local/state') }}"
- name: "Create shell rc files"
when: unix_user.value.shell_rc_files is defined
ansible.builtin.include_tasks: create_shell_rc_file.yml
with_items:
- "{{ unix_user.value.shell_rc_files }}"
loop_control:
loop_var: shell_rc_file
- name: "Create .netrc file"
when: unix_user.value.netrc is defined and unix_user.value.netrc | length > 0
ansible.builtin.template:
src: netrc.j2
dest: "{{ _unix_users_home }}/.netrc"
owner: "{{ unix_user.key }}"
group: "{{ unix_user.value.group | default('users') }}"
mode: "0600"
no_log: true