auth-yes/tasks/complete/2026-0823.01.gem.feat.spire.smart-image-packaging-and-single-volume-bootstrap-0941.md
google-labs-jules[bot] 4360d67064 feat: Package generic SPIRE images and simplify compose volume
This implements custom Alpine-based Smart SPIRE Server and Agent Docker
images with self-seeding configuration entrypoints. It also updates
the docker-compose configuration to utilize a single host storage volume
and a dedicated named socket volume. Setup build pipelines are expanded to
compile and push these custom SPIRE images.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-23 17:20:11 +00:00

196 lines
6.7 KiB
Markdown

# 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.