Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.DS_Store
license.json
istio-*
*.key
*.pem
build
spiffe-helper*
23 changes: 23 additions & 0 deletions 1-kubernetes-service-account-tokens/1-create-cluster.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

#########################
# Create the KIND cluster
#########################

cd "$(dirname "${BASH_SOURCE[0]}")"

#
# Create the cluster
#
../resources/cluster/create.sh
if [ $? -ne 0 ]; then
exit 1
fi

#
# Enable internal OIDC discovery to get the signing keys for Kubernetes service account tokens
#
kubectl apply -f cluster/oidc-discovery.yaml
if [ $? -ne 0 ]; then
exit 1
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash

###################################
# Deploy the Curity Identity Server
###################################

cd "$(dirname "${BASH_SOURCE[0]}")"

#
# Check for a license file before allowing deployment
#
if [ "$LICENSE_FILE_PATH" == '' ]; then
echo 'Please provide a LICENSE_FILE_PATH environment variable with the path to a Curity Identity Server license file.'
exit 1
fi

LICENSE_KEY=$(cat "$LICENSE_FILE_PATH" | jq -r .License)
if [ "$LICENSE_KEY" == '' ]; then
echo 'An invalid license file was provided for the Curity Identity Server'
exit 1
fi

#
# Prevent accidental check-ins of license files
#
cp ../hooks/pre-commit ../.git/hooks

#
# Build the Docker image for this deployment
#
./idsvr/build.sh
if [ $? -ne 0 ]; then
exit 1
fi

#
# Create the namespace and service accounts
#
kubectl create namespace curity 2>/dev/null
kubectl -n curity apply -f ../resources/idsvr/service-accounts.yaml

#
# Configure the Kubernetes root CA as a trust store
# This enables the Curity Identity Server to call the Kubernetes JWKS URI without trust errors
#
export WORKLOAD_ROOT_CA="$(kubectl get configmap/kube-root-ca.crt -o jsonpath='{.data.ca\.crt}' | openssl base64 | tr -d '\n')"
if [ "$WORKLOAD_ROOT_CA" == '' ]; then
echo 'Unable to get the Kubernetes root CA'
exit 1
fi

#
# Deploy runtime secrets
#
kubectl -n curity delete secret idsvr-secrets 2>/dev/null
kubectl -n curity create secret generic idsvr-secrets \
--from-literal="ADMIN_PASSWORD=Password1" \
--from-literal="LICENSE_KEY=$LICENSE_KEY" \
--from-literal="WORKLOAD_ROOT_CA=$WORKLOAD_ROOT_CA"

#
# Run the Helm Chart to deploy the system
#
helm repo add curity https://curityio.github.io/idsvr-helm
helm repo update
helm upgrade --install curity curity/idsvr \
--namespace curity \
--values=idsvr/values.yaml
if [ $? -ne 0 ]; then
echo 'Problem encountered running the Helm Chart for the Curity Identity Server'
exit 1
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

#####################################
# Deploy API client and API workloads
#####################################

cd "$(dirname "${BASH_SOURCE[0]}")"

#
# Create the namespace for applications
#
kubectl create namespace applications 2>/dev/null

#
# Deploy the client
#
./client/deploy.sh
9 changes: 9 additions & 0 deletions 1-kubernetes-service-account-tokens/4-delete-cluster.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

#########################
# Delete the KIND cluster
#########################

cd "$(dirname "${BASH_SOURCE[0]}")"

../resources/cluster/delete.sh
116 changes: 116 additions & 0 deletions 1-kubernetes-service-account-tokens/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Strong OAuth Client Credentials in Kubernetes

A deployment that uses Kubernetes built-in strong credential capabilities.\
A workload uses Kubernetes service account tokens to get OAuth access tokens.

## Deploy the System

First, create a local cluster:

```bash
./1-create-cluster.sh
```

Then, deploy the Curity Identity Server, supplying the path to a license file:

```bash
export LICENSE_FILE_PATH=license.json
./2-deploy-curity-identity-server.sh
```

Then, deploy application workloads:

```bash
./3-deploy-application-workloads.sh
```

## Use the Admin UI

To view security settings in the Curity Identity Server, use port-forwarding to expose the Admin UI:

```bash
POD=$(kubectl -n curity get pods --selector='role=curity-idsvr-admin' -o=name)
kubectl -n curity port-forward "$POD" 6749:6749
```

Then browse to `http://localhost:6749/admin` and sign in as user `admin` with password `Password1`.

## Test Workload Identities

Remote to the workload client's pod:

```bash
POD=$(kubectl -n applications get pods --selector='app=workload-client' -o=name)
kubectl -n applications exec -it "$POD" -- bash
```

Then run a flow to get an access token using a Kubernetes service account token:

```bash
./authenticate-and-get-access-token.sh
```

The script outputs the header and payload of the service account token used for authentication:

```json
{
"alg": "RS256",
"kid": "amfKVpyk2xBM9LZ3cIshwsmgNTvbpJS4l_Z_SxCur_8"
}
{
"aud": [
"https://login.curitydemo.example/oauth/v2/oauth-token"
],
"exp": 1763380172,
"iat": 1763376572,
"iss": "https://kubernetes.default.svc.cluster.local",
"jti": "cf0845fa-e075-41af-9b0b-2a3b94fb331c",
"kubernetes.io": {
"namespace": "applications",
"node": {
"name": "curitydemo-worker",
"uid": "b13fc143-6a3b-44b7-bb38-58d3e27e665a"
},
"pod": {
"name": "workload-client-86945585f4-4vp5q",
"uid": "b887b913-d942-4e81-ad63-8baf1d829c7e"
},
"serviceaccount": {
"name": "workload-client",
"uid": "513d3d79-a34a-4ae6-819d-d8ea58c51c09"
}
},
"nbf": 1763376572,
"sub": "system:serviceaccount:applications:workload-client"
}
```

