diff --git a/tasks/new/2026-0823.01.gem.feat.spire.smart-image-packaging-and-single-volume-bootstrap-0941.md b/tasks/new/2026-0823.01.gem.feat.spire.smart-image-packaging-and-single-volume-bootstrap-0941.md new file mode 100644 index 0000000..3a181dc --- /dev/null +++ b/tasks/new/2026-0823.01.gem.feat.spire.smart-image-packaging-and-single-volume-bootstrap-0941.md @@ -0,0 +1,195 @@ +# TASK METADATA + +- **Target Files:** + - `spire/Dockerfile.server` + - `spire/Dockerfile.agent` + - `spire/entrypoint.server.sh` + - `spire/entrypoint.agent.sh` + - `spire/templates/server.conf` + - `spire/templates/agent.conf` + - `infra/compose.spire.yml` + - `infra/setup.ts` + - `infra/.env` +- **Core Objective:** Package standalone, generic Smart SPIRE Server and Agent + container images with self-seeding configuration entrypoints, and simplify the + stack to a 1-host-volume persistent layout with a dedicated named socket + volume for cross-stack zero-trust IPC. +- **Dependencies:** + - Upstream SPIRE binaries (`ghcr.io/spiffe/spire-server:1.9.3` and + `ghcr.io/spiffe/spire-agent:1.9.3`) + - Minimal Alpine base image (`${REG}/library/alpine:3.20`) +- **Additional Important Notes:** + - SPIRE must remain 100% decoupled and generic (named `spire-server` and + `spire-agent`, not prefixed with `auth-yes`). + - No inline shell scripts, heredocs, or temporary `init` helper containers in + `compose.spire.yml`. + - Storing configuration files on the persistent host volume ensures full user + editability while preventing upgrades from overwriting user customizations. + +--- + +## Architectural Considerations & Risks + +### Risks + +1. **Root CA Private Key Exposure:** If client workloads (e.g. `auth-api`) mount + the entire host SPIRE storage volume to reach `agent.sock`, they could + potentially read `keys.json` (server CA private keys) or SQLite databases. + - _Mitigation:_ The Workload API UNIX domain socket is written to and shared + via a dedicated named volume (`spire-socket`), which only exposes + `agent.sock` to client containers. +2. **Stale UNIX Socket on Unclean Shutdown:** An abrupt host crash or power loss + may leave a dead `agent.sock` file on disk. + - _Mitigation:_ `entrypoint.agent.sh` unconditionally runs + `rm -f /var/run/spire/agent.sock` before launching `spire-agent`. +3. **Database File Locking over Network Mounts:** SQLite datastores can suffer + locking issues over NFS/CIFS. + - _Mitigation:_ `SPIRE_DATA_PATH` defaults to local filesystem + `/volume1/docker/spire` (ext4/Btrfs). + +### Alternatives Considered + +- **Inline Compose Scripting (`spire-init` helper container):** Rejected. + Putting multi-line heredocs and shell scripts inside `compose.spire.yml` is + fragile across orchestrators (Portainer, Podman), pollutes compose files, and + degrades developer experience. +- **Separate Conf/Data Volumes (4+ volume mounts):** Rejected. Mounting + individual subfolders (`server/conf`, `server/data`, `agent/conf`, + `agent/data`) introduces unnecessary configuration complexity. A single host + root volume (`/volume1/docker/spire`) containing `server.conf`, `agent.conf`, + and `data/` subdirectories provides the cleanest DX. + +--- + +## Proposed Implementation + +### Phase 1: Create Generic SPIRE Smart Container Module (`spire/`) + +Create the decoupled `spire/` directory with self-bootstrapping Dockerfiles, +entrypoints, and master configuration templates: + +```text +spire/ +├── Dockerfile.server # Generic SPIRE Server Image +├── Dockerfile.agent # Generic SPIRE Agent Image +├── entrypoint.server.sh # Self-seeding bootstrap entrypoint +├── entrypoint.agent.sh # Self-seeding bootstrap entrypoint & stale socket cleanup +└── templates/ + ├── server.conf # Master commented configuration template + └── agent.conf # Master commented configuration template +``` + +#### 1. `spire/Dockerfile.server` + +- Multi-stage build extracting `spire-server` binary from + `ghcr.io/spiffe/spire-server:1.9.3` into `alpine:3.20`. +- Bakes master template into `/etc/spire/templates/server.conf`. +- Sets `ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]` and + `CMD ["run", "-config", "/opt/spire/server.conf"]`. + +#### 2. `spire/entrypoint.server.sh` + +- Checks if `/opt/spire/server.conf` exists on the mounted volume. +- If missing, copies `/etc/spire/templates/server.conf` to + `/opt/spire/server.conf` with `chmod 644`. +- Creates `/opt/spire/data/server` datastore directory. +- `exec /usr/local/bin/spire-server "$@"`. + +#### 3. `spire/Dockerfile.agent` + +- Multi-stage build extracting `spire-agent` binary from + `ghcr.io/spiffe/spire-agent:1.9.3` into `alpine:3.20`. +- Bakes master template into `/etc/spire/templates/agent.conf`. +- Sets `ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]` and + `CMD ["run", "-config", "/opt/spire/agent.conf"]`. + +#### 4. `spire/entrypoint.agent.sh` + +- Removes any stale `/var/run/spire/agent.sock` file. +- Checks if `/opt/spire/agent.conf` exists on the mounted volume. +- If missing, copies `/etc/spire/templates/agent.conf` to + `/opt/spire/agent.conf` with `chmod 644`. +- Creates `/opt/spire/data/agent` cache directory and `/var/run/spire`. +- `exec /usr/local/bin/spire-agent "$@"`. + +#### 5. `spire/templates/server.conf` & `spire/templates/agent.conf` + +- Heavily commented HCL templates configuring trust domain (`system.local`), + SQLite datastore (`/opt/spire/data/server/datastore.sqlite3`), key storage, + and Docker workload attestor. + +--- + +### Phase 2: Ultra-Clean Compose Configuration (`infra/compose.spire.yml`) + +Update +[`infra/compose.spire.yml`](file:///home/tylerg/p/data/auth-yes/infra/compose.spire.yml) +to use the 1-volume host layout and named socket volume: + +```yaml +version: "3.8" + +services: + spire-server: + image: ${REG}/library/spire-server:latest + container_name: spire-server + hostname: spire-server + networks: + - auth-internal-net + volumes: + - spire-data:/opt/spire + + spire-agent: + image: ${REG}/library/spire-agent:latest + container_name: spire-agent + hostname: spire-agent + pid: host + depends_on: + - spire-server + networks: + - auth-internal-net + volumes: + - spire-data:/opt/spire + - spire-socket:/var/run/spire + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + spire-data: + driver: local + driver_opts: + type: none + device: ${SPIRE_DATA_PATH} + o: bind + spire-socket: + name: spire-socket + +networks: + auth-internal-net: + external: true +``` + +--- + +### Phase 3: Setup CLI & Build Pipeline Integration (`infra/setup.ts`) + +Update `generateBuildCommands` in +[`infra/setup.ts`](file:///home/tylerg/p/data/auth-yes/infra/setup.ts) to build +all stack images: + +1. `auth-yes-api:latest` (`Dockerfile`) +2. `spire-server:latest` (`spire/Dockerfile.server`) +3. `spire-agent:latest` (`spire/Dockerfile.agent`) + +Update `DEFAULT_AUTH_CONFIG`: + +- `dbDataPath: "/volume1/docker/auth-yes/data"` +- `spireDataPath: "/volume1/docker/spire"` + +--- + +### Phase 4: Quality Gates & Verification + +1. Run `deno fmt` and `deno task lint`. +2. Run `deno task check` across all modules. +3. Run `deno test -A` to verify unit and integration tests. +4. Verify image build commands via `podman build` test.