|
| 1 | +# Self-hosted Kubernetes setup |
| 2 | + |
| 3 | +If you are running your own Kubernetes cluster, there are several steps required for this feature to work. |
| 4 | + |
| 5 | +This feature requires Kubernetes 1.12 or greater. |
| 6 | + |
| 7 | +## Projected Token Signing Keypair |
| 8 | + |
| 9 | +The first thing required is a new key pair for signing and verifying projected |
| 10 | +service account tokens. This can be done using the following `ssh-keygen` |
| 11 | +commands. |
| 12 | + |
| 13 | +```bash |
| 14 | +# Generate the keypair |
| 15 | +PRIV_KEY="sa-signer.key" |
| 16 | +PUB_KEY="sa-signer.key.pub" |
| 17 | +PKCS_KEY="sa-signer-pkcs8.pub" |
| 18 | +# Generate a key pair |
| 19 | +ssh-keygen -t rsa -b 2048 -f $PRIV_KEY -m pem |
| 20 | +# convert the SSH pubkey to PKCS8 |
| 21 | +ssh-keygen -e -m PKCS8 -f $PUB_KEY > $PKCS_KEY |
| 22 | +``` |
| 23 | + |
| 24 | +## Public Issuer |
| 25 | + |
| 26 | +As of 1.16, Kubernetes does not include an OIDC discovery endpoint itself (see |
| 27 | +[kubernetes/community#1190](https://github.com/kubernetes/enhancements/pull/1190)), |
| 28 | +so you will need to put your public signing key somewhere that AWS STS can |
| 29 | +discover it. This example, we will create one in a public S3 bucket, but you |
| 30 | +could host the following documents any way you'd like on a different domain. |
| 31 | + |
| 32 | +### Create an S3 bucket |
| 33 | + |
| 34 | +```bash |
| 35 | +# Create S3 bucket with a random name. Feel free to set your own name here |
| 36 | +export S3_BUCKET=${S3_BUCKET:-oidc-test-$(cat /dev/random | LC_ALL=C tr -dc "[:alpha:]" | tr '[:upper:]' '[:lower:]' | head -c 32)} |
| 37 | +# Create the bucket if it doesn't exist |
| 38 | +_bucket_name=$(aws s3api list-buckets --query "Buckets[?Name=='$S3_BUCKET'].Name | [0]" --out text) |
| 39 | +if [ $_bucket_name == "None" ]; then |
| 40 | + if [ "$AWS_REGION" == "us-east-1" ]; then |
| 41 | + aws s3api create-bucket --bucket $S3_BUCKET |
| 42 | + else |
| 43 | + aws s3api create-bucket --bucket $S3_BUCKET --create-bucket-configuration LocationConstraint=$AWS_REGION |
| 44 | + fi |
| 45 | +fi |
| 46 | +echo "export S3_BUCKET=$S3_BUCKET" |
| 47 | +export HOSTNAME=s3-$AWS_REGION.amazonaws.com |
| 48 | +export ISSUER_HOSTPATH=$HOSTNAME/$S3_BUCKET |
| 49 | +``` |
| 50 | + |
| 51 | +### Create the OIDC discovery and keys documents |
| 52 | + |
| 53 | +Part of the OIDC spec is to host an OIDC discovery and a keys JSON document. |
| 54 | +Lets create these: |
| 55 | + |
| 56 | +```bash |
| 57 | +# Get the sha of the public key and use it as the key id |
| 58 | +KID=$(sha1sum $PUB_KEY | awk '{print $1}') |
| 59 | +cat <<EOF > discovery.json |
| 60 | +{ |
| 61 | + "issuer": "https://$ISSUER_HOSTPATH/", |
| 62 | + "jwks_uri": "https://$ISSUER_HOSTPATH/keys.json", |
| 63 | + "authorization_endpoint": "urn:kubernetes:programmatic_authorization", |
| 64 | + "response_types_supported": [ |
| 65 | + "id_token" |
| 66 | + ], |
| 67 | + "subject_types_supported": [ |
| 68 | + "public" |
| 69 | + ], |
| 70 | + "id_token_signing_alg_values_supported": [ |
| 71 | + "RS256" |
| 72 | + ], |
| 73 | + "claims_supported": [ |
| 74 | + "sub", |
| 75 | + "iss" |
| 76 | + ] |
| 77 | +} |
| 78 | +EOF |
| 79 | +``` |
| 80 | + |
| 81 | +Included in this repo is a small go file to help create the keys json document. |
| 82 | + |
| 83 | +```bash |
| 84 | +go run ./hack/self-hosted/main.go -key $PKCS_KEY -kid $KID > keys.json |
| 85 | +``` |
| 86 | + |
| 87 | +After you have the `keys.json` and `discovery.json` files, you'll need to place |
| 88 | +them in your bucket. It is critical these objects are public so STS can access |
| 89 | +them. |
| 90 | + |
| 91 | +```bash |
| 92 | +aws s3 cp --acl public-read ./discovery.json s3://$S3_BUCKET/.well-known/openid-configuration |
| 93 | +aws s3 cp --acl public-read ./keys.json s3://$S3_BUCKET/keys.json |
| 94 | +``` |
| 95 | + |
| 96 | +## Kubernetes API Server configuration |
| 97 | + |
| 98 | +As of Kubernetes 1.12, Kubernetes can issue and mount projected service account |
| 99 | +tokens in pods. |
| 100 | + |
| 101 | +In order to use this feature, you'll need to set the following |
| 102 | +[API server flags](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/). |
| 103 | + |
| 104 | +``` |
| 105 | +# This flag is likely already specified for legacy service accounts, you can |
| 106 | +# specify this flag multiple times, and you'll need to add this with the path |
| 107 | +# to the $PUB_KEY file from the beginning |
| 108 | +--service-account-key-file |
| 109 | +
|
| 110 | +# Path to the signing (private) key ($PRIV_KEY) |
| 111 | +--service-account-signing-key-file |
| 112 | +
|
| 113 | +# Identifiers of the API. The service account token authenticator will validate |
| 114 | +# that tokens used against the API are bound to at least one of these audiences. |
| 115 | +# If the --service-account-issuer flag is configured and this flag is not, this |
| 116 | +# field defaults to a single element list containing the issuer URL. |
| 117 | +# |
| 118 | +# `--api-audiences` is for v1.13+, `--service-account-api-audiences` in v1.12 |
| 119 | +--api-audiences |
| 120 | +
|
| 121 | +# The issuer URL, or "https://$ISSUER_HOSTPATH" from above. |
| 122 | +--service-account-issuer |
| 123 | +``` |
| 124 | + |
| 125 | +## Audiences |
| 126 | + |
| 127 | +The above `--api-audiences` flag sets an `aud` value for tokens that do not |
| 128 | +request an audience, and the API server requires that any projected tokens used |
| 129 | +for pod to API server authentication must have this audience set. This can |
| 130 | +usually be set to `kubernetes.svc.default`, or optionally the DNS name of your |
| 131 | +API server. |
| 132 | + |
| 133 | +When using a Kubernetes-issued token for an external system, you should use a |
| 134 | +different audience (or in OAuth-2 parlance, `client-id`). The external system |
| 135 | +(such as AWS IAM) will usually require an audience, or client-id, at setup. For |
| 136 | +AWS IAM, a token's `aud` must match the OIDC Identity Provider's client ID. EKS |
| 137 | +uses the string `sts.amazonaws.com` as the default, but when using the webhook |
| 138 | +yourself, you can use any audience you'd like as long as the webhook's flag |
| 139 | +`--token-audience` is set to the same value as your IDP in IAM. |
| 140 | + |
| 141 | +## Provider creation |
| 142 | + |
| 143 | +From here, you can mostly follow the process in the [EKS |
| 144 | +documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) |
| 145 | +and substitue the cluster issuer with `https://$ISSUER_HOSTPATH`. |
0 commit comments