MakerFLOSS_Mikrotik/roles/makerfloss.mikrotik_switch/tasks/users.yml

95 lines
3.8 KiB
YAML
Raw Normal View History

---
# Operator accounts and their SSH keys.
#
# `switch_operators` (host_vars) is the source of truth: one entry per person/seat,
# each with the group it belongs to and the public keys that may log in as it. The
# role only holds mechanism.
#
# Idempotency: the device is queried FIRST and only genuinely missing keys are
# uploaded — an unconditional `net_put` would report changed on every run. RouterOS
# reports a key's comment field as `key-owner`, which is what we match on.
#
# Passwords: a RouterOS user with no password can still be reached over WinBox/console
# without one, so operators are created with a vaulted password even though SSH is
# key-only (`/ip/ssh always-allow-password-login=no`). See CLAUDE.md.
- name: Assert every operator has a vaulted password
ansible.builtin.assert:
that: switch_operators | map(attribute='name') | difference(vault_operator_passwords | default({}) | list) | length == 0
fail_msg: >-
Every entry in switch_operators needs a vault_operator_passwords.<name> entry —
a passwordless RouterOS user is reachable over WinBox/console without one.
Missing: {{ switch_operators | map(attribute='name')
| difference(vault_operator_passwords | default({}) | list) | join(', ') }}
quiet: true
- name: Read the SSH keys currently installed on the device
community.routeros.command:
commands:
- /user/ssh-keys/print detail
register: switch_ssh_keys_raw
changed_when: false
- name: Build the set of user/key-owner pairs already present
ansible.builtin.set_fact:
switch_ssh_keys_present: >-
{{ switch_ssh_keys_raw.stdout[0]
| regex_findall('user=(\S+).*?key-owner="([^"]*)"')
| map('join', '|') | list }}
- name: Ensure each operator account exists in the right group
community.routeros.command:
commands:
- >-
:if ([:len [/user find name="{{ item.name }}"]] = 0)
do={ /user add name="{{ item.name }}" group="{{ item.group }}"
password="{{ vault_operator_passwords[item.name] }}" }
else={ :if ([/user get [find name="{{ item.name }}"] group] != "{{ item.group }}")
do={ /user set [find name="{{ item.name }}"] group="{{ item.group }}" } }
loop: "{{ switch_operators }}"
loop_control:
label: "{{ item.name }}"
no_log: true
changed_when: false
- name: Upload the operator public keys that are not on the device yet
ansible.netcommon.net_put:
src: "{{ item.1 }}"
dest: "{{ item.1 | basename }}"
loop: "{{ switch_operators | subelements('pubkey_files') }}"
loop_control:
label: "{{ item.0.name }} <- {{ item.1 | basename }}"
when: item.0.name ~ '|' ~ (lookup('file', item.1).split() | last) not in switch_ssh_keys_present
- name: Import the newly uploaded operator public keys
community.routeros.command:
commands:
- >-
/user/ssh-keys/import public-key-file="{{ item.1 | basename }}"
user="{{ item.0.name }}"
loop: "{{ switch_operators | subelements('pubkey_files') }}"
loop_control:
label: "{{ item.0.name }} <- {{ item.1 | basename }}"
when: item.0.name ~ '|' ~ (lookup('file', item.1).split() | last) not in switch_ssh_keys_present
changed_when: true
- name: Remove the uploaded key files from the switch flash
community.routeros.command:
commands:
- >-
:if ([:len [/file find name="{{ item.1 | basename }}"]] > 0)
do={ /file remove [find name="{{ item.1 | basename }}"] }
loop: "{{ switch_operators | subelements('pubkey_files') }}"
loop_control:
label: "{{ item.1 | basename }}"
changed_when: false
- name: Disable the default admin user
community.routeros.command:
commands:
- >-
:if ([:len [/user find name="admin"]] > 0) do={
/user/set admin disabled=yes }
when: switch_disable_default_admin | bool
changed_when: false