Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions ansible/roles/basic_users/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,52 @@ without requiring LDAP etc. Features:
- Login to the control node is prevented (by default).
- When deleting users, systemd user sessions are terminated first.

> [!IMPORTANT] This role assumes that `$HOME` for users managed by this role
(e.g. not `rocky` and other system users) is on a shared filesystem. The export
of this shared filesystem may be root squashed if its server is in the
`basic_user` group - see configuration examples below.
> [!IMPORTANT] The defaults for this role assumes that `$HOME` for users
managed by this role (e.g. not `rocky` and other system users) is on a shared
filesystem. The export of this shared filesystem may be root squashed if its
server is in the `basic_user` group - see configuration examples below.

Role Variables
--------------

- `basic_users_users`: Optional, default empty list. A list of mappings defining information for each user. In general, mapping keys/values are passed through as parameters to [ansible.builtin.user](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html) and default values are as given there. However:
- `create_home` and `generate_ssh_key`: Normally set automatically. Can be
set `false` if necessary to disable home directory creation/cluster ssh
key creation. Should not be set `true` to avoid trying to modify home
directories from multiple nodes simultaneously.
- `basic_users_homedir_server`: Optional inventory hostname in the `basic_users`
group defining the host to use to create home directories. If the home
directory export is root squashed, this host *must* be the home directory
server. Default is the `control` node which is appropriate for the default
appliance configuration. Not relevant if `create_home` is false for all users.
- `basic_users_homedir_server_path`: Optional path prefix for home directories on
the `basic_users_homedir_server`, i.e. on the "server side". Default is
`/exports/home` which is appropriate for the default appliance configuration.
- `basic_users_homedir_client`: Optional inventory hostname in the `basic_users`
group defining the host to use to create ssh keys etc in home directories.
This should be a host mounting the home directories. Default is the first
node in the `login` group which is appropriate for the default appliance
configuration.
- `basic_users_users`: Optional, default empty list. A list of mappings defining
information for each user. In general, mapping keys/values are passed through
as parameters to [ansible.builtin.user](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html)
and default values are as given there, with the following differences:
- `generate_ssh_key`: Default is `true`, and the generated key is added to
the user's authorized keys.
- `ssh_key_comment`: Default is user name.
- `home`: Set automatically based on the user name and
`basic_users_homedir_host_path`. Can be overriden if required for e.g.
users with non-standard home directory paths.
`basic_users_homedir_server_path`. Can be overriden for users with
non-standard home directory paths.
- `uid`: Should be set, so that the UID/GID is consistent across the cluster
(which Slurm requires).
- `shell`: If *not* set will be `/sbin/nologin` on the `control` node to
prevent users logging in to this node, and the default shell on other
nodes. Explicitly setting this defines the shell for all nodes and if the
shared home directories are mounted on the control node will allow the
user to log in to the control node.
- An additional key `public_key` may optionally be specified to define a key to log into the cluster.
- An additional key `sudo` may optionally be specified giving a string (possibly multiline) defining sudo rules to be templated.
- `ssh_key_type` defaults to `ed25519` instead of the `ansible.builtin.user` default of `rsa`.
- `public_key`: Optional, define a key to log into the cluster with.
- `sudo`: Optional, a (possibly multiline) string defining sudo rules for the
user.
- `ssh_key_type` defaults to `ed25519` instead of the `ansible.builtin.user`
default of `rsa`.
- Any other keys may present for other purposes (i.e. not used by this role).
- `basic_users_groups`: Optional, default empty list. A list of mappings defining information for each group. Mapping keys/values are passed through as parameters to [ansible.builtin.group](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/group_module.html) and default values are as given there.
- `basic_users_override_sssd`: Optional bool, default false. Whether to disable `sssd` when ensuring users/groups exist with this role. Permits creating local users/groups even if they clash with users provided via sssd (e.g. from LDAP). Ignored if host is not in group `sssd` as well. Note with this option active `sssd` will be stopped and restarted each time this role is run.
- `basic_users_homedir_host`: Optional inventory hostname defining the host
to use to create home directories. If the home directory export is root
squashed, this host *must* be the home directory server. Default is the
`control` node which is appropriate for the default appliance configuration.
Not relevant if `create_home` is false for all users.
- `basic_users_homedir_host_path`: Optional path prefix for home directories on
the `basic_users_homedir_host`, i.e. on the "server side". Default is
`/exports/home` which is appropriate for the default appliance configuration.

Dependencies
------------
Expand Down
6 changes: 3 additions & 3 deletions ansible/roles/basic_users/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
basic_users_homedir_host: "{{ groups['control'] | first }}" # no way, generally, to find the nfs_server
basic_users_homedir_host_path: /exports/home
# _basic_users_manage_homedir: "{{ ansible_hostname == basic_users_homedir_host }}"
basic_users_homedir_server: "{{ groups['control'] | first }}" # no way, generally, to find the nfs_server
basic_users_homedir_server_path: /exports/home
basic_users_homedir_client: "{{ groups['login'] | first }}"
basic_users_userdefaults:
state: present # need this here so don't have to add default() everywhere
generate_ssh_key: true
Expand Down
136 changes: 69 additions & 67 deletions ansible/roles/basic_users/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,88 +49,90 @@
state: started
when: _stop_sssd is changed