The script then outputs the header and payload of the access token that the workload uses to call APIs:

```json
{
"kid": "-1327236000",
"x5t": "7vcSVKpOYe3ckTlcYLLm5Y_Vdpg",
"alg": "ES256"
}
{
"jti": "284fbf2f-0875-4db8-8bf3-48ecfce78847",
"delegationId": "790369fb-1e5d-4261-95bf-3415890d46b3",
"exp": 1763377508,
"nbf": 1763376608,
"scope": "reports",
"iss": "https://login.curitydemo.example/oauth/v2/oauth-anonymous",
"sub": "jwt_assertion_client",
"aud": "api.curitydemo.example",
"iat": 1763376608,
"purpose": "access_token"
}
```

## Free Resources

Tear down the cluster when you have finished testing:

```bash
./4-delete-cluster.sh
```
10 changes: 10 additions & 0 deletions 1-kubernetes-service-account-tokens/client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# A utility container that can make curl requests to use workload identities and access tokens
#
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y curl jq

WORKDIR /work
COPY scripts/*.sh /work/

CMD ["/bin/sleep", "infinity"]
37 changes: 37 additions & 0 deletions 1-kubernetes-service-account-tokens/client/client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#
# A test workload that requests a Kubernetes projected service account token
#
apiVersion: v1
kind: ServiceAccount
metadata:
name: workload-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: workload-client
spec:
replicas: 1
selector:
matchLabels:
app: workload-client
template:
metadata:
labels:
app: workload-client
spec:
serviceAccountName: workload-client
containers:
- name: workload-client
image: client:1.0
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: jwt-workload-credential
volumes:
- name: jwt-workload-credential
projected:
sources:
- serviceAccountToken:
path: assertion
expirationSeconds: 3600
audience: 'https://login.curitydemo.example/oauth/v2/oauth-token'
26 changes: 26 additions & 0 deletions 1-kubernetes-service-account-tokens/client/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

####################################
# Deploy the example client workload
####################################

cd "$(dirname "${BASH_SOURCE[0]}")"

#
# Build the client workload and load it into the KIND registry
#
docker build --no-cache -t client:1.0 .
if [ $? -ne 0 ]; then
exit 1
fi

kind load docker-image client:1.0 --name curitydemo
if [ $? -ne 0 ]; then
exit 1
fi

#
# Deploy the client workload
#
kubectl -n applications delete -f client.yaml 2>/dev/null
kubectl -n applications apply -f client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

cd "$(dirname "${BASH_SOURCE[0]}")"

##################################################################################
# A script to test getting an access token using a Kubenetes service account token
##################################################################################

function jwt_header() {
local _INPUT="$1"
local _OUTPUT=$(echo "$_INPUT" | jq -R 'split(".") | .[0] | gsub("-"; "+") | gsub("_"; "/") | gsub("%3D"; "=") | @base64d | fromjson')
echo $_OUTPUT
}

function jwt_payload() {
local _INPUT="$1"
local _OUTPUT=$(echo "$_INPUT" | jq -R 'split(".") | .[1] | gsub("-"; "+") | gsub("_"; "/") | gsub("%3D"; "=") | @base64d | fromjson')
echo $_OUTPUT
}

echo 'Using JWT workload credential to authenticate and get an access token ...'
JWT_ASSERTION="$(cat /var/run/secrets/kubernetes.io/serviceaccount/assertion)"
jwt_header "$JWT_ASSERTION" | jq
jwt_payload "$JWT_ASSERTION" | jq

HTTP_STATUS=$(curl -s -X POST http://curity-idsvr-runtime-svc.curity:8443/oauth/v2/oauth-token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d "client_assertion=$JWT_ASSERTION" \
-d 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
-d 'scope=reports' \
-o response.txt \
-w '%{http_code}')
if [ "$HTTP_STATUS" != '200' ]; then
echo "Problem encountered getting an access token, status: $HTTP_STATUS"
cat response.txt
exit 1
fi

echo 'Received JWT access token ...'
ACCESS_TOKEN=$(cat response.txt | jq -r .access_token)
jwt_header "$ACCESS_TOKEN" | jq
jwt_payload "$ACCESS_TOKEN" | jq
14 changes: 14 additions & 0 deletions 1-kubernetes-service-account-tokens/cluster/oidc-discovery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#
# Enable calls to the Kubernetes JWKS URI to get public keys to validate service account tokens
#
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: k8s-jwks-uri
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:service-account-issuer-discovery
subjects:
- kind: Group
name: system:unauthenticated
11 changes: 11 additions & 0 deletions 1-kubernetes-service-account-tokens/idsvr/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM curity.azurecr.io/curity/idsvr:latest

#
# Copy in XML configuration for this deployment
#
COPY build/*.xml /opt/idsvr/etc/init/

#
# Copy in JavaScript procedures
#
COPY build/client_credentials_token_issuer.js /opt/idsvr/etc/init/token-procedures/oauth-token-client-credentials/
Loading