Archived
feat(raspberrypi): add Raspberry Pi configuration section
Secret Scan / Scan for secrets and sensitive config (push) Failing after 5s
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:
@@ -6,9 +6,9 @@ OVERRIDE any default behaviour and must be followed exactly.
|
|||||||
## Repo purpose
|
## Repo purpose
|
||||||
|
|
||||||
Configuration toolsets and docs for Wayne's Debian-based LAN machines.
|
Configuration toolsets and docs for Wayne's Debian-based LAN machines.
|
||||||
Three sections currently exist: `proxmox/` (Proxmox VE hosts),
|
Four sections currently exist: `proxmox/` (Proxmox VE hosts),
|
||||||
`pihole/` (Pi-hole DNS/DHCP), and `freeipa/` (FreeIPA identity
|
`pihole/` (Pi-hole DNS/DHCP), `freeipa/` (FreeIPA identity management),
|
||||||
management). Each has its own README.
|
and `raspberrypi/` (Raspberry Pi 4). Each has its own README.
|
||||||
|
|
||||||
## Safety rules (apply everywhere in this repo)
|
## 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`).
|
management server (`domain-controller.sweet.home`, VMID 108 on `pve1`).
|
||||||
All scripts that SSH into the server are production operations — treat
|
All scripts that SSH into the server are production operations — treat
|
||||||
them as off-limits without an explicit same-session go-ahead.
|
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
|
## Adding a new machine type
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ service type.
|
|||||||
proxmox/ Proxmox VE hosts (pve1 production, pve-test sandbox)
|
proxmox/ Proxmox VE hosts (pve1 production, pve-test sandbox)
|
||||||
pihole/ Pi-hole DNS/DHCP (config snapshots, pull/apply scripts)
|
pihole/ Pi-hole DNS/DHCP (config snapshots, pull/apply scripts)
|
||||||
freeipa/ FreeIPA identity management server (domain-controller.sweet.home)
|
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)
|
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`
|
`freeipa/README.md` for the quick-start and `freeipa/docs/install.md`
|
||||||
for the full reproduction procedure.
|
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
|
## Secret scanning
|
||||||
|
|
||||||
All commits are scanned for secrets by a CI pipeline that runs on both
|
All commits are scanned for secrets by a CI pipeline that runs on both
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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 |
|
||||||
@@ -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."
|
||||||
@@ -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."
|
||||||
Reference in New Issue
Block a user