graylog-deploy/bootstrap-host.sh
byrsapty d98a63c0f1 Automate the AppArmor fix with a narrowly-scoped sudoers rule
Adds bootstrap-host.sh (one-time, run as root on a fresh Proxmox host) and
fix-lxc-apparmor.sh, the fixed-content script it installs. The sudoers
rule it wires up only ever invokes that one root-owned script with a VMID
argument - deliberately not a broader rule like `tee -a <conf>` or
`sh -c '...'`, since those only restrict the command's own argv, not
stdin/heredoc content, letting the caller write arbitrary lines to any
200-299 container's config instead of just this one fixed line.

create-graylog-lxc.sh now tries `sudo -n fix-lxc-apparmor.sh` first and
falls back to the existing manual instructions if that sudoers rule isn't
present yet - fully backward compatible with hosts that haven't run
bootstrap-host.sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 22:17:02 +03:00

44 lines
1.9 KiB
Bash

#!/bin/bash
# Run ONCE, as root, on a fresh Proxmox host - not part of the claude-deploy
# workflow, since claude-deploy is exactly the account this script is
# widening permissions FOR. Idempotent: safe to re-run.
#
# Installs fix-lxc-apparmor.sh (root-owned, not writable by claude-deploy)
# and a narrow sudoers rule that lets claude-deploy invoke it - and only
# it, only with a VMID argument in the 200-299 range - so create-graylog-lxc.sh
# can clear the "Docker-in-unprivileged-LXC AppArmor block" quirk (see
# README.md) without a human editing /etc/pve/lxc/*.conf by hand every time.
#
# Deliberately does NOT grant broader access (e.g. `tee -a` or `sh -c` on
# the conf file): those let the caller write arbitrary content to any
# 200-299 container's config, not just this one fixed line. See the
# "Automating the AppArmor fix" note in README.md for why.
set -euo pipefail
[ "$(id -u)" -eq 0 ] || { echo "Must run as root." >&2; exit 1; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGET="/usr/local/sbin/fix-lxc-apparmor.sh"
SUDOERS_FILE="/etc/sudoers.d/claude-deploy-apparmor"
SUDOERS_LINE="claude-deploy ALL=(root) NOPASSWD: ${TARGET} 2[0-9][0-9]"
if [ -f "$TARGET" ] && cmp -s "$SCRIPT_DIR/fix-lxc-apparmor.sh" "$TARGET"; then
echo "$TARGET already up to date"
else
install -o root -g root -m 700 "$SCRIPT_DIR/fix-lxc-apparmor.sh" "$TARGET"
echo "✓ installed $TARGET (root-owned, 700)"
fi
if [ -f "$SUDOERS_FILE" ] && grep -qxF "$SUDOERS_LINE" "$SUDOERS_FILE"; then
echo "⏭ sudoers rule already present in $SUDOERS_FILE"
else
tmp="$(mktemp)"
echo "$SUDOERS_LINE" > "$tmp"
chmod 440 "$tmp"
visudo -c -f "$tmp" || { echo "generated sudoers snippet failed validation" >&2; rm -f "$tmp"; exit 1; }
mv "$tmp" "$SUDOERS_FILE"
chown root:root "$SUDOERS_FILE"
echo "✓ installed sudoers rule at $SUDOERS_FILE"
fi
echo "Done. claude-deploy can now run: sudo $TARGET <vmid>"