Wardengate

Administer

Asset onboarding

Targets are the things users actually connect to — Linux servers, Windows hosts, databases, Kubernetes clusters. Onboarding a target is the act of telling Wardengate where it lives, how to reach it, and which accounts may be used there. This page walks through the supported kinds, the two connection modes, and the patterns that hold up as you scale from dozens to thousands.

Supported target types

  • Linux and BSD hosts over SSH.
  • Windows hosts over RDP with TLS or CredSSP.
  • PostgreSQL, MySQL / MariaDB, MongoDB, Redis, Microsoft SQL Server.
  • Kubernetes API servers — kubectl is proxied with its own policy hooks.
  • HTTP applications behind Wardengate's reverse-proxy target kind.

Each target has a protocol, a network address, zero or more accounts, and a set of tags. Tags are the connective tissue: policies, scoped roles, and notification rules all select on them.

Proxy mode vs agent mode

Wardengate brokers sessions in one of two ways:

  • Proxy mode — the gateway opens an outbound TCP connection to the target and relays the protocol. Nothing runs on the target. Use this when the target is in a network the gateway can reach directly.
  • Agent mode — a lightweight agent on the target reaches out to the gateway and the brokered session runs over that reverse tunnel. Use this for assets behind NAT, in customer VPCs, or on laptops that must be reachable from anywhere.

Proxy mode is the default. Agent mode is selected per target by flipping spec.mode: agent and installing the agent package. Both modes honour the same policy layer.

Credential modes

Each target declares how credentials are presented to the far end:

  • Stored — a password, SSH key, or certificate held in the Wardengate vault. Encrypted at rest with envelope encryption; the unwrapping key sits in a KMS or HSM.
  • External secret — a reference to HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault. Fetched per session.
  • Just-in-time — Wardengate creates a short-lived account on the target at connection time and tears it down at disconnect. Nothing to rotate, nothing to leak.
  • SSH certificate — Wardengate signs an ephemeral user certificate with an internal CA that the target trusts. Zero stored secrets on the target side.

Linux SSH

apiVersion: wardengate/v1
kind: Target
metadata:
  name: web-01.prod
  organization: acme
spec:
  protocol: ssh
  address: 10.10.4.20:22
  mode: proxy
  hostKey:
    algorithm: ssh-ed25519
    fingerprint: SHA256:h8Cj...yqI=
  accounts:
    - name: deploy
      credential: { kind: stored, ref: vault/ssh/deploy-prod }
    - name: ops
      credential: { kind: sshCertificate, ca: internal-ssh-ca }
  tags:
    env: prod
    role: web
    team: platform

Pin host keys on first onboard. Wardengate will refuse to connect if the presented key does not match the pinned fingerprint; that is the bit that protects you from a silent man-in-the-middle inside your own network.

Windows RDP

apiVersion: wardengate/v1
kind: Target
metadata:
  name: win-dc01.prod
  organization: acme
spec:
  protocol: rdp
  address: 10.10.2.5:3389
  security: tls
  nla: true
  accounts:
    - name: "CORP\\svc_admin"
      credential: { kind: stored, ref: vault/win/dc-admin }
  tags:
    env: prod
    role: dc

Require NLA where the fleet supports it — the alternative is handing Wardengate a password to type into the RDP logon screen, which is worse on every axis. If the target's CA is not in the public trust store, reference a caBundleRef on the target.

PostgreSQL

apiVersion: wardengate/v1
kind: Target
metadata:
  name: billing.prod.pg
  organization: acme
spec:
  protocol: postgres
  address: billing.prod.rds.example:5432
  tls:
    mode: require
    serverName: billing.prod.rds.example
  accounts:
    - name: app_ro
      credential: { kind: stored, ref: vault/pg/billing-ro }
    - name: analyst
      credential: { kind: jit, template: analyst-role }
  tags:
    env: prod
    data: pii

JIT Postgres accounts are created by running a connection-time template in the target database — typically a CREATE ROLE with a generated password, a role grant, and a VALID UNTIL matching the session cap. Wardengate drops the role at disconnect.

MySQL, MongoDB, Redis

apiVersion: wardengate/v1
kind: Target
metadata:
  name: orders.prod.mysql
  organization: acme
spec:
  protocol: mysql
  address: orders.prod.mysql.example:3306
  tls: { mode: require }
  accounts:
    - name: reader
      credential: { kind: stored, ref: vault/mysql/orders-ro }
---
apiVersion: wardengate/v1
kind: Target
metadata:
  name: sessions.prod.mongo
  organization: acme
spec:
  protocol: mongodb
  address: mongo.prod.example:27017
  tls: { mode: require }
  authMechanism: SCRAM-SHA-256
  accounts:
    - name: reader
      credential: { kind: stored, ref: vault/mongo/sessions-ro }
---
apiVersion: wardengate/v1
kind: Target
metadata:
  name: cache.prod.redis
  organization: acme
spec:
  protocol: redis
  address: cache.prod.example:6379
  tls: { mode: require }
  accounts:
    - name: default
      credential: { kind: stored, ref: vault/redis/cache-default }

Kubernetes

apiVersion: wardengate/v1
kind: Target
metadata:
  name: prod-eu.k8s
  organization: acme
spec:
  protocol: kubernetes
  apiServer: https://k8s-prod-eu.internal:6443
  caBundleRef: k8s-prod-eu-ca
  accounts:
    - name: platform-viewer
      credential:
        kind: jit
        template: k8s-viewer-token
        groupBindings: ["platform:viewers"]
    - name: platform-admin
      credential:
        kind: jit
        template: k8s-admin-token
        groupBindings: ["platform:admins"]
  tags:
    env: prod
    region: eu-west-1

The Kubernetes target type mints short-lived tokens that bind the session to a Kubernetes group; the user's RBAC in the cluster is driven by cluster-side ClusterRoleBindings against those group names. Session recording captures kubectl request bodies — including the bits of YAML you apply.

Asset groups and label selectors

Once you have more than thirty targets, naming them individually in policy becomes a liability. Asset groups are label selectors that evaluate continuously — membership is always current, nothing to reconcile.

apiVersion: wardengate/v1
kind: AssetGroup
metadata:
  name: prod-web
  organization: acme
spec:
  selector: "env=prod,role=web"
  description: "Everything tagged as a production web host."

Discovery

Wardengate can pull inventory from AWS EC2/RDS, GCP Compute/Cloud SQL, Azure VMs, and Kubernetes clusters. Discovered assets land in a holding pool where an operator can review, tag, and promote them to real targets. Auto-promotion is available but off by default — the trade-off is between fewer clicks and the risk of exposing an asset before you have tagged it correctly.

wgctl discovery add aws \
  --org acme \
  --name aws-prod-eu \
  --role-arn arn:aws:iam::111122223333:role/wardengate-discovery \
  --regions eu-west-1,eu-central-1 \
  --tag-filter "Environment=prod"

Bulk import

For one-time migrations from spreadsheets or existing asset inventories, the CLI accepts CSV or multi-document YAML.

# CSV columns: name,protocol,address,account,tag:env,tag:role
wgctl target import --format csv --file fleet.csv --org acme

# YAML with a doc-per-target
wgctl apply -f targets/*.yaml

Health checks

Every target runs a light probe every 60 seconds from the nearest gateway — TCP open, TLS handshake, protocol greeting. Probes never authenticate, to keep them cheap and audit-clean. Failed probes flip the target to degraded and surface an alert through whatever notification channel is subscribed.

Related