guildhouse-spire-plugins/docs/deployment.md
Tyler King 420a4e2ea0 Remediate all 17 audit findings from AUDIT.md
Critical fixes:
- F-01: SatScope array form support (single pointer → slice with polymorphic JSON)
- F-02: Add governance-intent@guildhouse.dev as 10th Shellstream extension
- F-06: Replace os.Exit(1) stubs with go-plugin Serve() boilerplate in all cmd/
- F-13: Validate SatScope.ResourcePattern is non-empty

High priority:
- F-03: Add normative Accord policy syntax note to credential-governance.md §8.2
- F-04: Replace OID XXXXX placeholder with explicit PEN reference and IANA TODO
- F-05: Document CredentialComposer hook mapping in spec and plugin-types.md
- F-07/F-08: Commit CI pipeline (.github/workflows/ci.yaml)
- F-09: Add hashicorp/go-plugin v1.6.3 to go.mod

Medium priority:
- F-10: Wire sample-ssh-cert-extensions.json fixture into shellstream tests
- F-11: Cross-reference merkle proof depth limit (256 leaves) in governance spec
- F-12: Add YAML format clarification headers to deploy configs
- F-14: Expand README with project status, docs links, and quick-start

Low priority:
- F-15: Standardize "SSH SVID" → "SSH-SVID" terminology across docs
- F-16: Add GovernanceEpochSeconds to PluginConfig and deploy configs
- F-17: Add troubleshooting section to deployment.md, error handling to OIDC docs

Global: Rename all extension keys from @guildhouse.io to @guildhouse.dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:45:33 -05:00

5.9 KiB

Deployment Guide

Kubernetes deployment of Guildhouse SPIRE plugins.

Prerequisites

  • SPIRE Server and Agent deployed (v1.9+)
  • Guildhouse Quartermaster services running (GovernanceService, NotaryService)
  • Guildhouse Bascule services running (CeremonyService)
  • mTLS connectivity between SPIRE and Guildhouse services via SPIFFE

Plugin Distribution

Plugin binaries are packaged as a container image:

ghcr.io/guildhouse-cooperative/spire-plugins:latest

The image contains:

/plugins/
  oidc-attestor
  ssh-credential-composer
  governance-notifier
  substrate-keymanager

Installation via Kustomize

Apply the overlay in deploy/kustomization.yaml on top of your SPIRE deployment:

kubectl apply -k deploy/

This patches the SPIRE Server and Agent Deployments to:

  1. Add an init container that copies plugin binaries to a shared volume
  2. Mount the plugin directory into the SPIRE containers

SPIRE Server Configuration

Add plugin blocks to your SPIRE Server configuration (server.conf):

plugins {
    KeyManager "guildhouse_substrate" {
        plugin_cmd = "/opt/spire/plugins/substrate-keymanager"
        plugin_data {
            trust_domain = "guildhouse.example.org"
            governance_addr = "governance.quartermaster.svc.cluster.local:50051"
            notary_addr = "notary.quartermaster.svc.cluster.local:50051"
            cluster_id = "guildhouse-prod"
        }
    }

    CredentialComposer "guildhouse_ssh" {
        plugin_cmd = "/opt/spire/plugins/ssh-credential-composer"
        plugin_data {
            trust_domain = "guildhouse.example.org"
            governance_addr = "governance.quartermaster.svc.cluster.local:50051"
            default_cert_ttl = "5m"
            max_cert_ttl = "1h"
        }
    }

    Notifier "guildhouse_governance" {
        plugin_cmd = "/opt/spire/plugins/governance-notifier"
        plugin_data {
            governance_addr = "governance.quartermaster.svc.cluster.local:50051"
            ceremony_addr = "ceremony.bascule.svc.cluster.local:50052"
            notary_addr = "notary.quartermaster.svc.cluster.local:50051"
            cluster_id = "guildhouse-prod"
            trust_domain = "guildhouse.example.org"
        }
    }
}

SPIRE Agent Configuration

Add the OIDC attestor to your SPIRE Agent configuration (agent.conf):

plugins {
    WorkloadAttestor "guildhouse_oidc" {
        plugin_cmd = "/opt/spire/plugins/oidc-attestor"
        plugin_data {
            issuer = "https://keycloak.guildhouse.example.org/realms/platform"
            audience = "spire"
            token_path = "/var/run/secrets/oidc/token"
        }
    }
}

RBAC

The SPIRE Server ServiceAccount needs access to read Kubernetes Secrets (for webhook secrets) and ConfigMaps (for Accord policy):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: spire-server-guildhouse
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]
    resourceNames: ["guildhouse-governance-certs"]
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]

mTLS Configuration

Plugins authenticate to Guildhouse services using the SPIRE Server's own SVID. The plugin inherits the server's SPIFFE identity and uses it for mTLS.

No additional certificate configuration is needed — the plugin obtains its mTLS credentials from the SPIRE Server's Workload API socket.

Health Checks

Each plugin logs its health status at startup. Monitor SPIRE Server logs for:

level=info msg="guildhouse_ssh: connected to GovernanceService"
level=info msg="guildhouse_governance: connected to CeremonyService"
level=info msg="guildhouse_substrate: key manager initialized"

Plugin failures surface as SPIRE Server errors during credential minting or event notification.

Environment Variables

Plugins read configuration from HCL plugin_data blocks. No environment variables are required. The SPIRE plugin framework passes configuration directly.

Upgrading

To upgrade plugins:

  1. Build new plugin container image
  2. Update image tag in deploy/kustomization.yaml
  3. Restart SPIRE Server and Agent pods
  4. SPIRE reloads plugins on startup

Troubleshooting

Plugin fails to load

Symptom: SPIRE Server logs plugin failed to start or plugin handshake failed.

Checks:

  1. Verify plugin binary exists at the configured plugin_cmd path inside the container.
  2. Ensure the init container completed successfully: kubectl logs <pod> -c copy-plugins.
  3. Check that the binary is executable: kubectl exec <pod> -- ls -la /opt/spire/plugins/.
  4. Verify the SPIRE version is v1.9+ (required for CredentialComposer support).

GovernanceService unreachable

Symptom: SPIRE Server logs guildhouse_ssh: GovernanceService unavailable or gRPC deadline exceeded errors.

Checks:

  1. Verify the GovernanceService address in plugin_data is correct and resolvable from the pod.
  2. Check mTLS connectivity: the SPIRE Server SVID must be trusted by the GovernanceService.
  3. Ensure the Quartermaster namespace services are running: kubectl get pods -n quartermaster.

OIDC attestation returns no selectors

Symptom: Workloads fail to receive SVIDs; agent logs show oidc-attestor: no token found.

Checks:

  1. Verify the projected token volume is mounted at the configured token_path.
  2. Check token expiry: projected tokens rotate automatically but may be stale if the kubelet is unhealthy.
  3. Verify the issuer and audience in plugin config match the actual token claims.

Ceremony timeout

Symptom: Credential issuance hangs and eventually fails with ceremony timeout.

Checks:

  1. Check that the CeremonyService is reachable at the configured ceremony_addr.
  2. Verify that an approver is available for the ceremony type (SingleApproval requires one approver, QuorumApproval requires the configured quorum).
  3. Review the ceremony_timeout_seconds setting in the Accord policy defaults.