Skip to content

Commit d76351a

Browse files
Workload Identity Implementation (#34)
Add Workload Identity auth mechanism Co-authored-by: BalKrishna <[email protected]>
1 parent 8d6041e commit d76351a

File tree

195 files changed

+3834
-1328
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

195 files changed

+3834
-1328
lines changed

.github/workflows/e2e-tests.yaml

Lines changed: 156 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ jobs:
123123

124124
deploy-provider:
125125
runs-on: ubuntu-latest
126-
name: Deploy Provider
126+
name: Deploy Provider and Run Tests
127127
needs: [ setup-vault , setup-cluster , build ]
128128
env:
129129
OCI_VAULT_ID: ${{ needs.setup-vault.outputs.OCI_VAULT_ID }}
@@ -181,87 +181,209 @@ jobs:
181181
kubectl get daemonset --namespace oci-provider \
182182
--selector='app.kubernetes.io/name in (oci-secrets-store-csi-driver-provider, secrets-store-csi-driver)'
183183
184-
- name: update auth file with correct values
184+
- name: user principal authentication - update auth file with correct values
185185
run: |
186186
sed -e 's/region:.*/region: ${{ env.OCI_CLI_REGION }}/' \
187187
-e 's/tenancy:.*/tenancy: ${{ env.OCI_CLI_TENANCY }}/' \
188188
-e 's/user:.*/user: ${{ env.OCI_CLI_USER }}/' \
189-
-e 's/fingerprint:.*/fingerprint: ${{ env.OCI_CLI_FINGERPRINT }}/' e2e/example/user-auth-config-example.yaml > e2e/example/user-auth-config-example.yaml.tmp
189+
-e 's/fingerprint:.*/fingerprint: ${{ env.OCI_CLI_FINGERPRINT }}/' e2e/example/user-principal/user-auth-config-example.yaml > e2e/example/user-principal/user-auth-config-example.yaml.tmp
190190
191191
# - name: print updated yaml file
192192
# run: cat e2e/example/user-auth-config-example.yaml.tmp
193+
194+
- name: user principal authentication - create namespace
195+
# continue-on-error: true
196+
run: kubectl create namespace app-user
193197

194-
- name: delete secret if exists
198+
- name: user principal authentication - delete secret if exists
195199
continue-on-error: true
196-
run: kubectl delete secret oci-config
200+
run: kubectl delete secret oci-config -n app-user
197201

198-
- name: create kubernetes secret for user auth config
202+
- name: user principal authentication - create kubernetes secret for user auth config
199203
run: |
200204
kubectl create secret generic oci-config \
201-
--from-file=config=e2e/example/user-auth-config-example.yaml.tmp \
202-
--from-literal=private-key="${{ env.OCI_CLI_KEY_CONTENT }}"
205+
--from-file=config=e2e/example/user-principal/user-auth-config-example.yaml.tmp \
206+
--from-literal=private-key="${{ env.OCI_CLI_KEY_CONTENT }}" -n app-user
203207
204-
- name: update spc file with correct values
208+
- name: user principal authentication - update spc file with correct values
205209
run: |
206210
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
207211
-e 's/authType:.*/authType: user/' \
208-
-e 's/- name:.*/- name: ${{ env.OCI_VAULT_SECRET_NAME }}/' e2e/example/secret-provider-class.yaml > e2e/example/secret-provider-class.yaml.tmp
212+
-e 's/- name:.*/- name: ${{ env.OCI_VAULT_SECRET_NAME }}/' e2e/example/user-principal/secret-provider-class.yaml > e2e/example/user-principal/secret-provider-class.yaml.tmp
209213
210-
- name: update deployment file with secret name
214+
- name: user principal authentication - update deployment file with secret name
211215
run: |
212216
sed -e 's/testingSecretName:.*/testingSecretName: ${{ env.OCI_VAULT_SECRET_NAME }}/' \
213-
e2e/example/app.deployment.yaml > e2e/example/app.deployment.yaml.tmp
217+
e2e/example/user-principal/app.deployment.yaml > e2e/example/user-principal/app.deployment.yaml.tmp
214218
215-
- name: print updated yaml file
216-
run: cat e2e/example/secret-provider-class.yaml.tmp
219+
- name: user principal authentication - print updated yaml file
220+
run: cat e2e/example/user-principal/secret-provider-class.yaml.tmp
217221

218-
- name: deploy spc
219-
run: kubectl apply -f e2e/example/secret-provider-class.yaml.tmp
222+
- name: user principal authentication - deploy spc
223+
run: kubectl apply -f e2e/example/user-principal/secret-provider-class.yaml.tmp -n app-user
220224

221-
- name: deploy workload
222-
run: kubectl apply -f e2e/example/app.deployment.yaml.tmp
225+
- name: user principal authentication - deploy workload
226+
run: kubectl apply -f e2e/example/user-principal/app.deployment.yaml.tmp -n app-user
223227

224-
- name: Wait for pod to run
225-
id: wait-on-pod
228+
- name: user principal authentication - Wait for pod to run
229+
id: wait-on-pod-user
226230
# run: kubectl wait --for=jsonpath='{.status.phase}'=Running pods/${{ env.POD_NAME }} --timeout=90s
227231
run: sleep 90
228232

229-
- name: Verify pods are running
230-
id: pod-names
231-
run: kubectl get pods -l testingSecretName=${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers
233+
- name: user principal authentication - Verify pods are running
234+
id: pod-names-user
235+
run: kubectl get pods -l testingSecretName=${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-user
232236

233-
- name: capture pod name into env
234-
run: echo "POD_NAME=`kubectl get pods -l testingSecretName=${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers`" >> $GITHUB_ENV
237+
- name: user principal authentication - capture pod name into env
238+
run: echo "POD_NAME=`kubectl get pods -l testingSecretName=${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-user`" >> $GITHUB_ENV
235239

236-
- name: print secret value
237-
id: print-secret-content
238-
run: echo "SECRET_CONTENT=`kubectl exec -it ${{ env.POD_NAME }} -- cat /mnt/secrets-store/${{ env.OCI_VAULT_SECRET_NAME }} 2> /dev/null | base64`" >> $GITHUB_ENV
240+
- name: user principal authentication - print secret value
241+
id: print-secret-content-user
242+
run: echo "SECRET_CONTENT=`kubectl exec -n app-user -it ${{ env.POD_NAME }} -- cat /mnt/secrets-store/${{ env.OCI_VAULT_SECRET_NAME }} 2> /dev/null | base64`" >> $GITHUB_ENV
239243

240244
# - name: convert to base64
241245
# id: convert-to-base64
242246
# run: echo -n ${{ steps.print-secret-content.outputs.output }} | base64
243247

244-
- name: print values
248+
- name: user principal authentication - print values
245249
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"
246250

247-
- name: verify value
251+
- name: user principal authentication - verify value
248252
run: if [ "${{ env.SECRET_CONTENT }}" == "${{ env.OCI_VAULT_SECRET_VALUE}}" ]; then exit 0; else exit 1; fi
249253

254+
#
255+
# End of user principal
256+
#
257+
- name: workload identity principal authentication - update spc file with correct values
258+
run: |
259+
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
260+
-e 's/authType:.*/authType: workload/' \
261+
-e 's/- name:.*/- name: ${{ env.OCI_VAULT_SECRET_NAME }}/' e2e/example/workload-identity/secret-provider-class.yaml > e2e/example/workload-identity/secret-provider-class.yaml.tmp
262+
263+
- name: workload identity principal authentication - update deployment file with secret name
264+
run: |
265+
sed -e 's/testingSecretName:.*/testingSecretName: workload-${{ env.OCI_VAULT_SECRET_NAME }}/' \
266+
e2e/example/workload-identity/app.deployment.yaml > e2e/example/workload-identity/app.deployment.yaml.tmp
267+
268+
- name: workload identity principal authentication - print updated yaml file
269+
run: cat e2e/example/workload-identity/secret-provider-class.yaml.tmp
270+
271+
- name: workload identity principal authentication - create namespace
272+
# continue-on-error: true
273+
run: kubectl create namespace app-workload
274+
275+
- name: workload identity principal authentication - deploy namespace and service account
276+
run: kubectl apply -f e2e/example/workload-identity/service-account.yaml -n app-workload
277+
278+
- name: workload identity principal authentication - deploy spc
279+
run: kubectl apply -f e2e/example/workload-identity/secret-provider-class.yaml.tmp -n app-workload
280+
281+
- name: workload identity principal authentication - deploy workload
282+
run: kubectl apply -f e2e/example/workload-identity/app.deployment.yaml.tmp -n app-workload
283+
284+
- name: workload identity principal authentication - Wait for pod to run
285+
id: wait-on-pod-workload
286+
# run: kubectl wait --for=jsonpath='{.status.phase}'=Running pods/${{ env.POD_NAME }} --timeout=90s
287+
run: sleep 90
288+
289+
- name: workload identity principal authentication - Verify pods are running
290+
id: pod-names-workload
291+
run: kubectl get pods -l testingSecretName=workload-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-workload
292+
293+
- name: workload identity principal authentication - capture pod name into env
294+
run: echo "POD_NAME=`kubectl get pods -l testingSecretName=workload-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-workload`" >> $GITHUB_ENV
295+
296+
- name: workload identity principal authentication - print secret value
297+
id: print-secret-content-workload
298+
run: echo "SECRET_CONTENT=`kubectl exec -n app-workload -it ${{ env.POD_NAME }} -- cat /mnt/secrets-store/${{ env.OCI_VAULT_SECRET_NAME }} 2> /dev/null | base64`" >> $GITHUB_ENV
299+
300+
- name: workload identity principal authentication - print values
301+
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"
302+
303+
- name: workload identity principal authentication - verify value
304+
run: if [ "${{ env.SECRET_CONTENT }}" == "${{ env.OCI_VAULT_SECRET_VALUE}}" ]; then exit 0; else exit 1; fi
305+
306+
#
307+
# End of workload identity principal
308+
#
309+
- name: instance principal authentication - update spc file with correct values
310+
run: |
311+
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
312+
-e 's/authType:.*/authType: instance/' \
313+
-e 's/- name:.*/- name: ${{ env.OCI_VAULT_SECRET_NAME }}/' e2e/example/instance-principal/secret-provider-class.yaml > e2e/example/instance-principal/secret-provider-class.yaml.tmp
314+
315+
- name: instance principal authentication - update deployment file with secret name
316+
run: |
317+
sed -e 's/testingSecretName:.*/testingSecretName: instance-${{ env.OCI_VAULT_SECRET_NAME }}/' \
318+
e2e/example/instance-principal/app.deployment.yaml > e2e/example/instance-principal/app.deployment.yaml.tmp
319+
320+
- name: instance principal authentication - print updated yaml file
321+
run: cat e2e/example/instance-principal/secret-provider-class.yaml.tmp
322+
323+
- name: instance principal authentication - create namespace
324+
# continue-on-error: true
325+
run: kubectl create namespace app-instance
326+
327+
- name: instance principal authentication - deploy spc
328+
run: kubectl apply -f e2e/example/instance-principal/secret-provider-class.yaml.tmp -n app-instance
329+
330+
- name: instance principal authentication - deploy workload
331+
run: kubectl apply -f e2e/example/instance-principal/app.deployment.yaml.tmp -n app-instance
332+
333+
- name: instance principal authentication - Wait for pod to run
334+
id: wait-on-pod-instance
335+
# run: kubectl wait --for=jsonpath='{.status.phase}'=Running pods/${{ env.POD_NAME }} --timeout=90s
336+
run: sleep 90
337+
338+
- name: instance principal authentication - Verify pods are running
339+
id: pod-names-instance
340+
run: kubectl get pods -l testingSecretName=instance-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-instance
341+
342+
- name: instance principal authentication - capture pod name into env
343+
run: echo "POD_NAME=`kubectl get pods -l testingSecretName=instance-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-instance`" >> $GITHUB_ENV
344+
345+
- name: instance principal authentication - print secret value
346+
id: print-secret-content-instance
347+
run: echo "SECRET_CONTENT=`kubectl exec -n app-instance -it ${{ env.POD_NAME }} -- cat /mnt/secrets-store/${{ env.OCI_VAULT_SECRET_NAME }} 2> /dev/null | base64`" >> $GITHUB_ENV
348+
349+
- name: instance principal authentication - print values
350+
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"
351+
352+
- name: instance principal authentication - verify value
353+
run: if [ "${{ env.SECRET_CONTENT }}" == "${{ env.OCI_VAULT_SECRET_VALUE}}" ]; then exit 0; else exit 1; fi
354+
250355
# cleanup
251-
- name: remove deployment
356+
- name: remove deployment - user principal authentication
357+
if: ${{ always() }}
358+
run: |
359+
kubectl delete -f e2e/example/user-principal/app.deployment.yaml.tmp \
360+
-f e2e/example/user-principal/secret-provider-class.yaml.tmp -n app-user
361+
362+
- name: remove deployment - workload identity authentication
252363
if: ${{ always() }}
253364
run: |
254-
kubectl delete -f e2e/example/app.deployment.yaml.tmp \
255-
-f e2e/example/secret-provider-class.yaml.tmp
365+
kubectl delete -f e2e/example/workload-identity/secret-provider-class.yaml.tmp \
366+
-f e2e/example/workload-identity/app.deployment.yaml.tmp \
367+
-f e2e/example/workload-identity/service-account.yaml -n app-workload
368+
369+
- name: remove deployment - instance principal authentication
370+
if: ${{ always() }}
371+
run: |
372+
kubectl delete -f e2e/example/instance-principal/secret-provider-class.yaml.tmp \
373+
-f e2e/example/instance-principal/app.deployment.yaml.tmp -n app-instance
256374
257375
- name: delete secret
258376
if: ${{ always() }}
259-
run: kubectl delete secret oci-config
377+
run: kubectl delete secret oci-config -n app-user
260378

261379
- name: uninstall provider
262380
if: ${{ always() }}
263381
run: helm uninstall oci-provider -n ${{ env.PROVIDER_NAMESPACE }}
264382

383+
- name: delete namespaces
384+
if: ${{ always() }}
385+
run: kubectl delete namespace app-user app-workload app-instance
386+
265387
cleanup:
266388
runs-on: ubuntu-latest
267389
needs: [deploy-provider]

GettingStarted.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The provider is a gRPC server accessible via the Unix domain socket. It's interf
1111
* [Authentication & Authorization](#authn-authz)
1212
* [User Principal](#auth-user-principal)
1313
* [Instance Princiapl](#auth-instance-principal)
14+
* [Workload Identity](#auth-workload-identity)
1415
* [Access Policies](#access-policies)
1516
* [Deployment](#deployment)
1617
* [Helm](#helm-deployment)
@@ -49,9 +50,10 @@ This section describes steps to deploy and test solution.
4950

5051
<a name="authn-authz"></a>
5152
### Authentication and Authorization
52-
Currently, two modes of authentication is supported. Some AuthN modes are applicable only for a particular variant of cluster.
53+
Currently, three modes of authentication is supported. Some AuthN modes are applicable only for a particular variant of cluster.
5354
* [User Principal](#auth-user-principal)
5455
* [Instance Principal](#auth-instance-principal)
56+
* [Workload Identity](#auth-workload-identity)
5557

5658
<a name="auth-user-principal"></a>
5759
### User Principal
@@ -73,6 +75,15 @@ kubectl create secret generic oci-config \
7375
### Instance Principal
7476
Instance principal would work only on OKE cluster.
7577
Access should be granted using Access Policies(See [Access Policies](#access-polices) section).
78+
79+
<a name="auth-workload-identity"></a>
80+
### Workload Identity
81+
Workload Identity works only in OKE Enhanced clusters.
82+
83+
Access should be granted using Access Policies(See [Access Policies for Workloads](#access-policies-workloads) section).
84+
85+
Workload Identity uses a Resource Principal auth, which requires settings a couple of ENV variables on the provider pod, including the region where the cluster is deployed. To achieve this, make sure to specify the `provider.oci.auth.types.workload.resourcePrincipalVersion=<version>` and `provider.oci.auth.types.workload.resourcePrincipalRegion=<region>` parameters in the `values.yaml` for the Helm chart deployment, or as inline parameters.
86+
7687
<a name="access-policies"></a>
7788
### Access Policies
7889
Access to the vault and secrets should be explicity granted using Policies in case of Instance principal authencation or other users(non owner of vault) or groups of tenancy in case of user principal authentication.
@@ -103,6 +114,13 @@ It involves two steps
103114

104115
More information on [Policy](https://docs.oracle.com/en-us/iaas/Content/Identity/Concepts/policysyntax.htm)
105116

117+
<a name="access-policies-workload"></a>
118+
### Access Policies for Workloads
119+
120+
With Workload Identity authentication, only a policy is required, which defines the kubernetes workload the policy works for:
121+
122+
`allow any-user to use secret-family in compartment <compartment-name> where ALL {request.principal.type='workload', request.principal.namespace ='<namespace>', request.principal.service_account = 'oci-secrets-store-csi-driver-provider-sa', request.principal.cluster_id = 'ocid1.cluster.oc1....'}`
123+
106124
<a name="deployment"></a>
107125
### Deployment
108126
Provider and Driver would be deployed as Daemonset. `kube-system` namespace is preferred, but not restricted.
@@ -132,7 +150,7 @@ Default values are provided in `charts/oci-secrets-store-csi-driver-provider/val
132150
kubectl apply -f deploy/provider.daemonset.yaml
133151
kubectl apply -f deploy/provider.serviceaccount.yaml
134152
135-
# if user authention principal is required
153+
# if user authentication principal is required
136154
kubectl apply -f deploy/provider.roles.yaml
137155
```
138156
<a name="provider-verification"></a>

charts/oci-secrets-store-csi-driver-provider/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
apiVersion: v2
88
name: oci-secrets-store-csi-driver-provider
99
description: OCI Vault provider for the Secrets Store CSI driver.
10-
version: 0.3.1
10+
version: 0.4.0
1111
type: application
1212

13-
appVersion: "0.9.5"
13+
appVersion: "0.10.0"
1414
kubeVersion: ">=1.19.0-0" # CSI Driver 1.2.0 is compatible with K8S 1.19+
1515

1616
dependencies:

charts/oci-secrets-store-csi-driver-provider/templates/provider.daemonset.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ spec:
4242
name: health-port
4343
- containerPort: {{ .Values.provider.metricsPort }}
4444
name: metrics-port
45+
{{ if .Values.provider.oci.auth.types.workload.enabled }}
46+
env:
47+
- name: OCI_RESOURCE_PRINCIPAL_VERSION
48+
value: {{ .Values.provider.oci.auth.types.workload.resourcePrincipalVersion | quote }}
49+
- name: OCI_RESOURCE_PRINCIPAL_REGION
50+
value: {{ .Values.provider.oci.auth.types.workload.resourcePrincipalRegion }}
51+
{{ end }}
4552
resources:
4653
{{- toYaml .Values.provider.resources | nindent 12 }}
4754
# Container should run as root to mount the hostPath volume and create Unix Domain Socket in that volume.

charts/oci-secrets-store-csi-driver-provider/templates/provider.roles.yaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,29 @@ subjects:
2727
- kind: ServiceAccount
2828
name: {{ .Chart.Name }}-sa
2929
namespace: {{ .Release.Namespace }}
30-
{{ end }}
30+
{{ end }}
31+
32+
{{ if .Values.provider.oci.auth.types.workload.enabled }}
33+
---
34+
apiVersion: rbac.authorization.k8s.io/v1
35+
kind: ClusterRole
36+
metadata:
37+
name: {{ .Chart.Name }}-workload-identity-cluster-role
38+
rules:
39+
- apiGroups: [""]
40+
resources: ["serviceaccounts/token"]
41+
verbs: ["create"]
42+
---
43+
apiVersion: rbac.authorization.k8s.io/v1
44+
kind: ClusterRoleBinding
45+
metadata:
46+
name: {{ .Chart.Name }}-workload-identity-cluster-rolebinding
47+
roleRef:
48+
apiGroup: rbac.authorization.k8s.io
49+
kind: ClusterRole
50+
name: {{ .Chart.Name }}-workload-identity-cluster-role
51+
subjects:
52+
- kind: ServiceAccount
53+
name: {{ .Chart.Name }}-sa
54+
namespace: {{ .Release.Namespace }}
55+
{{ end }}

0 commit comments

Comments
 (0)