Skip to content
This repository has been archived by the owner on Mar 22, 2024. It is now read-only.

Commit

Permalink
Federation test (#423)
Browse files Browse the repository at this point in the history
Co-authored-by: kfox1111 <Kevin.Fox@pnnl.gov>
Co-authored-by: Marco Franssen <marco.franssen@gmail.com>
  • Loading branch information
3 people committed Aug 22, 2023
1 parent 8e9d42a commit cbe0001
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env bash

set -x

# https://gist.github.com/stokito/f2d7ea0b300f14638a9063559384ec89/
# Decode a JWT from stdin and verify it's signature with the JWT issuer public key
# Only RS256 keys are supported for signature check
#
# HOW TO USE:
# $ export JWTTOKEN="eyF...<your token here>...g"
# $ ./jwt-decode.sh https://example.com/keys "${JWTTOKEN}"
# if signature check failed then error code will be non-zero

URL=$1

JWT=$2

if [ -z "$(command -v jq)" ]; then
echo "This script will NOT work on your machine."
echo "Please install jq first: https://stedolan.github.io/jq/download/"
exit 1
fi

base64_padding() {
local len=$(( ${#1} % 4 ))
local padded_b64=''
if [ ${len} = 2 ]; then
padded_b64="${1}=="
elif [ ${len} = 3 ]; then
padded_b64="${1}="
else
padded_b64="${1}"
fi
echo -n "$padded_b64"
}

base64url_to_b64() {
base64_padding "${1}" | tr -- '-_' '+/'
}

b2hex() { echo -n "$1"==== | fold -w 4 | sed '$ d' | tr -d '\n' |base64 -d | xxd -p | tr -d \\n; }

mint_rsa_key() {
JWK=$1

# Extract the modulus and exponent from the JWK, converting from URL-safe Base64 to standard Base64
MODULUS=$(echo "$JWK" | jq -r '.n' | tr '_-' '/+')
EXPONENT=$(echo "$JWK" | jq -r '.e' | tr '_-' '/+')

modulus=$(b2hex "$MODULUS")
exponent=$(b2hex "$EXPONENT")

asnconf=$(mktemp)

asnconf="asn1=SEQUENCE:pubkeyinfo\n[pubkeyinfo]\nalgorithm=SEQUENCE:rsa_alg\npubkey=BITWRAP,SEQUENCE:rsapubkey\n[rsa_alg]\nalgorithm=OID:rsaEncryption\nparameter=NULL\n[rsapubkey]\nn=INTEGER:0x$modulus\ne=INTEGER:0x$exponent"

derfile=$(mktemp)
echo >&2 "derfile: $derfile"
echo -e "$asnconf" | openssl asn1parse -genconf /dev/stdin -noout -out "$derfile"

openssl rsa -in "$derfile" -inform DER -pubin
}

# read the JWT from stdin and split by comma into three variables
IFS='.' read -r JWT_HEADER_B64URL JWT_PAYLOAD_B64URL JWT_SIGNATURE_B64URL <<< "${JWT}"

JWT_HEADER_B64=$(base64url_to_b64 "${JWT_HEADER_B64URL}")
JWT_PAYLOAD_B64=$(base64url_to_b64 "${JWT_PAYLOAD_B64URL}")
JWT_SIGNATURE_B64=$(base64url_to_b64 "${JWT_SIGNATURE_B64URL}")

JWT_HEADER=$(echo "${JWT_HEADER_B64}" | base64 -d)
JWT_PAYLOAD=$(echo "${JWT_PAYLOAD_B64}" | base64 -d)

echo "JWT Header:"
echo "${JWT_HEADER}" | jq
echo "JWT Payload:"
echo "${JWT_PAYLOAD}" | jq
echo "JWT Signature (Base 64 padded):"
echo "${JWT_SIGNATURE_B64}"

JWT_ALG=$(echo "$JWT_HEADER" | jq -r .alg)
JWT_KID=$(echo "$JWT_HEADER" | jq -r .kid)
#JWT_TYP=$(echo "$JWT_HEADER" | jq -r .typ)
#JWT_ISS=$(echo "$JWT_PAYLOAD" | jq -r .iss)
JWT_SUB=$(echo "$JWT_PAYLOAD" | jq -r .sub)
JWT_EMAIL=$(echo "$JWT_PAYLOAD" | jq -r .email)
JWT_IAT=$(echo "$JWT_PAYLOAD" | jq -r .iat)
echo "alg: $JWT_ALG kid: $JWT_KID"
echo "sub: $JWT_SUB email: $JWT_EMAIL iat: $JWT_IAT"

echo "URL: ${URL}"
JWK_SET=$(curl -k -s "${URL}")
echo >&2 "JWK_SET: $JWK_SET"
JWK=$(echo "$JWK_SET" | jq -c -r --arg KID "$JWT_KID" '.keys[] | select(.kid==$KID)')
echo >&2 "JWK: $JWK"

PUB_KEY_FILE=$(mktemp)
mint_rsa_key "$JWK" > "$PUB_KEY_FILE"

# verify signature
if [ "${JWT_ALG}" = "RS256" ]; then
#SIG_FILE="/tmp/$JWT_SUB-$JWT_IAT.sig.dat"
SIG_FILE=$(mktemp)
echo -n "$JWT_SIGNATURE_B64" | base64 -d > "${SIG_FILE}"
JWT_BODY=$(echo -n "$JWT_HEADER_B64URL.$JWT_PAYLOAD_B64URL")
echo -n "$JWT_BODY" | openssl dgst -sha256 -verify "${PUB_KEY_FILE}" -signature "${SIG_FILE}"
JWT_SIG_VERIFIED=$?
rm "${SIG_FILE}"
if [ ${JWT_SIG_VERIFIED} -ne 0 ]; then
>&2 echo "Bad Signature"
exit ${JWT_SIG_VERIFIED};
fi
else
>&2 echo "Error 3: Unsupported signature algorithm $JWT_ALG"
exit 3
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "spiffe-oidc-discovery-provider.fullname" . }}-test-keys"
namespace: {{ include "spiffe-oidc-discovery-provider.namespace" . }}
labels:
{{- include "spiffe-oidc-discovery-provider.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 4 }}
{{- end }}
serviceAccountName: {{ include "spiffe-oidc-discovery-provider.serviceAccountName" . }}
initContainers:
- name: static-busybox
image: {{ template "spire-lib.image" (dict "image" .Values.tests.busybox.image "global" .Values.global) }}
command:
- sh
- -c
- |
cp /bin/busybox /data/busybox
chmod +x /data/busybox
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
volumeMounts:
- name: data-volume
mountPath: /data
- name: gettoken
image: {{ template "spire-lib.image" (dict "appVersion" $.Chart.AppVersion "image" .Values.tools.kubectl.image "global" .Values.global "KubeVersion" .Capabilities.KubeVersion.Version "image" .Values.tests.agent.image) }}
command:
- /data/busybox
- sh
- -c
- |
while true; do
/opt/spire/bin/spire-agent api fetch jwt -audience foo -format json -socketPath /spire-agent/spire-agent.sock -timeout 5s > /data/token.svid
[ $? -eq 0 ] && break
sleep 1
done
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
volumeMounts:
- name: data-volume
mountPath: /data
- name: spire-api
mountPath: /spire-agent
readOnly: true
containers:
- name: verify-keys
image: {{ template "spire-lib.image" (dict "image" .Values.tests.toolkit.image "global" .Values.global) }}
command:
- bash
env:
- name: TMPDIR
value: /data
args:
- -c
- |
URL=http://{{ include "spiffe-oidc-discovery-provider.fullname" . }}.{{ include "spiffe-oidc-discovery-provider.namespace" . }}.svc.{{ include "spire-lib.cluster-domain" . }}:{{ .Values.service.port }}
curl -k -s -f "${URL}"/keys
JWT=$(cat /data/token.svid | jq -r '.[].svids[0].svid' | xargs)
cat <<'EOF' >> /data/jwt-decode.sh
{{- (.Files.Get "files/test/jwt-decode.sh") | nindent 10 }}
EOF
bash /data/jwt-decode.sh "${URL}"/keys "${JWT}"
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
volumeMounts:
- mountPath: /data
name: data-volume
restartPolicy: Never
volumes:
- csi:
driver: csi.spiffe.io
readOnly: true
name: spire-api
- name: data-volume
emptyDir: {}
---
38 changes: 38 additions & 0 deletions charts/spire/charts/spiffe-oidc-discovery-provider/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,44 @@ tests:
# -- Overrides the image tag
tag: latest@sha256:96ab1600d945b4a99c8610b5c8b31e346da63dc20573a26bb0777dd0190db5d4

toolkit:
image:
# -- The OCI registry to pull the tests image from
registry: cgr.dev
# -- The repository within the registry
repository: chainguard/slim-toolkit-debug
# -- The tests image pull policy
pullPolicy: IfNotPresent
# -- This value is deprecated in favor of tag. (Will be removed in a future release)
version: ""
# -- Overrides the image tag
tag: latest@sha256:d717d0a2c88518f8e36d9cfe1571639a40617e8c4291e34876d46bdeefb1ab5a

busybox:
image:
# -- The OCI registry to pull the image from
registry: ""
# -- The repository within the registry
repository: busybox
# -- The image pull policy
pullPolicy: IfNotPresent
# -- This value is deprecated in favor of tag. (Will be removed in a future release)
version: ""
# -- Overrides the image tag
tag: uclibc@sha256:3e516f71d8801b0ce6c3f8f8e4f11093ec04e168177a90f1da4498014ee06b6b
agent:
image:
# -- The OCI registry to pull the image from
registry: ghcr.io
# -- The repository within the registry
repository: spiffe/spire-agent
# -- The image pull policy
pullPolicy: IfNotPresent
# -- This value is deprecated in favor of tag. (Will be removed in a future release)
version: ""
# -- Overrides the image tag
tag: ""

tools:
kubectl:
image:
Expand Down

0 comments on commit cbe0001

Please sign in to comment.