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>
196 lines
6.7 KiB
Markdown
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.
|