From d00d99ce450490910de30be153afb31edfbbdc84 Mon Sep 17 00:00:00 2001 From: Nicola Belluti Date: Tue, 13 Oct 2026 12:49:13 +0200 Subject: [PATCH] Added `users` role to manage the playbook as root and remove admin privileges from `deck` --- README.md | 16 +++++++ inventory/inventory.yml | 4 +- roles/40cu/tasks/main.yml | 3 -- roles/cpu_governor/tasks/main.yml | 7 --- roles/fan/tasks/main.yml | 3 -- roles/gpu_governor/defaults/main.yml | 2 + roles/gpu_governor/handlers/main.yml | 1 - roles/gpu_governor/tasks/main.yml | 14 +++--- roles/kernel/tasks/main.yml | 8 ---- roles/memory/handlers/main.yml | 1 - roles/memory/tasks/main.yml | 9 ---- roles/mesa/tasks/main.yml | 2 - roles/users/defaults/main.yml | 7 +++ roles/users/handlers/main.yml | 5 +++ roles/users/tasks/main.yml | 65 ++++++++++++++++++++++++++++ roles/users/templates/deck.rules.j2 | 17 ++++++++ roles/users/templates/sshd.conf.j2 | 6 +++ site.yml | 25 +++++++++++ 18 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 roles/gpu_governor/defaults/main.yml create mode 100644 roles/users/defaults/main.yml create mode 100644 roles/users/handlers/main.yml create mode 100644 roles/users/tasks/main.yml create mode 100644 roles/users/templates/deck.rules.j2 create mode 100644 roles/users/templates/sshd.conf.j2 diff --git a/README.md b/README.md index 344dfe7..0900c29 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,19 @@ git clone https://git.nicolabelluti.me/nicolabelluti/bc250-cachyos-handheld cd bc250-cachyos-handheld nix develop ``` + +# Usage + +On a fresh install, log in once with the installer's `deck`/`deck` to hand +administration over to `root` (SSH key only) and strip `deck` of its password +and admin rights: + +```sh +ansible-playbook site.yml --tags users +``` + +Then configure the board as `root`: + +```sh +ansible-playbook site.yml +``` diff --git a/inventory/inventory.yml b/inventory/inventory.yml index d4aa1d9..42503b4 100644 --- a/inventory/inventory.yml +++ b/inventory/inventory.yml @@ -2,6 +2,4 @@ bc250: hosts: 192.168.1.156: vars: - ansible_user: deck - ansible_ssh_pass: deck - ansible_become_password: "{{ ansible_ssh_pass }}" + ansible_user: root diff --git a/roles/40cu/tasks/main.yml b/roles/40cu/tasks/main.yml index 65795d0..aadd4f4 100644 --- a/roles/40cu/tasks/main.yml +++ b/roles/40cu/tasks/main.yml @@ -1,6 +1,5 @@ # The custom `linux-cachyos-bc250` brings in the 40CU patch - name: Enable the 40 CU unlock - become: true ansible.builtin.copy: content: "options amdgpu bc250_cc_write_mode=3\n" dest: /etc/modprobe.d/bc250-40cu.conf @@ -8,13 +7,11 @@ register: cu40_conf - name: Rebuild initramfs - become: true ansible.builtin.command: limine-mkinitcpio when: cu40_conf.changed changed_when: true - name: Reboot to reinitialise amdgpu - become: true ansible.builtin.reboot: reboot_timeout: 600 when: cu40_conf.changed diff --git a/roles/cpu_governor/tasks/main.yml b/roles/cpu_governor/tasks/main.yml index ab057e0..11fec23 100644 --- a/roles/cpu_governor/tasks/main.yml +++ b/roles/cpu_governor/tasks/main.yml @@ -1,5 +1,4 @@ - name: Install build and runtime prerequisites - become: true community.general.pacman: name: - git @@ -32,7 +31,6 @@ # pipx keeps this out of the system python environment. - name: Install the CLI system-wide - become: true ansible.builtin.command: argv: - pipx @@ -42,21 +40,18 @@ always: - name: Remove temporary directory - become: true ansible.builtin.file: path: "{{ smu_tmp.path }}" state: absent when: smu_tmp.path is defined - name: Create the config directory - become: true ansible.builtin.file: path: /etc/bc250 state: directory mode: '0755' - name: Deploy the overclock profile - become: true ansible.builtin.template: src: overclock.conf.j2 dest: /etc/bc250/overclock.conf @@ -64,7 +59,6 @@ register: oc_conf - name: Install the overclock service - become: true ansible.builtin.command: argv: - bc250-apply @@ -73,7 +67,6 @@ when: oc_conf.changed - name: Enable the overclock service at boot - become: true ansible.builtin.systemd_service: name: bc250-smu-oc enabled: true diff --git a/roles/fan/tasks/main.yml b/roles/fan/tasks/main.yml index f227859..5a71685 100644 --- a/roles/fan/tasks/main.yml +++ b/roles/fan/tasks/main.yml @@ -1,6 +1,5 @@ # The custom `linux-cachyos-bc250` brings in the nct6678 module - name: Configure the nct6687 sensor module - become: true ansible.builtin.copy: content: | blacklist nct6683 @@ -9,14 +8,12 @@ mode: '0644' - name: Load nct6687 at boot - become: true ansible.builtin.copy: content: "nct6687\n" dest: /etc/modules-load.d/99-sensors.conf mode: '0644' - name: Load nct6687 now - become: true community.general.modprobe: name: nct6687 state: present diff --git a/roles/gpu_governor/defaults/main.yml b/roles/gpu_governor/defaults/main.yml new file mode 100644 index 0000000..4b1ef17 --- /dev/null +++ b/roles/gpu_governor/defaults/main.yml @@ -0,0 +1,2 @@ +# Unprivileged user makepkg runs as. +gpu_governor__build_user: deck diff --git a/roles/gpu_governor/handlers/main.yml b/roles/gpu_governor/handlers/main.yml index 1f5a0b2..51d037b 100644 --- a/roles/gpu_governor/handlers/main.yml +++ b/roles/gpu_governor/handlers/main.yml @@ -1,5 +1,4 @@ - name: Restart the GPU governor - become: true ansible.builtin.systemd_service: name: cyan-skillfish-governor-smu state: restarted diff --git a/roles/gpu_governor/tasks/main.yml b/roles/gpu_governor/tasks/main.yml index 025adec..d942110 100644 --- a/roles/gpu_governor/tasks/main.yml +++ b/roles/gpu_governor/tasks/main.yml @@ -12,17 +12,23 @@ when: is_gov_installed.rc != 0 block: - name: Create temporary dir for build + become: true + become_user: "{{ gpu_governor__build_user }}" ansible.builtin.tempfile: state: directory suffix: csgov register: gov_tmp - name: Download the AUR snapshot + become: true + become_user: "{{ gpu_governor__build_user }}" ansible.builtin.get_url: url: "https://aur.archlinux.org/cgit/aur.git/snapshot/cyan-skillfish-governor-smu.tar.gz" dest: "{{ gov_tmp.path }}/cyan-skillfish-governor-smu.tar.gz" - name: Extract the snapshot + become: true + become_user: "{{ gpu_governor__build_user }}" ansible.builtin.unarchive: src: "{{ gov_tmp.path }}/cyan-skillfish-governor-smu.tar.gz" dest: "{{ gov_tmp.path }}" @@ -41,13 +47,15 @@ | unique }} - name: Ensure build prerequisites are present - become: true ansible.builtin.pacman: name: "{{ ['base-devel', 'fakeroot'] + gov_build_deps }}" state: present + # makepkg refuses to run as root. # Rust build, expect several minutes on Zen 2. - name: Build the package (no install) + become: true + become_user: "{{ gpu_governor__build_user }}" ansible.builtin.command: argv: - makepkg @@ -64,9 +72,7 @@ file_type: file register: gov_built_pkgs - # Install with root, but OUTSIDE makepkg - name: Install built package(s) - become: true ansible.builtin.command: argv: - pacman @@ -84,7 +90,6 @@ when: gov_tmp.path is defined - name: Deploy the governor configuration - become: true ansible.builtin.template: src: config.toml.j2 dest: /etc/cyan-skillfish-governor-smu/config.toml @@ -92,7 +97,6 @@ notify: Restart the GPU governor - name: Enable and start the GPU governor - become: true ansible.builtin.systemd_service: name: cyan-skillfish-governor-smu enabled: true diff --git a/roles/kernel/tasks/main.yml b/roles/kernel/tasks/main.yml index f425c02..d8014b3 100644 --- a/roles/kernel/tasks/main.yml +++ b/roles/kernel/tasks/main.yml @@ -1,5 +1,4 @@ - name: Add the bc250-cachyos pacman repository - become: true ansible.builtin.blockinfile: path: /etc/pacman.conf marker: "# {mark} - Ansible managed" @@ -11,14 +10,12 @@ register: pacman_repo - name: Perform a full system upgrade - become: true community.general.pacman: update_cache: "{{ pacman_repo.changed }}" upgrade: true register: sys_upgrade - name: Give the BC-250 kernel priority in BOOT_ORDER - become: true ansible.builtin.lineinfile: path: /etc/default/limine regexp: '^BOOT_ORDER=' @@ -26,7 +23,6 @@ register: boot_order - name: Install the BC-250 kernel and the fallback - become: true community.general.pacman: name: - linux-cachyos-bore-bc250 @@ -37,13 +33,11 @@ register: kernel_pkg - name: Regenerate Limine entries when only BOOT_ORDER changed - become: true ansible.builtin.command: limine-mkinitcpio when: boot_order.changed and not kernel_pkg.changed changed_when: true - name: Preselect the BC-250 kernel for the next boot - become: true ansible.builtin.shell: executable: /bin/bash cmd: | @@ -57,7 +51,6 @@ failed_when: efivar_write.rc not in [0, 90] - name: Reboot into the new kernel - become: true ansible.builtin.reboot: reboot_timeout: 600 when: kernel_pkg.changed or boot_order.changed or sys_upgrade.changed @@ -72,7 +65,6 @@ fail_msg: "Running {{ ansible_kernel }}" - name: Remove the deckify kernel - become: true community.general.pacman: name: - linux-cachyos-deckify diff --git a/roles/memory/handlers/main.yml b/roles/memory/handlers/main.yml index 97d440b..674ce9e 100644 --- a/roles/memory/handlers/main.yml +++ b/roles/memory/handlers/main.yml @@ -1,4 +1,3 @@ - name: Rebuild initramfs and limine entries - become: true ansible.builtin.command: limine-mkinitcpio changed_when: true diff --git a/roles/memory/tasks/main.yml b/roles/memory/tasks/main.yml index 4ed1d19..12a2c54 100644 --- a/roles/memory/tasks/main.yml +++ b/roles/memory/tasks/main.yml @@ -7,7 +7,6 @@ # default. 3959290 pages of 4K = ~15.1 GiB # ttm.page_pool_size - must match pages_limit or allocations can fail - name: Add BC-250 kernel parameters - become: true ansible.builtin.blockinfile: path: /etc/default/limine marker: '# {mark} ANSIBLE bc250 memory' @@ -17,7 +16,6 @@ # lz4 must be in the initramfs for zswap to use it from early boot. - name: Add lz4 to the initramfs modules - become: true ansible.builtin.lineinfile: path: /etc/mkinitcpio.conf regexp: '^MODULES=' @@ -27,14 +25,12 @@ # CachyOS ships a udev rule that disables zswap when zram comes up. # An empty file with the same name overrides it. - name: Neutralise the CachyOS zram udev rule - become: true ansible.builtin.copy: content: '' dest: /etc/udev/rules.d/30-zram.rules mode: '0644' - name: Lower swappiness - become: true ansible.builtin.copy: content: vm.swappiness=10 dest: /etc/sysctl.d/99-bc250.conf @@ -42,7 +38,6 @@ register: sysctl_conf - name: Apply swappiness now - become: true ansible.builtin.command: argv: - sysctl @@ -51,7 +46,6 @@ changed_when: true - name: Create the swap subvolume - become: true ansible.builtin.command: argv: - btrfs @@ -62,7 +56,6 @@ creates: /swap - name: Create the swapfile - become: true ansible.builtin.command: argv: - btrfs @@ -77,7 +70,6 @@ creates: /swap/swapfile - name: Add the swapfile to fstab - become: true ansible.builtin.lineinfile: path: /etc/fstab regexp: '^/swap/swapfile' @@ -87,6 +79,5 @@ ansible.builtin.meta: flush_handlers - name: Reboot to apply the new kernel parameters - become: true ansible.builtin.reboot: reboot_timeout: 600 diff --git a/roles/mesa/tasks/main.yml b/roles/mesa/tasks/main.yml index 3ddd10b..4a29383 100644 --- a/roles/mesa/tasks/main.yml +++ b/roles/mesa/tasks/main.yml @@ -1,6 +1,5 @@ # community.general.pacman ignores the repo/pkg syntax, so call pacman. - name: Install the patched Mesa stack - become: true ansible.builtin.command: argv: "{{ ['pacman', '-S', '--noconfirm'] + (mesa_stack | map('regex_replace', '^', 'bc250-cachyos/') | list) }}" register: mesa_install @@ -20,7 +19,6 @@ # Pin last: IgnorePkg outranks the repo/pkg syntax, so writing it before # the install would make pacman prompt instead of proceeding. - name: Pin the Mesa stack against upstream upgrades - become: true ansible.builtin.lineinfile: path: /etc/pacman.conf regexp: '^IgnorePkg\s*=' diff --git a/roles/users/defaults/main.yml b/roles/users/defaults/main.yml new file mode 100644 index 0000000..2e21565 --- /dev/null +++ b/roles/users/defaults/main.yml @@ -0,0 +1,7 @@ +users__desktop_user: deck + +# Groups that grant administrative rights: wheel for sudo and polkit, sys for +# CUPS administration. +users__admin_groups: + - wheel + - sys diff --git a/roles/users/handlers/main.yml b/roles/users/handlers/main.yml new file mode 100644 index 0000000..2ba09fd --- /dev/null +++ b/roles/users/handlers/main.yml @@ -0,0 +1,5 @@ +# A reload keeps established sessions, including Ansible's own. +- name: Reload sshd + ansible.builtin.systemd_service: + name: sshd + state: reloaded diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml new file mode 100644 index 0000000..3713374 --- /dev/null +++ b/roles/users/tasks/main.yml @@ -0,0 +1,65 @@ +# Runs as the installer's desktop user until root is reachable with a key, +# then switches over and strips that user of its admin rights. +- name: Authorize the SSH key for root + become: true + ansible.posix.authorized_key: + user: root + key: "{{ lookup('ansible.builtin.file', install_ssh_keys__ssh_pubkey_path | expanduser) }}" + +# Empty passwords make Ansible skip sshpass and fall back to the key. +- name: Switch the connection to root + ansible.builtin.set_fact: + ansible_user: root + ansible_ssh_pass: '' + ansible_become_password: '' + +- name: Drop the desktop user connection + ansible.builtin.meta: reset_connection + +# Arch already allows root logins with a key. Stop here, with sshd and the +# desktop user untouched, if root can't get in. +- name: Check root login + ansible.builtin.ping: + +- name: Deploy the sshd configuration + ansible.builtin.template: + src: sshd.conf.j2 + dest: /etc/ssh/sshd_config.d/01-bc250.conf + mode: '0644' + notify: Reload sshd + +- name: Validate the sshd configuration + ansible.builtin.command: + argv: + - sshd + - -t + changed_when: false + +- name: Apply the sshd configuration + ansible.builtin.meta: flush_handlers + +- name: Grant the desktop user the udisks actions Steam needs + ansible.builtin.template: + src: deck.rules.j2 + dest: /etc/polkit-1/rules.d/50-bc250-{{ users__desktop_user }}.rules + mode: '0644' + +- name: Read the group database + ansible.builtin.getent: + database: group + +- name: Remove the desktop user from the admin groups + ansible.builtin.user: + name: "{{ users__desktop_user }}" + groups: >- + {{ ansible_facts.getent_group | dict2items + | selectattr('value.2', 'search', '(^|,)' ~ users__desktop_user ~ '(,|$)') + | map(attribute='key') + | difference(users__admin_groups) }} + append: false + +# '!' with no hash: password logins are impossible, SDDM autologin still works. +- name: Remove and lock the desktop user password + ansible.builtin.user: + name: "{{ users__desktop_user }}" + password: '!' diff --git a/roles/users/templates/deck.rules.j2 b/roles/users/templates/deck.rules.j2 new file mode 100644 index 0000000..90714e2 --- /dev/null +++ b/roles/users/templates/deck.rules.j2 @@ -0,0 +1,17 @@ +// {{ ansible_managed }} + +// org.valve.holo.rules only grants these to wheel. Steam needs them to eject +// and power off drives from Game Mode, where no polkit agent can prompt. +polkit.addRule(function(action, subject) { + var permission = { + "org.freedesktop.udisks2.eject-media": polkit.Result.YES, + "org.freedesktop.udisks2.power-off-drive": polkit.Result.YES, + "org.freedesktop.udisks2.filesystem-unmount-others": polkit.Result.YES, + "org.freedesktop.udisks2.eject-media-other-seat": polkit.Result.YES, + "org.freedesktop.udisks2.power-off-drive-other-seat": polkit.Result.YES, + }; + + if (subject.user == "{{ users__desktop_user }}") { + return permission[action.id]; + } +}); diff --git a/roles/users/templates/sshd.conf.j2 b/roles/users/templates/sshd.conf.j2 new file mode 100644 index 0000000..42af379 --- /dev/null +++ b/roles/users/templates/sshd.conf.j2 @@ -0,0 +1,6 @@ +# {{ ansible_managed }} + +PermitRootLogin prohibit-password +PasswordAuthentication no +KbdInteractiveAuthentication no +AllowUsers root diff --git a/site.yml b/site.yml index f512ad8..f029b89 100644 --- a/site.yml +++ b/site.yml @@ -1,3 +1,28 @@ +- name: Manage users + hosts: bc250 + tags: + - 'never' + - 'users' + vars: + ansible_user: deck + ansible_ssh_pass: deck + ansible_become_password: deck + pre_tasks: + - name: Inserisci il percorso della chiave pubblica + run_once: true # noqa: run-once[task] + ansible.builtin.pause: + prompt: |- + Inserisci il percorso della chiave pubblica [~/.ssh/id_ed25519.pub] + register: install_ssh_keys__ssh_pubkey_path + + - name: Imposta il percorso della chiave pubblica + run_once: true # noqa: run-once[task] + ansible.builtin.set_fact: + install_ssh_keys__ssh_pubkey_path: "{{ install_ssh_keys__ssh_pubkey_path.user_input or '~/.ssh/id_ed25519.pub' }}" + roles: + - name: Manage the users + role: users + - name: Configure the BC250 hosts: bc250 roles: