diff --git a/ansible/roles/basic_users/README.md b/ansible/roles/basic_users/README.md index 7267b271b..70ab1545a 100644 --- a/ansible/roles/basic_users/README.md +++ b/ansible/roles/basic_users/README.md @@ -11,23 +11,37 @@ 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 @@ -35,20 +49,14 @@ Role Variables 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 ------------ diff --git a/ansible/roles/basic_users/defaults/main.yml b/ansible/roles/basic_users/defaults/main.yml index 3de8dd6a4..7b24ef784 100644 --- a/ansible/roles/basic_users/defaults/main.yml +++ b/ansible/roles/basic_users/defaults/main.yml @@ -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 diff --git a/ansible/roles/basic_users/tasks/main.yml b/ansible/roles/basic_users/tasks/main.yml index 6d3d9825a..23d160850 100644 --- a/ansible/roles/basic_users/tasks/main.yml +++ b/ansible/roles/basic_users/tasks/main.yml @@ -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