Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workload Identity Implementation #34

Merged
merged 3 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 156 additions & 34 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ jobs:

deploy-provider:
runs-on: ubuntu-latest
name: Deploy Provider
name: Deploy Provider and Run Tests
needs: [ setup-vault , setup-cluster , build ]
env:
OCI_VAULT_ID: ${{ needs.setup-vault.outputs.OCI_VAULT_ID }}
Expand Down Expand Up @@ -181,87 +181,209 @@ jobs:
kubectl get daemonset --namespace oci-provider \
--selector='app.kubernetes.io/name in (oci-secrets-store-csi-driver-provider, secrets-store-csi-driver)'

- name: update auth file with correct values
- name: user principal authentication - update auth file with correct values
run: |
sed -e 's/region:.*/region: ${{ env.OCI_CLI_REGION }}/' \
-e 's/tenancy:.*/tenancy: ${{ env.OCI_CLI_TENANCY }}/' \
-e 's/user:.*/user: ${{ env.OCI_CLI_USER }}/' \
-e 's/fingerprint:.*/fingerprint: ${{ env.OCI_CLI_FINGERPRINT }}/' e2e/example/user-auth-config-example.yaml > e2e/example/user-auth-config-example.yaml.tmp
-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

# - name: print updated yaml file
# run: cat e2e/example/user-auth-config-example.yaml.tmp

- name: user principal authentication - create namespace
# continue-on-error: true
run: kubectl create namespace app-user

- name: delete secret if exists
- name: user principal authentication - delete secret if exists
continue-on-error: true
run: kubectl delete secret oci-config
run: kubectl delete secret oci-config -n app-user

- name: create kubernetes secret for user auth config
- name: user principal authentication - create kubernetes secret for user auth config
run: |
kubectl create secret generic oci-config \
--from-file=config=e2e/example/user-auth-config-example.yaml.tmp \
--from-literal=private-key="${{ env.OCI_CLI_KEY_CONTENT }}"
--from-file=config=e2e/example/user-principal/user-auth-config-example.yaml.tmp \
--from-literal=private-key="${{ env.OCI_CLI_KEY_CONTENT }}" -n app-user

- name: update spc file with correct values
- name: user principal authentication - update spc file with correct values
run: |
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
-e 's/authType:.*/authType: user/' \
-e 's/- name:.*/- name: ${{ env.OCI_VAULT_SECRET_NAME }}/' e2e/example/secret-provider-class.yaml > e2e/example/secret-provider-class.yaml.tmp
-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

- name: update deployment file with secret name
- name: user principal authentication - update deployment file with secret name
run: |
sed -e 's/testingSecretName:.*/testingSecretName: ${{ env.OCI_VAULT_SECRET_NAME }}/' \
e2e/example/app.deployment.yaml > e2e/example/app.deployment.yaml.tmp
e2e/example/user-principal/app.deployment.yaml > e2e/example/user-principal/app.deployment.yaml.tmp

- name: print updated yaml file
run: cat e2e/example/secret-provider-class.yaml.tmp
- name: user principal authentication - print updated yaml file
run: cat e2e/example/user-principal/secret-provider-class.yaml.tmp

- name: deploy spc
run: kubectl apply -f e2e/example/secret-provider-class.yaml.tmp
- name: user principal authentication - deploy spc
run: kubectl apply -f e2e/example/user-principal/secret-provider-class.yaml.tmp -n app-user

- name: deploy workload
run: kubectl apply -f e2e/example/app.deployment.yaml.tmp
- name: user principal authentication - deploy workload
run: kubectl apply -f e2e/example/user-principal/app.deployment.yaml.tmp -n app-user

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

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

- name: capture pod name into env
run: echo "POD_NAME=`kubectl get pods -l testingSecretName=${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers`" >> $GITHUB_ENV
- name: user principal authentication - capture pod name into env
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

- name: print secret value
id: print-secret-content
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
- name: user principal authentication - print secret value
id: print-secret-content-user
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

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

- name: print values
- name: user principal authentication - print values
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"

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

#
# End of user principal
#
- name: workload identity principal authentication - update spc file with correct values
run: |
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
-e 's/authType:.*/authType: workload/' \
-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

- name: workload identity principal authentication - update deployment file with secret name
run: |
sed -e 's/testingSecretName:.*/testingSecretName: workload-${{ env.OCI_VAULT_SECRET_NAME }}/' \
e2e/example/workload-identity/app.deployment.yaml > e2e/example/workload-identity/app.deployment.yaml.tmp

