Skip to content

Commit 83cbcaf

Browse files
author
rzisholz
committed
CP-21164: add Helm chart support for Conjur JWT authentication
Exposes config.cyberark's Conjur JWT fields (serviceId, account, jwtSource) in values.yaml/values.schema.json, and documents per-cluster Conjur onboarding as a prerequisite in the chart README — the tenant administrator's own Conjur Cloud credentials, no admin credential needed at deploy time. Drops the jwtFilePath knob: it let an operator set a path the chart's projected-volume mount couldn't actually honor (mountPath was hardcoded independently of it), producing a deployment that renders cleanly but never authenticates. The agent's own DefaultTokenPath already matches the chart's fixed mount path, so there's nothing to configure. Gates the projected token volume on serviceId being set, not just jwtSource (which defaults to "file") — a pure username/password install was getting an unused audience=conjur token volume for no reason, unnecessary surface for clusters with admission policies constraining projected-token audiences. Factored the condition into a named template since it's needed in two places and needs to stay in sync. Documents that the volume's audience is fixed at conjur, not a values.yaml setting, and that the config-validation fix in the previous PR makes the README's "fails closed at startup" claim actually true (previously that only held because sendSecretValues:true is the chart default and triggers eager authenticator construction). jwtSource now has an enum constraint in values.schema.json, so a typo fails at `helm install` with a clear message instead of a CrashLoopBackOff — ValidateJWTSource already rejects it at agent startup, this just moves where the operator finds out. Dropped the <!-- AUTO-GENERATED --> markers around the Values section: nothing regenerates it for this chart (the repo's generate target only covers venafi-kubernetes-agent), and the markers implied otherwise — the cyberark.* fields are already hand-documented above, outside that block, which the markers made look like an oversight rather than intent.
1 parent 6a31ea8 commit 83cbcaf

7 files changed

Lines changed: 199 additions & 28 deletions

File tree

deploy/charts/disco-agent/README.md

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,38 @@ kubectl create ns "$NAMESPACE" || true
1616

1717
### Add credentials to a Secret
1818

19-
You will require tenant details and credentials for the CyberArk Identity Security Platform.
20-
Put them in the following environment variables:
19+
The agent supports **two authentication methods**, selected automatically by
20+
config:
21+
22+
| Set this | Method used |
23+
|---|---|
24+
| `config.cyberark.serviceId` (Conjur authn-jwt service-id) | **Conjur JWT exchange** — exchanges a projected ServiceAccount token for a short-lived Conjur access token. No stored password. Preferred for new installs. |
25+
| `ARK_USERNAME` + `ARK_SECRET` in the Secret (and no `serviceId`) | **Legacy CyberArk Identity username/password** — backward compatible with existing GA installs. |
26+
27+
If **both** are set, the Conjur `serviceId` wins (so a migrating install can add
28+
the service-id before removing its old credentials) and a warning is logged. If
29+
**neither** is set, the agent fails closed at startup.
30+
31+
The only credential always required in the Kubernetes Secret is the CyberArk
32+
tenant subdomain (`ARK_SUBDOMAIN`).
2133

2234
```sh
23-
export ARK_SUBDOMAIN= # your CyberArk tenant subdomain e.g. tlskp-test
24-
export ARK_USERNAME= # your CyberArk username
25-
export ARK_SECRET= # your CyberArk password
26-
# OPTIONAL: the URL for the CyberArk Discovery API if not using the production environment
35+
export ARK_SUBDOMAIN= # your CyberArk tenant subdomain, e.g. tlskp-test
36+
# OPTIONAL: Discovery API URL for non-production environments
2737
export ARK_DISCOVERY_API=https://platform-discovery.integration-cyberark.cloud/
2838
```
2939

30-
Create a Secret containing the tenant details and credentials:
40+
Create the Secret:
3141

3242
```sh
43+
# Production (no ARK_DISCOVERY_API override needed):
44+
kubectl create secret generic agent-credentials \
45+
--namespace "$NAMESPACE" \
46+
--from-literal=ARK_SUBDOMAIN=$ARK_SUBDOMAIN
47+
48+
# Non-production, targeting a non-default Discovery API:
3349
kubectl create secret generic agent-credentials \
3450
--namespace "$NAMESPACE" \
35-
--from-literal=ARK_USERNAME=$ARK_USERNAME \
36-
--from-literal=ARK_SECRET=$ARK_SECRET \
3751
--from-literal=ARK_SUBDOMAIN=$ARK_SUBDOMAIN \
3852
--from-literal=ARK_DISCOVERY_API=$ARK_DISCOVERY_API
3953
```
@@ -49,23 +63,67 @@ metadata:
4963
namespace: cyberark
5064
type: Opaque
5165
stringData:
52-
ARK_SUBDOMAIN: $ARK_SUBDOMAIN # your CyberArk tenant subdomain e.g. tlskp-test
53-
ARK_SECRET: $ARK_SECRET # your CyberArk password
54-
ARK_USERNAME: $ARK_USERNAME # your CyberArk username
55-
# OPTIONAL: the URL for the CyberArk Discovery API if not using the production environment
66+
ARK_SUBDOMAIN: "tlskp-test" # your CyberArk tenant subdomain
67+
# OPTIONAL: uncomment for non-production Discovery API
5668
# ARK_DISCOVERY_API: https://platform-discovery.integration-cyberark.cloud/
69+
# LEGACY (only if NOT using Conjur serviceId) — username/password auth:
70+
# ARK_USERNAME: "svc-agent@tenant"
71+
# ARK_SECRET: "<password>"
5772
```
5873

59-
### Deploy the agent
74+
### Configure Conjur JWT authentication
75+
76+
> Skip this section if you are using the legacy username/password method
77+
> (set `ARK_USERNAME`/`ARK_SECRET` in the Secret and leave `serviceId` empty).
78+
79+
Set `config.cyberark.serviceId` to the authn-jwt authenticator service ID
80+
configured for this cluster in your Conjur tenant. This is the **bare service-id
81+
segment** (e.g. `disco-agent`), NOT the policy path `conjur/authn-jwt/disco-agent`
82+
— the agent builds the authenticate URL as
83+
`<base>/authn-jwt/<serviceId>/<account>/authenticate`, so a path here would
84+
double the `conjur/authn-jwt` prefix. The remaining defaults are correct for
85+
CyberArk-hosted tenants:
86+
87+
| Value | Default | Description |
88+
|---|---|---|
89+
| `config.cyberark.serviceId` | `""` | Conjur authn-jwt service ID (required). Example: `disco-agent` |
90+
| `config.cyberark.account` | `conjur` | Conjur account name. Always `conjur` for CyberArk-hosted tenants. |
91+
| `config.cyberark.jwtSource` | `file` | Token source. `file` = projected SA-token volume (default). `spiffe` deferred. |
92+
93+
When `config.cyberark.serviceId` is set and `jwtSource` is `file` (the
94+
default), the chart automatically renders a projected ServiceAccount token
95+
volume (audience=`conjur`, expiry 600 s) and mounts it at the fixed path the
96+
agent expects (`/var/run/secrets/tokens/jwt`) — this path isn't configurable,
97+
so there's one fewer way to misconfigure it. No manual volume configuration
98+
is required.
99+
100+
### Per-tenant Conjur onboarding
101+
102+
Before deploying the agent, the target tenant must be onboarded in Conjur
103+
Cloud: an `authn-jwt` authenticator scoped to the cluster's OIDC issuer and
104+
JWKS, a registered workload for the agent's ServiceAccount, and the grants that
105+
let that workload authenticate and upload. Onboarding is performed through the
106+
CyberArk web console — see the product documentation for the current
107+
walkthrough.
108+
109+
Onboarding needs only the tenant administrator's own Conjur Cloud credentials.
110+
The agent itself holds no Conjur identity beyond its projected ServiceAccount
111+
token, and nothing in this chart requires a Conjur admin credential at deploy
112+
time.
113+
114+
Once onboarding is complete, note the authenticator's service ID — that is the
115+
value for `config.cyberark.serviceId` below.
60116

61-
Deploy the agent:
117+
### Deploy the agent
62118

63119
```sh
64120
helm upgrade agent "oci://${OCI_BASE}/charts/disco-agent" \
65121
--install \
66122
--create-namespace \
67123
--namespace "$NAMESPACE" \
68-
--set fullnameOverride=disco-agent
124+
--set fullnameOverride=disco-agent \
125+
--set config.cyberark.serviceId=disco-agent \
126+
--set acceptTerms=true
69127
```
70128

71129
### Troubleshooting
@@ -80,9 +138,15 @@ Check the logs:
80138
kubectl logs deployments/disco-agent --namespace "${NAMESPACE}" --follow
81139
```
82140

83-
## Values
141+
#### Conjur authentication errors
84142

85-
<!-- AUTO-GENERATED -->
143+
| Symptom | Likely cause | Fix |
144+
|---|---|---|
145+
| Agent logs `401 Unauthorized` from Conjur | ServiceAccount token `audience` does not match the authenticator's configured `audience` value, or the authn-jwt authenticator is not enabled for the account | The chart's projected volume always requests `audience=conjur` — this is fixed, not a values.yaml setting. Confirm the Conjur `conjur/authn-jwt/<serviceId>/audience` variable is also set to `conjur`, and that the authenticator is enabled (`CONJUR_AUTHENTICATORS` includes `authn-jwt/<serviceId>`) |
146+
| Agent logs `403 Forbidden` from the upload API | The agent's workload is authenticated but not authorized to upload | Confirm in the CyberArk console that this cluster's workload was granted the uploader permission during onboarding |
147+
| Agent logs `500` / no upload attempt | Conjur is unreachable or returned an unexpected error | Check network policy / DNS; inspect Conjur audit logs for the host identity |
148+
149+
## Values
86150

87151
#### **replicaCount** ~ `number`
88152
> Default value:
@@ -456,5 +520,3 @@ endpointAdditionalProperties:
456520
targetLabel: instance
457521
```
458522
459-
<!-- /AUTO-GENERATED -->
460-

deploy/charts/disco-agent/templates/_helpers.tpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,13 @@ usage through tuple/variable indirection.
116116
{{- printf "%s" $defaultReference -}}
117117
{{- end -}}
118118
{{- end }}
119+
120+
{{/*
121+
Whether the Conjur JWT projected-token volume is needed: only when
122+
serviceId selects the Conjur JWT auth path AND jwtSource is unset or "file"
123+
(the only supported source in this POC) — a pure username/password install
124+
(serviceId unset) has no use for it.
125+
*/}}
126+
{{- define "disco-agent.conjurJWTEnabled" -}}
127+
{{- and .Values.config.cyberark.serviceId (or (not .Values.config.cyberark.jwtSource) (eq .Values.config.cyberark.jwtSource "file")) -}}
128+
{{- end }}

deploy/charts/disco-agent/templates/configmap.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ data:
1010
cluster_name: {{ .Values.config.clusterName | quote }}
1111
cluster_description: {{ .Values.config.clusterDescription | quote }}
1212
period: {{ .Values.config.period | quote }}
13+
cyberark:
14+
service_id: {{ .Values.config.cyberark.serviceId | quote }}
15+
account: {{ .Values.config.cyberark.account | quote }}
16+
jwt_source: {{ .Values.config.cyberark.jwtSource | quote }}
1317
{{- with .Values.config.excludeAnnotationKeysRegex }}
1418
exclude-annotation-keys-regex:
1519
{{- . | toYaml | nindent 6 }}

deploy/charts/disco-agent/templates/deployment.yaml

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,26 +58,32 @@ spec:
5858
valueFrom:
5959
fieldRef:
6060
fieldPath: spec.nodeName
61-
- name: ARK_USERNAME
61+
- name: ARK_SUBDOMAIN
6262
valueFrom:
6363
secretKeyRef:
6464
name: {{ .Values.authentication.secretName }}
65-
key: ARK_USERNAME
66-
- name: ARK_SECRET
65+
key: ARK_SUBDOMAIN
66+
- name: ARK_DISCOVERY_API
6767
valueFrom:
6868
secretKeyRef:
6969
name: {{ .Values.authentication.secretName }}
70-
key: ARK_SECRET
71-
- name: ARK_SUBDOMAIN
70+
key: ARK_DISCOVERY_API
71+
optional: true
72+
# Legacy CyberArk Identity username/password (backward compatibility).
73+
# Used only when config.cyberark.serviceId is empty; otherwise the
74+
# agent uses the Conjur JWT exchange and ignores these. Optional so
75+
# JWT-only installs need not set them.
76+
- name: ARK_USERNAME
7277
valueFrom:
7378
secretKeyRef:
7479
name: {{ .Values.authentication.secretName }}
75-
key: ARK_SUBDOMAIN
76-
- name: ARK_DISCOVERY_API
80+
key: ARK_USERNAME
81+
optional: true
82+
- name: ARK_SECRET
7783
valueFrom:
7884
secretKeyRef:
7985
name: {{ .Values.authentication.secretName }}
80-
key: ARK_DISCOVERY_API
86+
key: ARK_SECRET
8187
optional: true
8288
- name: ARK_SEND_SECRET_VALUES
8389
value: {{ .Values.config.sendSecretValues | default "false" | quote }}
@@ -116,6 +122,11 @@ spec:
116122
- name: config
117123
mountPath: "/etc/disco-agent"
118124
readOnly: true
125+
{{- if eq (include "disco-agent.conjurJWTEnabled" .) "true" }}
126+
- name: conjur-token
127+
mountPath: /var/run/secrets/tokens
128+
readOnly: true
129+
{{- end }}
119130
{{- with .Values.volumeMounts }}
120131
{{- toYaml . | nindent 12 }}
121132
{{- end }}
@@ -127,6 +138,15 @@ spec:
127138
configMap:
128139
name: {{ include "disco-agent.fullname" . }}-config
129140
optional: false
141+
{{- if eq (include "disco-agent.conjurJWTEnabled" .) "true" }}
142+
- name: conjur-token
143+
projected:
144+
sources:
145+
- serviceAccountToken:
146+
path: jwt
147+
audience: conjur
148+
expirationSeconds: 600
149+
{{- end }}
130150
{{- with .Values.volumes }}
131151
{{- toYaml . | nindent 8 }}
132152
{{- end }}

deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ custom-cluster-description:
66
cluster_name: ""
77
cluster_description: "A cloud hosted Kubernetes cluster hosting production workloads.\n\nteam: team-1\nemail: team-1@example.com\npurpose: Production workloads\n"
88
period: "12h0m0s"
9+
cyberark:
10+
service_id: ""
11+
account: "conjur"
12+
jwt_source: "file"
913
data-gatherers:
1014
- kind: oidc
1115
name: ark/oidc
@@ -165,6 +169,10 @@ custom-cluster-name:
165169
cluster_name: "cluster-1 region-1 cloud-1 "
166170
cluster_description: ""
167171
period: "12h0m0s"
172+
cyberark:
173+
service_id: ""
174+
account: "conjur"
175+
jwt_source: "file"
168176
data-gatherers:
169177
- kind: oidc
170178
name: ark/oidc
@@ -324,6 +332,10 @@ custom-period:
324332
cluster_name: ""
325333
cluster_description: ""
326334
period: "1m"
335+
cyberark:
336+
service_id: ""
337+
account: "conjur"
338+
jwt_source: "file"
327339
data-gatherers:
328340
- kind: oidc
329341
name: ark/oidc
@@ -483,6 +495,10 @@ defaults:
483495
cluster_name: ""
484496
cluster_description: ""
485497
period: "12h0m0s"
498+
cyberark:
499+
service_id: ""
500+
account: "conjur"
501+
jwt_source: "file"
486502
data-gatherers:
487503
- kind: oidc
488504
name: ark/oidc

deploy/charts/disco-agent/values.schema.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@
124124
"clusterName": {
125125
"$ref": "#/$defs/helm-values.config.clusterName"
126126
},
127+
"cyberark": {
128+
"$ref": "#/$defs/helm-values.config.cyberark"
129+
},
127130
"excludeAnnotationKeysRegex": {
128131
"$ref": "#/$defs/helm-values.config.excludeAnnotationKeysRegex"
129132
},
@@ -149,6 +152,38 @@
149152
"description": "A human readable name for the cluster where the agent is deployed (optional).\n\nThis cluster name will be associated with the data that the agent uploads to the Discovery and Context service. If empty (the default), the service account name will be used instead.",
150153
"type": "string"
151154
},
155+
"helm-values.config.cyberark": {
156+
"additionalProperties": false,
157+
"description": "CyberArk Conjur JWT authentication settings. The agent exchanges a projected ServiceAccount token (audience=conjur) for a short-lived Conjur access token used to authenticate to the Discovery & Context upload API.",
158+
"properties": {
159+
"account": {
160+
"$ref": "#/$defs/helm-values.config.cyberark.account"
161+
},
162+
"jwtSource": {
163+
"$ref": "#/$defs/helm-values.config.cyberark.jwtSource"
164+
},
165+
"serviceId": {
166+
"$ref": "#/$defs/helm-values.config.cyberark.serviceId"
167+
}
168+
},
169+
"type": "object"
170+
},
171+
"helm-values.config.cyberark.account": {
172+
"default": "conjur",
173+
"description": "The Conjur account name. For CyberArk-hosted tenants this is always \"conjur\".",
174+
"type": "string"
175+
},
176+
"helm-values.config.cyberark.jwtSource": {
177+
"default": "file",
178+
"description": "Token source for Conjur JWT authentication. \"file\" reads the token from the chart's own projected SA-token volume (fixed mount path, not configurable). \"spiffe\" is deferred and not implemented in this POC.",
179+
"enum": ["", "file"],
180+
"type": "string"
181+
},
182+
"helm-values.config.cyberark.serviceId": {
183+
"default": "",
184+
"description": "The Conjur authn-jwt authenticator service ID configured for this tenant. Bare segment, not the policy path. Example: disco-agent",
185+
"type": "string"
186+
},
152187
"helm-values.config.excludeAnnotationKeysRegex": {
153188
"default": [],
154189
"description": "You can configure the agent to exclude some annotations or labels from being pushed . All Kubernetes objects are affected. The objects are still pushed, but the specified annotations and labels are removed before being pushed.\n\nDots is the only character that needs to be escaped in the regex. Use either double quotes with escaped single quotes or unquoted strings for the regex to avoid YAML parsing issues with `\\.`.\n\nExample: excludeAnnotationKeysRegex: ['^kapp\\.k14s\\.io/original.*']",

deploy/charts/disco-agent/values.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,30 @@ config:
202202
# a key managed by CyberArk, fetched from the Discovery and Context service.
203203
sendSecretValues: true
204204

205+
# CyberArk Conjur JWT authentication settings.
206+
# The agent exchanges a projected ServiceAccount token (audience=conjur) for a
207+
# short-lived Conjur access token, then uses that token to authenticate to the
208+
# Discovery & Context upload API.
209+
cyberark:
210+
# The Conjur authn-jwt authenticator service ID configured for this tenant.
211+
# Set this to use the Conjur JWT exchange (preferred). Leave empty to use the
212+
# legacy CyberArk Identity username/password method (ARK_USERNAME/ARK_SECRET
213+
# in the credentials Secret) for backward compatibility. If both are set, the
214+
# serviceId (Conjur) wins.
215+
# NOTE: bare service-id segment (e.g. "disco-agent"), NOT the policy path
216+
# "conjur/authn-jwt/disco-agent" — the agent builds the URL as
217+
# <base>/authn-jwt/<serviceId>/<account>/authenticate.
218+
serviceId: ""
219+
220+
# The Conjur account name. For CyberArk-hosted tenants this is always "conjur".
221+
account: "conjur"
222+
223+
# Token source for Conjur JWT authentication.
224+
# "file" — read the token from the chart's own projected SA-token volume
225+
# (default; the mount path is fixed, not configurable).
226+
# "spiffe" — deferred; not implemented in this POC.
227+
jwtSource: file
228+
205229
authentication:
206230
secretName: agent-credentials
207231

0 commit comments

Comments
 (0)