For enterprise environments, SecureGuard offers extensive configuration options targeting High Availability (HA), advanced authentication, and multi-cluster setups.
By default, the dex component included in the SecureGuard Helm chart provisions a static mock user for local testing. In a production environment, you must configure Dex to federate authentication to your organization’s primary Identity Provider (IDP).
Dex supports multiple connectors, including:
To enable GitHub login for developers, update the dex.config section of your values-production.yaml.
dex:
config:
issuer: https://dex.secureguard.yourcompany.com
connectors:
- type: github
id: github
name: GitHub
config:
clientID: $GITHUB_CLIENT_ID
clientSecret: $GITHUB_CLIENT_SECRET
redirectURI: https://dex.secureguard.yourcompany.com/callback
orgs:
- name: your-github-org
Note: You must create an OAuth application in GitHub to obtain the Client ID and Secret, and ensure the redirectURI matches your Dex ingress.
Authentication is mandatory in SecureGuard, and authorization is delegated entirely to Kubernetes RBAC. The backend proxy authenticates to the Kubernetes API as its own service account and then impersonates the logged-in user on every request, using two headers derived from the user’s OIDC token:
Impersonate-User ← the email claimImpersonate-Group ← each entry in the groups claimThis means what a user can see and do in the dashboard is exactly what their Kubernetes RBAC allows — no more, no less. The proxy’s own service account is not a backdoor: it only holds the impersonate verb plus the narrow permissions its controller/agent paths need. Any Impersonate-* headers supplied by the browser are stripped before the proxy sets its own, so a user cannot escalate by spoofing an identity.
Consequence: a freshly authenticated user who has no RBAC bindings will be able to log in but will receive 403 Forbidden from the API server for every resource. You must grant access explicitly, as shown below.
groups claim. Group-based RBAC only works if your connector is configured to return groups (e.g. GitHub orgs/teams, an LDAP groupSearch, or an OIDC groups scope). Without it, bind roles to individual user emails instead.impersonate verb on users and groups. The bundled Helm chart and k8s/rbac.yaml already include this rule:
- apiGroups: [""]
resources: ["users", "groups"]
verbs: ["impersonate"]
Grant everyone in the platform-engineers OIDC group cluster-wide read access to ESO resources:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secureguard-viewer
rules:
- apiGroups: ["external-secrets.io"]
resources: ["externalsecrets", "secretstores", "clustersecretstores", "pushsecrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["namespaces", "events", "secrets"]
verbs: ["get", "list"] # secret values are still redacted by the proxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: secureguard-viewer-platform-engineers
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: secureguard-viewer
subjects:
# Group name must match the value Dex puts in the `groups` claim.
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: platform-engineers
Grant alice@yourcompany.com full control of ESO resources, but only in the payments namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secureguard-editor
namespace: payments
rules:
- apiGroups: ["external-secrets.io"]
resources: ["externalsecrets", "secretstores", "pushsecrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["events", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secureguard-editor-alice
namespace: payments
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: secureguard-editor
subjects:
# User name must match the value Dex puts in the `email` claim.
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice@yourcompany.com
Use kubectl auth can-i with --as / --as-group to confirm a binding behaves as expected before the user logs in — this mirrors exactly what the proxy does:
# As the user (email claim)
kubectl auth can-i list externalsecrets.external-secrets.io \
--namespace payments --as alice@yourcompany.com
# As a group member (groups claim)
kubectl auth can-i list secretstores.external-secrets.io \
--as-group platform-engineers
If these return no, the user will see 403 errors in the dashboard until the appropriate Role/ClusterRole binding is created.
A central principle of SecureGuard is centralized governance. You deploy the core SecureGuard stack (OpenBao, Dex, Dashboard, Proxy) in a central “Management” cluster, and use the SG Agent Controller to manage ESO lifecycle on “Target” clusters.
ESODeployment CRDs.The SG Agent Controller automates ESO deployment to target clusters. Create an ESODeployment resource on the management cluster:
apiVersion: deploy.secureguard.io/v1alpha1
kind: ESODeployment
metadata:
name: eso-prod-cluster
namespace: secureguard-system
spec:
targetCluster: prod-us-east
esoVersion: v2.6.0
scope: cluster
ha:
enabled: true
replicas: 2
rbac:
createClusterRoles: true
createRoleBindings: true
The controller will:
ESODeployment status fieldOnly ESO 2.x is supported. esoVersion must be v2.0.0 or newer; the end-of-life 0.x line is no longer offered. The set of versions the dashboard presents is driven by the ESO Version Catalog (see below), not a hardcoded list.
The ESO versions offered in the dashboard’s ESODeployment create/edit form are
not hardcoded — they are sourced from cluster-scoped ESOVersion resources
(deploy.secureguard.io/v1alpha1). This lets operators curate which ESO releases
are available per environment without rebuilding the dashboard: add a release by
applying a CR, retire one by setting deprecated: true.
apiVersion: deploy.secureguard.io/v1alpha1
kind: ESOVersion
metadata:
name: v2-6-0 # cluster-scoped; one CR per release
spec:
version: v2.6.0 # semver (v2.0.0 or newer); shown in the picker
latest: true # marks the recommended/default selection
deprecated: false # when true, hidden from new deployments
releaseDate: "2026-05-04"
minKubeVersion: "1.23"
notes: Latest stable release.
Behaviour in the dashboard:
deprecated ESOVersion, sorted newest-first.latest: true is labelled (latest) and used as the default;
if none is flagged, the newest version wins.GET on esoversions); writes
are rejected by the route allowlist. Curate the catalog with kubectl apply
or via the SG Agent, which holds manage permissions on esoversions.A starter catalog (v2.0.0–v2.6.0) ships in
k8s/samples/eso-deployments/esoversions.yaml, and the CRD lives in
k8s/esoversion-crd.yaml. If the catalog is empty or unreachable, the form falls
back to the version already set on the resource (so editing existing deployments
still works).
If you prefer to manage ESO installations manually, you can deploy ESO independently and connect target clusters to the central OpenBao:
https://openbao.secureguard.yourcompany.com) is reachable from the Target cluster nodes.helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace \
--set installCRDs=true
ClusterSecretStore in the Target cluster pointing to the Central OpenBao URL.Clusters can be added to the SecureGuard dashboard by uploading a kubeconfig file via the UI or the API:
curl -X POST http://localhost:3001/api/clusters/kubeconfig \
-F "kubeconfig=@/path/to/target-kubeconfig" \
-F "registerAgent=true" \
-F "clusterName=prod-us-east"
This creates per-cluster Secrets and optionally registers an SGAgent CR. See the API Reference for details.
The backend proxy is configured via environment variables. See the API Reference — Environment Variables for the complete list.
| Variable | Default | Description |
|---|---|---|
OIDC_ISSUER_URL |
— (required) | Dex/OIDC issuer URL — the proxy exits if unset (auth is mandatory) |
PORT |
3001 |
Proxy listen port |
DEFAULT_CONTEXT |
current-context | Default kubeconfig context |
POD_NAMESPACE |
default |
Namespace for per-cluster Secrets |
The proxy only forwards Kubernetes API paths explicitly listed in proxy/internal/proxy/routes.go. If you add a new CRD and need dashboard access, you must add the corresponding path patterns to the allowlist. See API Reference — Route Allowlist for the current list.
When you register a remote cluster you upload its kubeconfig, which usually carries a long-lived (often admin) credential. The proxy never persists that credential. Instead, at registration it replaces it with a short-lived, self-renewing token so nothing long-lived is ever stored:
secureguard-system/secureguard-remote) and ClusterRole on the remote
cluster. That ClusterRole grants exactly two things: impersonate on
users/groups (so per-user RBAC still applies), and create on its own
serviceaccounts/token (scoped by resourceNames).serviceaccounts/token permission, rewriting a per-cluster
token file under REMOTE_TOKEN_DIR. The proxy transport re-reads the file per
request. The agent does the same for clusters it talks to.This RBAC lives on the remote cluster and is created by code
(proxy/internal/remotebootstrap) — it is intentionally not in the
management-cluster k8s/rbac.yaml. The management-cluster proxy/agent
ServiceAccounts do not need create on serviceaccounts/token, because
their own management-cluster identity is the kubelet-projected token, and all
minting happens against the remote cluster with the remote SA’s self-granted
permission.
Minting is unconditional — there is no “store the credential as-is” mode. A kubeconfig whose credential cannot provision the remote ServiceAccount (for example a narrowly scoped bearer token) is rejected at registration. Exec-plugin credentials (EKS/GKE/AKS) are the one exception: they are stored unchanged because client-go rotates them natively, so there is nothing to mint.
The only related Helm knob is the writable scratch directory for the rotating token files (the container root filesystem is read-only):
proxy:
remoteTokenDir: /var/run/secureguard/tokens
Recovery window: because nothing long-lived is stored, an outage longer than the token TTL requires re-registering the cluster. Widen the window by requesting a longer TTL.
The Helm chart includes templates for RBAC, NetworkPolicies, and PodDisruptionBudgets. Enable them in your values file:
# RBAC (enabled by default)
serviceAccount:
create: true
# Network policies
networkPolicy:
enabled: true
# Pod Disruption Budget
podDisruptionBudget:
enabled: true
minAvailable: 1
For detailed RBAC configuration, see the Security Hardening Guide.
While the default SecureGuard chart can deploy OpenBao with integrated Raft storage for HA, you can alternatively configure OpenBao to use external storage backends if mandated by your infrastructure team.
OpenBao supports various storage backends, although Integrated Storage (Raft) is highly recommended for modern deployments. If required, you can configure PostgreSQL, Consul, or cloud-specific storage (e.g., AWS DynamoDB, GCP Spanner) by modifying the openbao.server.ha.config block.
SecureGuard does not currently expose a Prometheus /metrics endpoint. Monitoring is achieved through Kubernetes-native observability:
/healthz endpoint used for liveness and readiness probes/api/clusters/{id}/health endpoint checks connectivity to each cluster’s API serverlog package, outputting structured logs to stdoutkubectl get events for real-time activityFor comprehensive monitoring, integrate with your existing observability stack by collecting pod logs and Kubernetes events.