- name: workload identity principal authentication - print updated yaml file
run: cat e2e/example/workload-identity/secret-provider-class.yaml.tmp

- name: workload identity principal authentication - create namespace
# continue-on-error: true
run: kubectl create namespace app-workload

- name: workload identity principal authentication - deploy namespace and service account
run: kubectl apply -f e2e/example/workload-identity/service-account.yaml -n app-workload

- name: workload identity principal authentication - deploy spc
run: kubectl apply -f e2e/example/workload-identity/secret-provider-class.yaml.tmp -n app-workload

- name: workload identity principal authentication - deploy workload
run: kubectl apply -f e2e/example/workload-identity/app.deployment.yaml.tmp -n app-workload

- name: workload identity principal authentication - Wait for pod to run
id: wait-on-pod-workload
# run: kubectl wait --for=jsonpath='{.status.phase}'=Running pods/${{ env.POD_NAME }} --timeout=90s
run: sleep 90

- name: workload identity principal authentication - Verify pods are running
id: pod-names-workload
run: kubectl get pods -l testingSecretName=workload-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-workload

- name: workload identity principal authentication - capture pod name into env
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

- name: workload identity principal authentication - print secret value
id: print-secret-content-workload
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

- name: workload identity principal authentication - print values
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"

- name: workload identity principal authentication - verify value
run: if [ "${{ env.SECRET_CONTENT }}" == "${{ env.OCI_VAULT_SECRET_VALUE}}" ]; then exit 0; else exit 1; fi

#
# End of workload identity principal
#
- name: instance principal authentication - update spc file with correct values
run: |
sed -e 's/vaultId:.*/vaultId: ${{ env.OCI_VAULT_ID }}/' \
-e 's/authType:.*/authType: instance/' \
-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

- name: instance principal authentication - update deployment file with secret name
run: |
sed -e 's/testingSecretName:.*/testingSecretName: instance-${{ env.OCI_VAULT_SECRET_NAME }}/' \
e2e/example/instance-principal/app.deployment.yaml > e2e/example/instance-principal/app.deployment.yaml.tmp

- name: instance principal authentication - print updated yaml file
run: cat e2e/example/instance-principal/secret-provider-class.yaml.tmp

- name: instance principal authentication - create namespace
# continue-on-error: true
run: kubectl create namespace app-instance

- name: instance principal authentication - deploy spc
run: kubectl apply -f e2e/example/instance-principal/secret-provider-class.yaml.tmp -n app-instance

- name: instance principal authentication - deploy workload
run: kubectl apply -f e2e/example/instance-principal/app.deployment.yaml.tmp -n app-instance

- name: instance principal authentication - Wait for pod to run
id: wait-on-pod-instance
# run: kubectl wait --for=jsonpath='{.status.phase}'=Running pods/${{ env.POD_NAME }} --timeout=90s
run: sleep 90

- name: instance principal authentication - Verify pods are running
id: pod-names-instance
run: kubectl get pods -l testingSecretName=instance-${{ env.OCI_VAULT_SECRET_NAME }} -o='custom-columns=PodName:.metadata.name' --no-headers -n app-instance

- name: instance principal authentication - capture pod name into env
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

- name: instance principal authentication - print secret value
id: print-secret-content-instance
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

- name: instance principal authentication - print values
run: echo "${{ env.SECRET_CONTENT }} == ${{ env.OCI_VAULT_SECRET_VALUE}}"

- name: instance principal authentication - verify value
run: if [ "${{ env.SECRET_CONTENT }}" == "${{ env.OCI_VAULT_SECRET_VALUE}}" ]; then exit 0; else exit 1; fi

# cleanup
- name: remove deployment
- name: remove deployment - user principal authentication
if: ${{ always() }}
run: |
kubectl delete -f e2e/example/user-principal/app.deployment.yaml.tmp \
-f e2e/example/user-principal/secret-provider-class.yaml.tmp -n app-user

- name: remove deployment - workload identity authentication
if: ${{ always() }}
run: |
kubectl delete -f e2e/example/app.deployment.yaml.tmp \
-f e2e/example/secret-provider-class.yaml.tmp
kubectl delete -f e2e/example/workload-identity/secret-provider-class.yaml.tmp \
-f e2e/example/workload-identity/app.deployment.yaml.tmp \
-f e2e/example/workload-identity/service-account.yaml -n app-workload

