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.3Building 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-stsTo 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.txtcosign 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 mTLSRun 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:localWarning: 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_issuersis set to the explicit list of issuers you accept. -
oidc.required_audienceis set to a value unique to this STS deployment (e.g.https://sts.example.com). Each trust policy’saudience:matches it. - GitHub.com repositories have opted in to immutable subject claims and trust policies contain exact
github.sources[]andgithub.targetIDs. Keepoidc.require_immutable_subject_claims: true. -
jti.backendisredisif 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.
-
/healthand/readyare wired to liveness/readiness probes. -
/metricsis scraped by Prometheus and dashboards are in place. - Audit log is written to a persistent location and forwarded to your SIEM.
- Top-level
bundle_enforcementisrequired; theGITHUBSTS_BUNDLE_ENFORCEMENToverride cannot downgrade production unexpectedly. - Exactly one global baseline has
apps: []andfail_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_revisionmatching 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-policyor an explicitly managedpublic_key_ref; verification is never skipped. - The mandatory baseline passes the fixed
sts.enterprise.v1metadata 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.