Wardengate

Administer

Account lifecycle

An account in Wardengate is a credential used to authenticate onto a target — root@web-01, postgres, CORP\\svc_deploy. Users never see these credentials. They pick a target, pick which account to use, and Wardengate presents the credential to the far end on their behalf. This page is about how those credentials are stored, rotated, attested to, and retired.

Stored accounts

A stored account holds a credential inside the Wardengate vault. Supported shapes are passwords, SSH private keys, SSH certificates (with or without a key), X.509 client certificates, and database URIs. The vault uses envelope encryption: each secret is encrypted with a per-record data key, which is itself encrypted by a key held in your KMS or HSM. The control plane never sees cleartext data keys at rest.

apiVersion: wardengate/v1
kind: Account
metadata:
  name: deploy-prod
  organization: acme
spec:
  kind: stored
  type: ssh-key
  algorithm: ed25519
  publicKey: |
    ssh-ed25519 AAAAC3Nz...Tj deploy@acme
  privateKeyRef: vault/ssh/deploy-prod
  rotation:
    schedule: "90d"
    window: "sat 02:00-05:00 UTC"

External secret references

Secrets can live in HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault. Wardengate fetches at session start, caches for the session duration, and wipes after disconnect. Use external references when you already have a secret lifecycle you trust; use the native vault when you want rotation tied to Wardengate's own audit trail.

Just-in-time accounts

JIT accounts are the preferred shape for new deployments. Instead of holding a standing credential, Wardengate creates a short-lived principal on the target at session start and removes it at session end. Common JIT shapes:

  • Unix user — an agent on the host calls useradd with a derived name, installs an SSH authorized key for the duration, and removes the user at disconnect.
  • SSH certificate — an internal CA issues an ephemeral user certificate with the user's SAML-derived name as the principal. No mutation on the target at all.
  • Database role — Wardengate runs a CREATE ROLE with a VALID UNTIL matching the session cap, grants a role template, and drops the role at disconnect.
  • Kubernetes token — a TokenRequest creates a bound service-account token for the session's duration.
apiVersion: wardengate/v1
kind: Account
metadata:
  name: analyst
  organization: acme
spec:
  kind: jit
  target: billing.prod.pg
  template:
    language: sql
    create: |
      CREATE ROLE "{{user.loginName}}" LOGIN
        PASSWORD '{{session.generatedPassword}}'
        VALID UNTIL '{{session.expiresAt}}';
      GRANT analyst_ro TO "{{user.loginName}}";
    destroy: |
      DROP ROLE IF EXISTS "{{user.loginName}}";

Credential rotation

Stored credentials have a rotation schedule. When the schedule fires, Wardengate generates a new secret, writes it to the target (through the same protocol it brokers sessions over), verifies a round trip, and then flips the vault record. Old and new are both valid for a short overlap so in-flight sessions do not break.

wgctl account rotate deploy-prod
# rotation  queued
# schedule  immediate
# overlap   5m

wgctl account history deploy-prod
# rotated  2026-04-01 02:17Z  automatic   success  overlap=5m
# rotated  2025-12-31 02:19Z  automatic   success  overlap=5m
# rotated  2025-10-02 02:21Z  manual (op: sara)  success  overlap=5m

Rotation failure is noisy on purpose. A failed rotation leaves the old credential in place, sends a notification, and writes an audit event that holds the error detail. We would rather interrupt an operator than leave a half-rotated credential.

Break-glass accounts

Break-glass is the credential you use when everything else is on fire. Wardengate models it as a regular stored account with three flags turned on:

  • Use is approval-gated. No one can initiate a break-glass session without an approval, even if their policy would otherwise allow it.
  • Use triggers a critical-severity notification to every subscribed channel.
  • The credential rotates automatically at the end of every use — a break-glass secret has a lifetime of "until next use".
apiVersion: wardengate/v1
kind: Account
metadata:
  name: root-breakglass
  organization: acme
spec:
  kind: stored
  type: password
  privateKeyRef: vault/break-glass/root
  breakGlass:
    enabled: true
    rotateOnUse: true
    approvalRequired: true
    notify: ["pagerduty:security-oncall", "slack:#security-incidents"]

Account discovery

On targets where Wardengate can read the account list — Linux /etc/passwd, Windows local users, database role catalogues — the nightly discovery job surfaces accounts that exist on the target but are not in Wardengate. That is usually the list you want to clean up.

wgctl account discover --target web-01.prod

# account            status       seen
# root               managed      2026-04-18
# deploy             managed      2026-04-18
# oldadmin           orphaned     2026-04-18
# jenkins            unmanaged    2026-04-18

orphaned means Wardengate used to manage this account and no longer does — a stale remnant that should be removed or re-adopted. unmanaged means Wardengate has never owned it — either adopt it or decide it is out of scope.

Attestation

Attestation is the quarterly (or monthly) sweep where an owner reviews a list of accounts and either confirms they are still needed or flags them for removal. Wardengate records who attested, when, and the comment. The report is auditor-ready without any manual formatting.

wgctl attestation start \
  --org acme \
  --scope "data=pii" \
  --owner security-lead@acme.example \
  --due 2026-05-15

# creates a campaign, emails the owner, and exposes a console page
# each account in scope must be keep/remove'd before the due date.

Retiring an account

Retirement is a two-step, like org deletion. First freeze the account — no new sessions can use it, but historical recordings still reference it cleanly. After the cooling-off window, delete it from the vault. The audit record of who used the account and when remains forever; that is the point of it.

wgctl account freeze deploy-legacy --reason "replaced by deploy"
wgctl account purge deploy-legacy --after 30d

Related