Federation test (#423)

Co-authored-by: kfox1111 <[email protected]>
Co-authored-by: Marco Franssen <[email protected]>
This commit is contained in:
Drew Wells
2023-08-22 20:07:54 +02:00
committed by GitHub
co-authored by kfox1111 Marco Franssen
parent 8e9d42acf7
commit cbe0001131
3 changed files with 241 additions and 0 deletions
@@ -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
@@ -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: {}
---
@@ -225,6 +225,44 @@ tests:
# -- Overrides the image tag # -- Overrides the image tag
tag: latest@sha256:96ab1600d945b4a99c8610b5c8b31e346da63dc20573a26bb0777dd0190db5d4 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: tools:
kubectl: kubectl:
image: image: