left4me: symlink /etc/sudoers.d/left4me to the checkout

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.
This commit is contained in:
CroneKorkN 2026-05-15 19:30:23 +02:00
parent 4820b7193f
commit 05ec7c9bee
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw
2 changed files with 23 additions and 12 deletions

View file

@ -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

View file

@ -95,13 +95,6 @@ files = {
'owner': 'root', 'owner': 'root',
'group': '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': { '/etc/left4me/host.env': {
'source': 'etc/left4me/host.env.mako', 'source': 'etc/left4me/host.env.mako',
'content_type': 'mako', 'content_type': 'mako',
@ -161,6 +154,17 @@ symlinks = {
'action:left4me_daemon_reload', '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 = { actions = {
@ -197,6 +201,18 @@ actions = {
'symlink:/etc/systemd/system/left4me-server@.service.d/10-hardening.conf', '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': { 'left4me_dpkg_add_i386_arch': {
# steamcmd is 32-bit and pulls libc6:i386 + lib32z1 from the 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 # apt-get update is part of this action because newly-added foreign