feat(raspberrypi): add Raspberry Pi configuration section
Secret Scan / Scan for secrets and sensitive config (push) Failing after 5s

Documents and scripts to reproduce the IPA integration on the Pi
(raspberrypi.tail13f623.ts.net, Debian 12 bookworm):

- setup-ipa-sudo.sh: writes /etc/sudoers.d/ipa-admins granting
  %admins NOPASSWD:ALL (same IPA admins group as pbs/pdm/pve1)
- setup-docker-ipa-gid.sh: pins local docker group GID to 50010
  via groupmod --non-unique so IPA docker-access group membership
  alone grants docker socket access (mirrors NixOS lib.mkForce approach)
- README.md + CLAUDE.md: quick-start, current status, guardrails

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 15:47:30 +10:00
co-authored by Claude Sonnet 4.6
parent a4d907e2eb
commit afabcbbeff
7 changed files with 225 additions and 7 deletions
+7 -3
View File
@@ -6,9 +6,9 @@ OVERRIDE any default behaviour and must be followed exactly.
## Repo purpose
Configuration toolsets and docs for Wayne's Debian-based LAN machines.
Three sections currently exist: `proxmox/` (Proxmox VE hosts),
`pihole/` (Pi-hole DNS/DHCP), and `freeipa/` (FreeIPA identity
management). Each has its own README.
Four sections currently exist: `proxmox/` (Proxmox VE hosts),
`pihole/` (Pi-hole DNS/DHCP), `freeipa/` (FreeIPA identity management),
and `raspberrypi/` (Raspberry Pi 4). Each has its own README.
## Safety rules (apply everywhere in this repo)
@@ -40,6 +40,10 @@ management). Each has its own README.
management server (`domain-controller.sweet.home`, VMID 108 on `pve1`).
All scripts that SSH into the server are production operations — treat
them as off-limits without an explicit same-session go-ahead.
- **Raspberry Pi:** see `raspberrypi/CLAUDE.md` — production host running
live services; same write-authorisation rules as `pve1`. Bootstrap access
via local `raspi` user (NOPASSWD sudo). IPA-enrolled; docker GID pinned
to 50010 to match IPA `docker-access` group.
## Adding a new machine type
+8
View File
@@ -10,6 +10,7 @@ service type.
proxmox/ Proxmox VE hosts (pve1 production, pve-test sandbox)
pihole/ Pi-hole DNS/DHCP (config snapshots, pull/apply scripts)
freeipa/ FreeIPA identity management server (domain-controller.sweet.home)
raspberrypi/ Raspberry Pi 4 (Debian 12, IPA-enrolled, Docker host)
scripts/ Repo-wide scripts (secret scanning, git hook installer)
```
@@ -34,6 +35,13 @@ LDAP, and integrated DNS for the `sweet.home` realm. See
`freeipa/README.md` for the quick-start and `freeipa/docs/install.md`
for the full reproduction procedure.
### `raspberrypi/`
Raspberry Pi 4 running Debian 12 bookworm (`raspberrypi.tail13f623.ts.net`,
reachable from LAN via Tailscale MagicDNS). IPA-enrolled; runs Traefik,
Uptime Kuma, CrowdSec, and Beszel. See `raspberrypi/README.md` for
setup scripts and current status.
## Secret scanning
All commits are scanned for secrets by a CI pipeline that runs on both
+39
View File
@@ -0,0 +1,39 @@
# raspberrypi CLAUDE.md
Guardrails for Claude Code working on raspberrypi configuration.
## Host
`raspberrypi.tail13f623.ts.net` (100.86.56.87) — Raspberry Pi 4, Debian 12
bookworm (aarch64). Reachable from LAN via Tailscale MagicDNS.
## Production status
The Raspberry Pi is a **production host** running live services (Traefik,
Uptime Kuma, CrowdSec, Beszel agent). Treat it the same as pve1: read-only
inspection is always fine; any script that writes to the host requires
explicit same-session operator go-ahead.
## Bootstrap access
The local `raspi` account has NOPASSWD sudo and the nixos ED25519 key
authorized. Use it to bootstrap IPA sudo rules or make root-level changes
when `wayne` sudo is not yet working.
## IPA integration
- Enrolled in `SWEET.HOME` realm via `ipa-client-install`.
- SSSD resolves IPA groups: `admins (50000)`, `docker-access (50010)`.
- `%admins NOPASSWD:ALL` granted via `/etc/sudoers.d/ipa-admins`
(written by `scripts/setup-ipa-sudo.sh`).
## Docker GID
The local `docker` group GID is pinned to 50010 (`groupmod --non-unique`)
so it matches the IPA `docker-access` group. Members of `docker-access`
in IPA can run docker without any per-host group membership entry.
Applied by `scripts/setup-docker-ipa-gid.sh`.
## What must never be committed
SSH private keys, passwords, API tokens, Tailscale auth keys.
+52
View File
@@ -0,0 +1,52 @@
# raspberrypi
Configuration scripts for `raspberrypi.tail13f623.ts.net` — Raspberry Pi 4
running Debian 12 bookworm (aarch64). Reachable from LAN via Tailscale
MagicDNS (`tail13f623.ts.net`).
## Services
- **Traefik** — reverse proxy (ports 80, 443, 8080)
- **Uptime Kuma** — uptime monitoring
- **CrowdSec** — intrusion detection
- **Beszel agent** — metrics collection
## IPA enrollment
The Pi is enrolled in the `sweet.home` FreeIPA domain. SSSD resolves:
- `admins (GID 50000)` — sudo access
- `docker-access (GID 50010)` — docker socket access
## Setup scripts
Run these **as root** (or via `sudo`) after `ipa-client-install` completes.
### 1. IPA sudo
```bash
sudo ./scripts/setup-ipa-sudo.sh
```
Writes `/etc/sudoers.d/ipa-admins` granting `%admins NOPASSWD:ALL`. After
this, IPA users in the `admins` group can `sudo` without a password.
### 2. Docker GID
```bash
sudo ./scripts/setup-docker-ipa-gid.sh
```
Pins the local `docker` group GID to 50010 to match the IPA `docker-access`
group. Restarts `docker.socket` + `docker.service` to recreate the socket
with the new GID. After this, IPA members of `docker-access` can run docker
without any per-host group membership entry.
## Current status
| Item | Status |
|---|---|
| IPA enrollment | ✅ |
| SSH (wayne) | ✅ via `raspberrypi.tail13f623.ts.net` |
| Sudo (wayne, NOPASSWD) | ✅ `/etc/sudoers.d/ipa-admins` |
| Docker (wayne, via IPA group) | ✅ docker group GID = 50010 |
| Bootstrap sudo | ✅ local `raspi` user, NOPASSWD |
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
# Shared helpers for raspberrypi scripts. Sourced, not executed directly:
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# source "${SCRIPT_DIR}/lib/common.sh"
require_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "Must run as root." >&2
exit 1
fi
}
# backup_file <path>
# Copies an existing file to <path>.bak.<epoch>. No-op if it doesn't exist.
backup_file() {
local path="$1"
if [ -f "$path" ]; then
cp "$path" "${path}.bak.$(date +%s)"
echo "Backed up ${path}"
fi
}
# write_if_changed <path> <content>
# Writes content to path only if it differs from what's already there,
# backing up the previous version first. Prints what happened.
write_if_changed() {
local path="$1" content="$2"
if [ -f "$path" ] && [ "$(cat "$path")" = "$content" ]; then
echo "Already up to date: $path"
return 0
fi
backup_file "$path"
printf '%s\n' "$content" > "$path"
echo "Wrote $path"
}
@@ -0,0 +1,53 @@
#!/bin/bash
# Pin the local 'docker' group GID to match the IPA 'docker-access' group
# (GID 50010) so that IPA group membership alone grants docker socket access.
#
# On Debian, GID 50010 is already present via SSSD (the IPA group), so
# groupmod requires --non-unique to allow the local docker group to share
# it. The docker.socket + docker.service pair must be fully stopped before
# removing the stale socket so systemd recreates it with the new GID.
#
# Idempotent — exits 0 without touching anything if the GID is already 50010.
#
# Run as root after setup-ipa-sudo.sh has been applied and docker is running.
#
# Usage: sudo ./setup-docker-ipa-gid.sh
set -euo pipefail
DOCKER_ACCESS_GID=50010
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/common.sh
source "${SCRIPT_DIR}/lib/common.sh"
require_root
if ! command -v docker &>/dev/null; then
echo "ERROR: docker not found -- install Docker before running this script." >&2
exit 1
fi
current_gid=$(getent group docker | cut -d: -f3)
if [ "$current_gid" = "$DOCKER_ACCESS_GID" ]; then
echo "docker group is already GID ${DOCKER_ACCESS_GID} -- nothing to do."
exit 0
fi
echo "Changing docker group GID: ${current_gid} -> ${DOCKER_ACCESS_GID}"
# SSSD exposes GID 50010 via the IPA docker-access group, so groupmod sees
# it as already in use. --non-unique lets the local docker group share it.
groupmod --non-unique -g "${DOCKER_ACCESS_GID}" docker
echo "Restarting docker to recreate socket with new GID..."
systemctl stop docker.service docker.socket
rm -f /var/run/docker.sock
systemctl start docker.socket docker.service
actual_gid=$(stat -c '%g' /var/run/docker.sock)
if [ "$actual_gid" != "$DOCKER_ACCESS_GID" ]; then
echo "ERROR: socket GID is ${actual_gid}, expected ${DOCKER_ACCESS_GID}" >&2
exit 1
fi
echo "Done. Docker socket is now GID ${DOCKER_ACCESS_GID} (docker / docker-access)."
echo "IPA members of the 'docker-access' group can now run docker without explicit local group membership."
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Grant the IPA 'admins' group passwordless sudo on this Raspberry Pi.
#
# Writes /etc/sudoers.d/ipa-admins with NOPASSWD: ALL for the admins group.
# Idempotent — safe to re-run.
#
# Run as root (or via the local 'raspi' user's NOPASSWD sudo) after
# ipa-client-install has been completed and SSSD is active.
#
# Usage: sudo ./setup-ipa-sudo.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/common.sh
source "${SCRIPT_DIR}/lib/common.sh"
require_root
if [ ! -f /etc/ipa/default.conf ]; then
echo "ERROR: /etc/ipa/default.conf not found -- is this host enrolled in FreeIPA?" >&2
exit 1
fi
write_if_changed /etc/sudoers.d/ipa-admins '%admins ALL=(ALL) NOPASSWD:ALL'
chmod 0440 /etc/sudoers.d/ipa-admins
visudo -c >/dev/null
echo "Sudoers file valid. IPA admins group has NOPASSWD sudo on this host."