- name: remove deployment - instance principal authentication
if: ${{ always() }}
run: |
kubectl delete -f e2e/example/instance-principal/secret-provider-class.yaml.tmp \
-f e2e/example/instance-principal/app.deployment.yaml.tmp -n app-instance

- name: delete secret
if: ${{ always() }}
run: kubectl delete secret oci-config
run: kubectl delete secret oci-config -n app-user

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

- name: delete namespaces
if: ${{ always() }}
run: kubectl delete namespace app-user app-workload app-instance

cleanup:
runs-on: ubuntu-latest
needs: [deploy-provider]
Expand Down
22 changes: 20 additions & 2 deletions GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The provider is a gRPC server accessible via the Unix domain socket. It's interf
* [Authentication & Authorization](#authn-authz)
* [User Principal](#auth-user-principal)
* [Instance Princiapl](#auth-instance-principal)
* [Workload Identity](#auth-workload-identity)
* [Access Policies](#access-policies)
* [Deployment](#deployment)
* [Helm](#helm-deployment)
Expand Down Expand Up @@ -49,9 +50,10 @@ This section describes steps to deploy and test solution.

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

<a name="auth-user-principal"></a>
### User Principal
Expand All @@ -73,6 +75,15 @@ kubectl create secret generic oci-config \
### Instance Principal
Instance principal would work only on OKE cluster.
Access should be granted using Access Policies(See [Access Policies](#access-polices) section).

<a name="auth-workload-identity"></a>
### Workload Identity
Workload Identity works only in OKE Enhanced clusters.

Access should be granted using Access Policies(See [Access Policies for Workloads](#access-policies-workloads) section).

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.

<a name="access-policies"></a>
### Access Policies
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.
Expand Down Expand Up @@ -103,6 +114,13 @@ It involves two steps

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

<a name="access-policies-workload"></a>
### Access Policies for Workloads

With Workload Identity authentication, only a policy is required, which defines the kubernetes workload the policy works for:

`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....'}`

<a name="deployment"></a>
### Deployment
Provider and Driver would be deployed as Daemonset. `kube-system` namespace is preferred, but not restricted.
Expand Down Expand Up @@ -132,7 +150,7 @@ Default values are provided in `charts/oci-secrets-store-csi-driver-provider/val
kubectl apply -f deploy/provider.daemonset.yaml
kubectl apply -f deploy/provider.serviceaccount.yaml

# if user authention principal is required
# if user authentication principal is required
kubectl apply -f deploy/provider.roles.yaml
```
<a name="provider-verification"></a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ spec:
name: health-port
- containerPort: {{ .Values.provider.metricsPort }}
name: metrics-port
{{ if .Values.provider.oci.auth.types.workload.enabled }}
env:
- name: OCI_RESOURCE_PRINCIPAL_VERSION
value: {{ .Values.provider.oci.auth.types.workload.resourcePrincipalVersion | quote }}
- name: OCI_RESOURCE_PRINCIPAL_REGION
value: {{ .Values.provider.oci.auth.types.workload.resourcePrincipalRegion }}
{{ end }}
resources:
{{- toYaml .Values.provider.resources | nindent 12 }}
# Container should run as root to mount the hostPath volume and create Unix Domain Socket in that volume.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,29 @@ subjects:
- kind: ServiceAccount
name: {{ .Chart.Name }}-sa
namespace: {{ .Release.Namespace }}
{{ end }}
{{ end }}

{{ if .Values.provider.oci.auth.types.workload.enabled }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ .Chart.Name }}-workload-identity-cluster-role
rules:
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ .Chart.Name }}-workload-identity-cluster-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Chart.Name }}-workload-identity-cluster-role
subjects:
- kind: ServiceAccount
name: {{ .Chart.Name }}-sa
namespace: {{ .Release.Namespace }}
{{ end }}
18 changes: 18 additions & 0 deletions charts/oci-secrets-store-csi-driver-provider/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@
}
}
},
"workload": {
"description": "Settings for OCI Workload authentication",
"type": "object",
"properties": {
"enabled": {
"description": "Settings for OCI Workload authentication",
"type": "boolean"
},
"resourcePrincipalVersion": {
"description": "Settings for OCI Workload authentication",
"type": "string"
},
"resourcePrincipalRegion": {
"description": "Settings for OCI Workload authentication",
"type": "string"
}
}
},
"additionalProperties": false
}
},
Expand Down
Loading
Loading