Skip to content
Deployment

Deployment

github-sts can be deployed with Docker (this page) or with the Helm chart on Kubernetes. For Kubernetes, see Kubernetes.

Docker

The official image is published at ghcr.io/depthmark/github-sts, built from a distroless base with a nonroot user for a minimal attack surface. Pin a release tag (e.g. 0.0.3) in production rather than latest.

# Run with config file
docker run -p 8080:8080 \
  -v $(pwd)/config/github-sts.example.yaml:/etc/github-sts/config.yaml:ro \
  -e GITHUBSTS_CONFIG_PATH=/etc/github-sts/config.yaml \
  -e GITHUBSTS_APP_DEFAULT_APP_ID="$GITHUBSTS_APP_DEFAULT_APP_ID" \
  -e GITHUBSTS_APP_DEFAULT_PRIVATE_KEY="$GITHUBSTS_APP_DEFAULT_PRIVATE_KEY" \
  ghcr.io/depthmark/github-sts:0.0.3

# Run with env vars only (explicit YAML-only development posture)
docker run -p 8080:8080 \
  -e GITHUBSTS_CONFIG_PATH=/dev/null \
  -e GITHUBSTS_APP_DEFAULT_APP_ID="$GITHUBSTS_APP_DEFAULT_APP_ID" \
  -e GITHUBSTS_APP_DEFAULT_PRIVATE_KEY="$GITHUBSTS_APP_DEFAULT_PRIVATE_KEY" \
  -e GITHUBSTS_OIDC_ALLOWED_ISSUERS="https://token.actions.githubusercontent.com" \
  -e GITHUBSTS_OIDC_REQUIRED_AUDIENCE="https://sts.example.com" \
  -e GITHUBSTS_OIDC_REQUIRE_IMMUTABLE_SUBJECT_CLAIMS="true" \
  -e GITHUBSTS_BUNDLE_ENFORCEMENT="optional" \
  ghcr.io/depthmark/github-sts:0.0.3

Building from source instead: docker build -t github-sts:local . from the repository root, then swap the image name above.

The env-only example intentionally has no enterprise bundle. It emits the optional/YAML-only warning and posture signals. Production deployments should mount a config with bundle_enforcement: required and a pinned, verified global baseline. See Enterprise Rego bundles.

Prebuilt binaries

Each release also publishes tar.gz archives of the server for Linux and macOS on amd64 and arm64, attached to the GitHub release.

Docker remains the recommended way to run the server. Use a binary when a container runtime is unavailable, or when you want to run the server directly on a host.

The archives contain the server only, matching the container image. The github-sts-bundle revision gate is a build-time tool rather than a deployed one, so it is run straight from the module at a pinned tag, under “Revision promotion checks” in Configuration.

curl -sSLO "https://github.com/Depthmark/github-sts/releases/latest/download/github-sts_Linux_x86_64.tar.gz"
tar xzf github-sts_Linux_x86_64.tar.gz github-sts

To pin a release rather than track the newest one, replace latest/download with download/v<version>. Older releases carry the container image only.

The server takes no command-line flags. It reads its configuration from GITHUBSTS_CONFIG_PATH and the GITHUBSTS_* environment variables, exactly as the Docker examples above do, and exits with a validation error when a required setting is missing. See Configuration.

Verifying a downloaded archive

Every release carries two independent supply-chain claims, and neither one is checked for you. Verify both before running a binary from a release.

You need cosign v3 or later and the GitHub CLI, authenticated with gh auth login. On macOS use shasum -a 256 in place of sha256sum; it has no --ignore-missing equivalent, so check one archive by name instead of the whole list.

The first claim is a cosign signature over checksums.txt. The release runs inside a shared reusable workflow, so the signing identity is that workflow in Depthmark/reusable-workflows, not this repository. Verify the checksum list, then verify the archive against it:

BASE="https://github.com/Depthmark/github-sts/releases/latest/download"
curl -sSLO "${BASE}/checksums.txt"
curl -sSLO "${BASE}/checksums.txt.sigstore.json"

cosign verify-blob \
  --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/Depthmark/reusable-workflows/' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  checksums.txt

sha256sum --ignore-missing --check checksums.txt

