103 lines
4.8 KiB
Markdown
103 lines
4.8 KiB
Markdown
|
|
# deploy/trillian — Trillian + transparency-log on Kubernetes
|
||
|
|
|
||
|
|
Deploys the production backend for ADR-0005 `POST /api/v1/log/entries` into
|
||
|
|
the `agent-trust` namespace of the BasicStack cluster.
|
||
|
|
|
||
|
|
## Component overview
|
||
|
|
|
||
|
|
| Manifest | What it brings up |
|
||
|
|
| ----------------------------- | ----------------------------------------------------- |
|
||
|
|
| `00-namespace.yaml` | `agent-trust` namespace. |
|
||
|
|
| `10-mysql.yaml` | MySQL 8.4 StatefulSet + schema init ConfigMap + secret. |
|
||
|
|
| `20-logserver.yaml` | Trillian LogServer Deployment + Service (8090 gRPC). |
|
||
|
|
| `21-logsigner.yaml` | Trillian LogSigner Deployment (`--force_master=true`). |
|
||
|
|
| `30-createtree-job.yaml` | One-shot Job that creates the LOG tree and writes the TreeID into a ConfigMap. Safe to re-apply. |
|
||
|
|
| `40-transparency-log.yaml` | `transparency-log` Deployment (ADR-0005 HTTP server) with `--backend=trillian`. |
|
||
|
|
|
||
|
|
`storage.sql` is the upstream Trillian MySQL schema (v1.6.1) vendored for
|
||
|
|
reference. The applied form lives inline inside the `trillian-mysql-init`
|
||
|
|
ConfigMap in `10-mysql.yaml`.
|
||
|
|
|
||
|
|
## First-time deploy
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# From a node with cluster access (e.g. k3s-cp-1).
|
||
|
|
kubectl apply -f deploy/trillian/00-namespace.yaml
|
||
|
|
kubectl apply -f deploy/trillian/10-mysql.yaml
|
||
|
|
kubectl -n agent-trust rollout status statefulset/trillian-mysql --timeout=5m
|
||
|
|
|
||
|
|
kubectl apply -f deploy/trillian/20-logserver.yaml
|
||
|
|
kubectl apply -f deploy/trillian/21-logsigner.yaml
|
||
|
|
kubectl -n agent-trust rollout status deployment/trillian-log-server --timeout=5m
|
||
|
|
kubectl -n agent-trust rollout status deployment/trillian-log-signer --timeout=5m
|
||
|
|
|
||
|
|
# Creates the tree and populates the trillian-log-id ConfigMap.
|
||
|
|
kubectl apply -f deploy/trillian/30-createtree-job.yaml
|
||
|
|
kubectl -n agent-trust wait --for=condition=complete job/trillian-createtree --timeout=5m
|
||
|
|
kubectl -n agent-trust get configmap trillian-log-id -o jsonpath='{.data.TRILLIAN_LOG_ID}{"\n"}'
|
||
|
|
|
||
|
|
# Build and push the transparency-log image to the in-cluster registry.
|
||
|
|
# See services/transparency-log/Dockerfile.
|
||
|
|
docker build -t harbor.basicstack.de/proofedmesh/transparency-log:v0.1.0 \
|
||
|
|
-f services/transparency-log/Dockerfile services/transparency-log
|
||
|
|
docker push harbor.basicstack.de/proofedmesh/transparency-log:v0.1.0
|
||
|
|
|
||
|
|
kubectl apply -f deploy/trillian/40-transparency-log.yaml
|
||
|
|
kubectl -n agent-trust rollout status deployment/transparency-log --timeout=5m
|
||
|
|
```
|
||
|
|
|
||
|
|
## Smoke test
|
||
|
|
|
||
|
|
Exercise the deployed service end-to-end against the real Trillian backend.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# From inside the cluster:
|
||
|
|
kubectl -n agent-trust run smoke --rm -it --restart=Never --image=curlimages/curl -- \
|
||
|
|
sh -euxc '
|
||
|
|
ENV=$(printf "%s" "{\"payloadType\":\"application/vnd.in-toto+json\",\"payload\":\"aGVsbG8\",\"signatures\":[{\"keyid\":\"k\",\"sig\":\"c2ln\"}]}")
|
||
|
|
# content_hash comes from the admin-api attest.EnvelopeContentHash (or the
|
||
|
|
# transparency-log backend.EnvelopeContentHash helper). The two produce
|
||
|
|
# byte-identical SHA-256(canonical envelope).
|
||
|
|
curl -sS -X POST http://transparency-log.agent-trust.svc.cluster.local/api/v1/log/entries \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d "{\"envelope\":$ENV,\"content_hash\":\"<precomputed>\"}"
|
||
|
|
'
|
||
|
|
```
|
||
|
|
|
||
|
|
A successful first submit returns `201 Created` with an inclusion proof
|
||
|
|
against a tree size >= 1. Repeating the same submit returns `200 OK` with
|
||
|
|
the same `log_index` (idempotency).
|
||
|
|
|
||
|
|
## Secret rotation
|
||
|
|
|
||
|
|
`trillian-mysql` currently ships with hard-coded credentials for the first
|
||
|
|
deploy. Rotate them to a Sealed Secret before any real attestations land:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Example — generate new creds, seal them, and replace 10-mysql.yaml's Secret.
|
||
|
|
NEW_PW=$(openssl rand -hex 24)
|
||
|
|
kubectl create secret generic trillian-mysql \
|
||
|
|
--namespace agent-trust \
|
||
|
|
--from-literal=MYSQL_ROOT_PASSWORD="$(openssl rand -hex 24)" \
|
||
|
|
--from-literal=MYSQL_USER=trillian \
|
||
|
|
--from-literal=MYSQL_PASSWORD="$NEW_PW" \
|
||
|
|
--from-literal=MYSQL_DATABASE=trillian \
|
||
|
|
--from-literal=MYSQL_URI="trillian:$NEW_PW@tcp(trillian-mysql.agent-trust.svc.cluster.local:3306)/trillian?parseTime=true" \
|
||
|
|
--dry-run=client -o yaml \
|
||
|
|
| kubeseal -o yaml > deploy/trillian/sealed-mysql-secret.yaml
|
||
|
|
```
|
||
|
|
|
||
|
|
Replace the plain Secret block in `10-mysql.yaml` with that sealed form and
|
||
|
|
re-apply.
|
||
|
|
|
||
|
|
## Signer key (future work)
|
||
|
|
|
||
|
|
Trillian stores its own tree-head signing key inside the `Trees` MySQL row.
|
||
|
|
That key is created and sealed by the createtree Job (`--signature_algorithm=ED25519`),
|
||
|
|
and the LogSigner uses it to sign STHs internally. **Our** transparency-log
|
||
|
|
service does not touch that key.
|
||
|
|
|
||
|
|
Services like admin-api sign DSSE envelopes via the separate `signing-service`
|
||
|
|
(DEV-599, already shipped). That service uses OpenBao Transit (future) or an
|
||
|
|
in-process keyring (current). It is unrelated to the Trillian tree key.
|