# This task runs (only) on the home directory server, if in the group, so it can
# handle root squashed exports
# This task runs only on the home directory server so it can handle
# root-squashed exports
- name: Create home directories
# doesn't delete with state=absent, same as ansible.builtin.user
ansible.builtin.copy:
remote_src: true
src: "{{ item.skeleton | default('/etc/skel/') }}"
dest: "{{ item.home | default( basic_users_homedir_host_path + '/' + item.name ) }}"
dest: "{{ item.home | default( basic_users_homedir_server_path + '/' + item.name ) }}"
owner: "{{ item.name }}"
group: "{{ item.name }}"
mode: u=rwX,go=
delegate_to: "{{ basic_users_homedir_host }}"
run_once: true
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- item.create_home | default(true) | bool
- inventory_hostname == basic_users_homedir_server

# The following tasks deliberately run on a (single) *client* node, so that
# home directory paths are easily constructed, becoming each user so that root
# squash doesn't matter
- delegate_to: "{{ groups['basic_users'] | difference([basic_users_homedir_host]) | first }}"
run_once: true
block:
- name: Create ~/.ssh directories
file:
state: directory
path: ~/.ssh/
owner: "{{ item.name }}"
group: "{{ item.name }}"
mode: u=rwX,go=
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
# The following tasks run on a single *client* node, so that home directory
# paths are easily constructed, becoming each user so that root-squash
# doesn't matter
- name: Create ~/.ssh directories
file:
state: directory
path: ~/.ssh/
owner: "{{ item.name }}"
group: "{{ item.name }}"
mode: u=rwX,go=
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- item.generate_ssh_key | default(true) | bool or item.public_key is defined
- inventory_hostname == basic_users_homedir_client

- name: Generate cluster ssh key
community.crypto.openssh_keypair:
path: "{{ item.ssh_key_file | default('~/.ssh/id_' + _ssh_key_type )}}" # NB: ssh_key_file is from ansible.builtin.user
type: "{{ _ssh_key_type }}"
comment: "{{ item.ssh_key_comment | default(item.name) }}"
vars:
_ssh_key_type: "{{ item.ssh_key_type | default('ed25519') }}"
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- item.generate_ssh_key | default(true) | bool
register: _cluster_ssh_keypair
- name: Generate cluster ssh key
community.crypto.openssh_keypair:
path: "{{ item.ssh_key_file | default('~/.ssh/id_' + _ssh_key_type )}}" # NB: ssh_key_file is from ansible.builtin.user
type: "{{ _ssh_key_type }}"
comment: "{{ item.ssh_key_comment | default(item.name) }}"
vars:
_ssh_key_type: "{{ item.ssh_key_type | default('ed25519') }}"
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- item.generate_ssh_key | default(true)
- inventory_hostname == basic_users_homedir_client
register: _cluster_ssh_keypair

- name: Write generated cluster ssh key to authorized_keys
ansible.posix.authorized_key:
user: "{{ item.item.name }}"
state: present
manage_dir: false
key: "{{ item.public_key }}"
path: ~/.ssh/authorized_keys
become_user: "{{ item.item.name }}"
loop: "{{ _cluster_ssh_keypair.results }}"
loop_control:
label: "{{ item.item.name }}"
when:
- item.item.state | default('present') == 'present'
- "'public_key' in item"
- name: Write generated cluster ssh key to authorized_keys
ansible.posix.authorized_key:
user: "{{ item.item.name }}"
state: present
manage_dir: false
key: "{{ item.public_key }}"
path: ~/.ssh/authorized_keys
become_user: "{{ item.item.name }}"
loop: "{{ _cluster_ssh_keypair.results }}"
loop_control:
label: "{{ item.item.name }}"
when:
- item.item.state | default('present') == 'present'
- item.item.generate_ssh_key | default(true)
- inventory_hostname == basic_users_homedir_client
- item.public_key is defined # NB this is the *returned* public key

- name: Write supplied public key to authorized_keys
ansible.posix.authorized_key:
user: "{{ item.name }}"
state: present
manage_dir: false
key: "{{ item.public_key }}"
path: ~/.ssh/authorized_keys
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- item.public_key is defined
- name: Write supplied public key to authorized_keys
ansible.posix.authorized_key:
user: "{{ item.name }}"
state: present
manage_dir: false
key: "{{ item.public_key }}"
path: ~/.ssh/authorized_keys
become_user: "{{ item.name }}"
loop: "{{ basic_users_users }}"
loop_control:
label: "{{ item.name }}"
when:
- item.state | default('present') == 'present'
- inventory_hostname == basic_users_homedir_client
- item.public_key is defined # NB this is the *provided* public key
Loading