cosign verify-blob prints Verified OK and exits zero on success. Both certificate flags are mandatory for keyless verification, so there is no silent pass to guard against: cosign refuses to run without an identity constraint. Widening the pattern is the real risk, because it accepts signatures from other workflows.

The second claim is a SLSA provenance attestation covering every artifact listed in that checksum file. It records which workflow, at which commit, produced the archive:

gh attestation verify github-sts_Linux_x86_64.tar.gz \
  --repo Depthmark/github-sts \
  --signer-workflow Depthmark/reusable-workflows/.github/workflows/go-release.yml

--repo is mandatory. --signer-workflow is what pins the attestation to the release workflow: without it the check still passes for any workflow in Depthmark/github-sts, which is a weaker claim than the one you want. The command exits non-zero when no attestation matches.

The container image carries its own signature and provenance, produced by a different reusable workflow, so it verifies against a different signer identity. The release run prints the image commands in its job summary.

TLS and mTLS

github-sts supports native HTTPS and mTLS, but does not manage certificates. TLS is enabled implicitly when both a certificate and a key are supplied; add a client CA bundle to require client certificates.

server:
  host: "0.0.0.0"
  port: 8443
  tls:
    cert_file: /etc/github-sts/tls/tls.crt
    key_file: /etc/github-sts/tls/tls.key
    # client_ca_file: /etc/github-sts/tls/ca.crt   # optional mTLS

Run it by mounting the certificate and key read-only (the container runs as a nonroot user, so the files must be readable by it):

docker run -p 8443:8443 \
  -v $(pwd)/config/github-sts.example.yaml:/etc/github-sts/config.yaml:ro \
  -v $(pwd)/certs:/etc/github-sts/tls:ro \
  -e GITHUBSTS_CONFIG_PATH=/etc/github-sts/config.yaml \
  github-sts:local

Warning: self-signed certificates are for local development only. In production, obtain certificates from a trusted CA (cert-manager/Let’s Encrypt, your internal PKI, or a managed service such as AWS ACM or Azure Key Vault) and terminate TLS at the ingress/Gateway where possible.

For a complete walkthrough, including generating a local CA, signing server and client certificates, and verifying HTTPS and mTLS rejection with curl, see Testing TLS and mTLS locally.

See Configuration for the full TLS option reference.

Production checklist

Before exposing github-sts publicly:

  • oidc.allowed_issuers is set to the explicit list of issuers you accept.
  • oidc.required_audience is set to a value unique to this STS deployment (e.g. https://sts.example.com). Each trust policy’s audience: matches it.
  • GitHub.com repositories have opted in to immutable subject claims and trust policies contain exact github.sources[] and github.target IDs. Keep oidc.require_immutable_subject_claims: true.
  • jti.backend is redis if you run more than one replica.
  • GitHub App private keys are mounted from a secret store (Kubernetes Secret, Vault, cloud KMS), not baked into images or env files.
  • /health and /ready are wired to liveness/readiness probes.
  • /metrics is scraped by Prometheus and dashboards are in place.
  • Audit log is written to a persistent location and forwarded to your SIEM.
  • Top-level bundle_enforcement is required; the GITHUBSTS_BUNDLE_ENFORCEMENT override cannot downgrade production unexpectedly.
  • Exactly one global baseline has apps: [] and fail_mode: closed; app-scoped bundles are additive.
  • Every bundle ref is trusted OCI pinned to @sha256:<64 lowercase hex>. File refs and mutable tags are not used.
  • Every bundle has a canonical expected_policy_revision matching its signed .manifest.revision; mandatory Rego metadata matches it too.
  • Policy release and deployment CI reject lower revisions and reused revision/digest values using a protected current tuple.
  • Every bundle is cosign verified with keyless signer/issuer constraints for Depthmark/github-sts-policy or an explicitly managed public_key_ref; verification is never skipped.
  • The mandatory baseline passes the fixed sts.enterprise.v1 metadata and negative-probe admission contract before rollout.
  • Alerts are configured for bundle pull failures, stale bundles, policy revision changes, and expiring exceptions.
  • TLS terminates at an ingress/Gateway, or native TLS/mTLS is enabled with CA-issued certificates (never self-signed in production).
  • Rate limits and request size limits are configured at the ingress layer.