## Summary Devpi was crash-looping under memory pressure on the minikube StatefulSet, breaking the Python toolchain across the repo (`mise run docs-mikado`, `prek`, every `uv pip install`). It moves to indri as a native LaunchAgent. ## What changed - **New ansible role** `ansible/roles/devpi/`: installs `devpi-server` + `devpi-web` into a uv-managed venv, initializes the server-dir on first run via 1Password root password, runs as a LaunchAgent (`mcquack.eblume.devpi`) bound to `127.0.0.1:3141`. Bootstraps from upstream PyPI (so devpi can install itself on a fresh box). - **Caddy**: `pypi.ops.eblu.me` now proxies to `http://localhost:3141`. - **Playbook**: `indri.yml` gains pre_tasks for the root password and the new role. - **service-versions.yaml**: devpi flipped from `type: argocd` to `type: ansible`. - **ArgoCD**: removed `apps/devpi.yaml` and `manifests/devpi/`. The in-cluster Application, namespace, and PVC have been deleted. - **Docs**: new how-to `docs/how-to/operations/devpi-on-indri.md`; `restart-indri.md` lists devpi in the LaunchAgent stop list. ## Already deployed (live on indri) - Service running: `launchctl list mcquack.eblume.devpi` → PID 53888 - `curl https://pypi.ops.eblu.me/+api` returns 200 ✅ - `mise run docs-mikado` works again ✅ - 1.0G of cached PyPI data was migrated from the PVC to `~erichblume/devpi/server-dir/` - Minikube namespace and PVC fully reclaimed ## Test plan - [ ] `mise run services-check` (after merge) - [ ] CI workflows that use devpi succeed - [ ] No regressions in tools that depend on `pypi.ops.eblu.me` (prek, uv-script tasks, dagger pipelines) ## Context This is the C1 prelude to a planned C2 chain (`mikado/retire-minikube-indri`) to retire minikube on indri entirely. Doing devpi as a standalone C1 was the right call because (a) it was urgent — it was breaking the toolchain — and (b) it shakes out the migration recipe before we commit to a multi-leaf chain. Reviewed-on: #341
200 lines
5.7 KiB
Text
200 lines
5.7 KiB
Text
// Tailnet ACL policy for tail8d86e.ts.net
|
|
// Managed by blumeops-pulumi
|
|
{
|
|
// ============== Groups ==============
|
|
"groups": {
|
|
// Placeholder for future Jellyfin media access
|
|
"group:allisonflix": [
|
|
"blume.erich@gmail.com",
|
|
"acmdavis@gmail.com",
|
|
],
|
|
},
|
|
|
|
// ============== Access Grants ==============
|
|
"grants": [
|
|
// --- Admins: full access to all infrastructure ---
|
|
{
|
|
"src": ["autogroup:admin"],
|
|
"dst": ["*"],
|
|
"ip": ["*"],
|
|
},
|
|
|
|
// --- Members: user-facing services only ---
|
|
// Kiwix, Forge, Miniflux, PostgreSQL
|
|
// (devpi moved off-cluster to indri; reachable via Caddy on tag:flyio-target)
|
|
{
|
|
"src": ["autogroup:member"],
|
|
"dst": ["tag:kiwix"],
|
|
"ip": ["tcp:443"],
|
|
},
|
|
{
|
|
"src": ["autogroup:member"],
|
|
"dst": ["tag:forge"],
|
|
"ip": ["tcp:443", "tcp:22"],
|
|
},
|
|
{
|
|
"src": ["autogroup:member"],
|
|
"dst": ["tag:feed"],
|
|
"ip": ["tcp:443"],
|
|
},
|
|
{
|
|
"src": ["autogroup:member"],
|
|
"dst": ["tag:pg"],
|
|
"ip": ["tcp:5432"],
|
|
},
|
|
// Note: No member access to grafana, loki, or NAS
|
|
|
|
// --- Infrastructure ---
|
|
{
|
|
"src": ["tag:homelab"],
|
|
"dst": ["tag:homelab"],
|
|
"ip": ["*"],
|
|
},
|
|
{
|
|
"src": ["tag:homelab"],
|
|
"dst": ["tag:nas"],
|
|
"ip": ["*"],
|
|
},
|
|
|
|
// --- Fly.io proxy ---
|
|
// Public reverse proxy can only reach explicitly tagged endpoints
|
|
{
|
|
"src": ["tag:flyio-proxy"],
|
|
"dst": ["tag:flyio-target"],
|
|
"ip": ["tcp:443"],
|
|
},
|
|
|
|
// --- CI Gateway ---
|
|
// Ephemeral CI containers can push images to registry
|
|
{
|
|
"src": ["tag:ci-gateway"],
|
|
"dst": ["tag:registry"],
|
|
"ip": ["tcp:443"],
|
|
},
|
|
|
|
// --- Kubernetes workloads ---
|
|
// k8s workloads (e.g., Woodpecker CI) can push/pull from registry
|
|
{
|
|
"src": ["tag:k8s"],
|
|
"dst": ["tag:registry"],
|
|
"ip": ["tcp:443"],
|
|
},
|
|
// k8s workloads (e.g., ArgoCD) can access forge on indri for GitOps
|
|
// HTTP on 3001, SSH on 2200
|
|
{
|
|
"src": ["tag:k8s"],
|
|
"dst": ["tag:homelab"],
|
|
"ip": ["tcp:3001", "tcp:2200"],
|
|
},
|
|
|
|
// Homelab can reach k8s services: PostgreSQL, CNPG metrics, Prometheus/Loki
|
|
{
|
|
"src": ["tag:homelab"],
|
|
"dst": ["tag:k8s"],
|
|
"ip": ["tcp:443", "tcp:5432", "tcp:9187"],
|
|
},
|
|
],
|
|
|
|
// ============== SSH Access ==============
|
|
"ssh": [
|
|
// Members can SSH to their own devices
|
|
{
|
|
"action": "check",
|
|
"src": ["autogroup:member"],
|
|
"dst": ["autogroup:self"],
|
|
"users": ["autogroup:nonroot"],
|
|
},
|
|
// Admins can SSH to homelab (for ansible)
|
|
{
|
|
"action": "check",
|
|
"src": ["autogroup:admin"],
|
|
"dst": ["tag:homelab"],
|
|
"users": ["autogroup:nonroot"],
|
|
"checkPeriod": "12h0m0s",
|
|
},
|
|
// Admins can SSH to NAS
|
|
{
|
|
"action": "check",
|
|
"src": ["autogroup:admin"],
|
|
"dst": ["tag:nas"],
|
|
"users": ["autogroup:nonroot"],
|
|
"checkPeriod": "12h0m0s",
|
|
},
|
|
// Homelab can SSH to homelab (for ansible, cross-host management)
|
|
// Tagged devices can't do interactive "check" auth, so use "accept".
|
|
{
|
|
"action": "accept",
|
|
"src": ["tag:homelab"],
|
|
"dst": ["tag:homelab"],
|
|
"users": ["autogroup:nonroot"],
|
|
},
|
|
],
|
|
|
|
// ============== Auto Approvers ==============
|
|
// Allow ProxyGroup pods (tag:k8s) to auto-approve VIP Services
|
|
// Required for multi-cluster Ingress per Tailscale docs
|
|
"autoApprovers": {
|
|
"services": {
|
|
"tag:k8s": ["tag:k8s"],
|
|
},
|
|
},
|
|
|
|
// ============== Tag Owners ==============
|
|
"tagOwners": {
|
|
"tag:blumeops": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:homelab": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:workstation": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:nas": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:grafana": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:kiwix": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:forge": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:loki": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:pg": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:feed": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:registry": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:k8s-api": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:k8s-operator": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"],
|
|
"tag:k8s": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"],
|
|
"tag:ci-gateway": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:flyio-proxy": ["autogroup:admin", "tag:blumeops"],
|
|
"tag:flyio-target": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"],
|
|
},
|
|
|
|
// ============== ACL Tests ==============
|
|
"tests": [
|
|
// Erich can access everything
|
|
{
|
|
"src": "blume.erich@gmail.com",
|
|
"accept": ["tag:grafana:443", "tag:kiwix:443", "tag:feed:443", "tag:loki:3100", "tag:pg:5432", "tag:homelab:22", "tag:registry:443", "tag:k8s-api:443"],
|
|
},
|
|
// Allison can access user services but NOT grafana, loki, or NAS
|
|
{
|
|
"src": "acmdavis@gmail.com",
|
|
"accept": ["tag:kiwix:443", "tag:forge:443", "tag:feed:443", "tag:pg:5432"],
|
|
"deny": ["tag:grafana:443", "tag:loki:3100", "tag:nas:445", "tag:registry:443", "tag:k8s-api:443"],
|
|
},
|
|
// Homelab can reach homelab, NAS, and k8s services (postgres, metrics, prometheus/loki)
|
|
{
|
|
"src": "tag:homelab",
|
|
"accept": ["tag:homelab:22", "tag:nas:445", "tag:k8s:443", "tag:k8s:5432", "tag:k8s:9187"],
|
|
},
|
|
// K8s workloads can reach registry and forge (on indri:3001 HTTP, :2200 SSH)
|
|
{
|
|
"src": "tag:k8s",
|
|
"accept": ["tag:registry:443", "tag:homelab:3001", "tag:homelab:2200"],
|
|
},
|
|
// CI gateway can push to registry
|
|
{
|
|
"src": "tag:ci-gateway",
|
|
"accept": ["tag:registry:443"],
|
|
},
|
|
// Fly.io proxy can only reach flyio-target tagged endpoints, nothing else.
|
|
// indri has tag:flyio-target (Caddy) so tag:homelab:443 is reachable on
|
|
// indri specifically but not other homelab devices.
|
|
{
|
|
"src": "tag:flyio-proxy",
|
|
"accept": ["tag:flyio-target:443"],
|
|
"deny": ["tag:k8s:443", "tag:homelab:22", "tag:nas:445", "tag:registry:443"],
|
|
},
|
|
],
|
|
}
|