From 05ec7c9beec69db5a04262f7d3396f9742193f8b Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Fri, 15 May 2026 19:30:23 +0200 Subject: [PATCH] left4me: symlink /etc/sudoers.d/left4me to the checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sudoers drop-in lives in left4me/deploy/files/etc/sudoers.d/left4me (single source of truth). Deleted the verbatim mirror in this bundle's files/ tree. Added an idempotent chmod action so the in-checkout file is 0440 root:root — required for sudo to accept it through the symlink. Syntax check on the source file is now a left4me-side pytest (deploy/tests/test_sudoers.py) running visudo -cf. Part of 2026-05-15-deployment-responsibility-design.md migration step 3. --- bundles/left4me/files/etc/sudoers.d/left4me | 5 ---- bundles/left4me/items.py | 30 ++++++++++++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) delete mode 100644 bundles/left4me/files/etc/sudoers.d/left4me diff --git a/bundles/left4me/files/etc/sudoers.d/left4me b/bundles/left4me/files/etc/sudoers.d/left4me deleted file mode 100644 index 5aa94eb..0000000 --- a/bundles/left4me/files/etc/sudoers.d/left4me +++ /dev/null @@ -1,5 +0,0 @@ -Defaults:left4me !requiretty -left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-systemctl * -left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-journalctl * -left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-overlay mount *, /usr/local/libexec/left4me/left4me-overlay umount * -left4me ALL=(root) NOPASSWD: /usr/local/libexec/left4me/left4me-script-sandbox diff --git a/bundles/left4me/items.py b/bundles/left4me/items.py index d958c29..a25eebd 100644 --- a/bundles/left4me/items.py +++ b/bundles/left4me/items.py @@ -95,13 +95,6 @@ files = { 'owner': 'root', 'group': 'root', }, - '/etc/sudoers.d/left4me': { - 'source': 'etc/sudoers.d/left4me', - 'mode': '0440', - 'owner': 'root', - 'group': 'root', - 'test_with': 'visudo -cf {}', - }, '/etc/left4me/host.env': { 'source': 'etc/left4me/host.env.mako', 'content_type': 'mako', @@ -161,6 +154,17 @@ symlinks = { 'action:left4me_daemon_reload', ], }, + '/etc/sudoers.d/left4me': { + 'target': '/opt/left4me/src/deploy/files/etc/sudoers.d/left4me', + 'owner': 'root', 'group': 'root', + 'needs': [ + 'action:left4me_chmod_sudoers', + 'git_deploy:/opt/left4me/src', + ], + # sudo follows symlinks; with the target file at root:root 0440 + # in a root-owned source tree, sudo accepts it. No daemon-reload + # equivalent — sudo re-reads /etc/sudoers.d/ on each invocation. + }, } actions = { @@ -197,6 +201,18 @@ actions = { 'symlink:/etc/systemd/system/left4me-server@.service.d/10-hardening.conf', ], }, + 'left4me_chmod_sudoers': { + # sudo refuses sudoers.d entries that aren't 0440 (or 0400) root:root. + # git_deploy extracts as root with the in-repo file mode; this action + # is belt-and-braces in case the repo mode drifts. Idempotent via + # the `unless` gate. + 'command': 'chmod 0440 /opt/left4me/src/deploy/files/etc/sudoers.d/left4me', + 'unless': 'test "$(stat -c %a /opt/left4me/src/deploy/files/etc/sudoers.d/left4me)" = "440"', + 'cascade_skip': False, + 'needs': [ + 'git_deploy:/opt/left4me/src', + ], + }, 'left4me_dpkg_add_i386_arch': { # steamcmd is 32-bit and pulls libc6:i386 + lib32z1 from the i386 arch. # apt-get update is part of this action because newly-added foreign