OrkStageOrkStage

Security hardening

Security controls built into OrkStage, a hardening checklist for hosting it yourself, the known gaps and how to report a vulnerability.

Controls built into OrkStage

AreaControl
SessionsAn opaque random token of 32 bytes in an httpOnly, SameSite=Lax cookie (Secure in production). Only its SHA-256 hash is stored. Sessions expire after 12 hours and are revoked on logout.
CSRFEvery request with a session that changes state must send x-orkstage-csrf: 1. The API allows no CORS from other origins, so a browser on another site cannot send this header. Requests with a bearer API key are exempt.
OIDCAuthorization code grant with PKCE (S256). state and nonce are verified. The flow state lives in a signed, short lived cookie. returnTo accepts relative paths only. Provider tokens are discarded.
Account linkingOnly through an email address the provider has verified (email_verified, or xms_edov for Entra). OrkStage never trusts preferred_username, which prevents takeover in the style of "nOAuth". Unknown users get no account unless a tenant has put their domain on its allowlist.
Dev loginOff unless AUTH_DEV_LOGIN_ENABLED=true. Startup fails if it is on while NODE_ENV=production.
API keysOnly the SHA-256 hash of the token is stored. Scopes narrow a role and never exceed it. A key cannot manage keys. Keys support expiry, revocation and a last used time. Every action is audited with the key's name and prefix.
AuthorizationCASL abilities for each request. Queries for tenant data are scoped to the caller's memberships, and each record is checked again after loading. Records of other tenants return 404, not 403.
SecretsThe API rejects target configuration with keys that look like secrets. Credentials are env:// or aws-sm:// references. Services receive secret references, not values.
Cloud credentialsAssumeRole with an external id. The credentials are short lived, stay in memory and reach only the runner container that needs them.
WebhooksHMAC-SHA256 signatures with a timestamp, and https only. SSRF protection blocks private, loopback, link local and multicast targets, checked at registration and again before each delivery. No redirects are followed, the timeout is 10 seconds, and signing secrets are encrypted at rest.
LoggingPino redacts tokens, secrets, cookies and authorization headers. Errors return a code and a correlation id, never a stack trace.
Rate limits600 requests per minute per client overall. Stricter limits apply to login, deploy, destroy, target validation, template publishing, API key creation, imports and webhook tests.
AuditAppend only. A Postgres trigger rejects UPDATE and DELETE on the audit table.
HeadersHelmet with a restrictive Content Security Policy.
Supply chainOnly pnpm build scripts on an allowlist run. Telemetry during install is denied. Very recent releases are avoided (minimum release age). Container images are pinned by digest where it matters (MiniStack, OpenTofu).

Hardening checklist

  • NODE_ENV=production, dev login off and an OIDC provider configured.
  • SESSION_SECRET and ENCRYPTION_KEY generated at random and injected from a secret store.
  • TLS in front of the console and the API, with WEB_PUBLIC_URL and API_PUBLIC_URL on https://.
  • Postgres and Redis on private networks, with authentication and encryption in transit.
  • The worker on a dedicated host (it holds the Docker socket), with an IAM identity limited to sts:AssumeRole on your orkstage-deploy roles and secretsmanager:GetSecretValue on the referenced secrets.
  • One AWS account per environment, with deployment roles created from the provided CloudFormation template and a unique external id for each.
  • State buckets versioned, encrypted and private.
  • WEBHOOK_ALLOW_PRIVATE=false (enforced in production).
  • One API key per integration, with the smallest preset and an expiry.
  • Postgres backups tested by restoring them (see Backups and recovery).

Known gaps

  • MFA policy, and a console page that lists and revokes sessions.
  • Container image scanning in CI (the dependency audit already blocks merges on high severity advisories).
  • Postgres row level security as an extra isolation layer.
  • An OpenTofu runner that does not need the Docker socket (ECS RunTask or a Kubernetes Job).

Reporting a vulnerability

Email security@orkstage.io with the details and, if you can, a proof of concept. Please do not open public issues for security problems. We acknowledge each report and coordinate disclosure with you.

On this page