Propagating OAuth2 tokens made easier
We are thrilled to announce the release of oauth2-refresh-controller v1.0.1 component. Modifying deployments to include OAuth2 secrets is now thing of the past. Inject and refresh tokens automatically with the oauth2-refresh-controller.
What is OAuth2
OAuth2 is the industry-standard protocol for authorization. It defines workflows on how to authorize user’s access to protected resources.
At the end of a workflow - such as verifying the application has access to the requested resources, that the secret provided is correct, … - the user obtains a piece of secret data called access token. This token is then passed around whenever access to protected resources is requested.
(source https://blog.oauth.io/introduction-oauth2-flow-diagrams/)
At CERN, the Authorization Service already provides ways to integrate services and applications. On the other side, a variety of services is able to consume access tokens - including the EOS storage service which we’ll talk about in this post.
Introducing oauth2-refresh-controller
oauth2-refresh-controller is a Kubernetes controller for injecting OAuth2 access tokens into Pods, and then their subsequent rotation using refresh tokens. It is deployed as an opt-in feature in the upcoming v1.26 cluster templates at CERN.
How do I use this?
Create a secret containing the OAuth2 token, and annotate your Pods accordingly. It’s as simple as that.
apiVersion: v1
kind: Secret
metadata:
name: hello-token
annotations:
# is-token annotation makes this Secret visible to the oauth2-refresh-controller.
oauth2-refresh-controller.cern.ch/is-token: "true"
stringData:
# oauth2-refresh-controller expects tokens to have "oauth",
# "clientID" and "clientSecret" fields set.
# "oauth" must be a JSON-formatted string with "access_token"
# and "refresh_token" fields (other fields are ignored).
oauth: '{"access_token": "eyJhb...","expires_in":1199,"refresh_expires_in":0,"refresh_token":"eyJhbG...","token_type":"Bearer","not-before-policy":1643110683,"session_state":"5d5e8bc2-6557-4453-9ba2-8ed99be6c898","scope":"offline_access profile email"}}'
clientID: "my-app"
clientSecret: "my-app-client-secret"
apiVersion: v1
kind: Pod
metadata:
name: token-printer-pod
annotations:
# to-inject annotation describes which token to inject into what container, and under what user.
# It's a JSON-formatted string holding an array of objects with following schema:
#
# * secretName: (string) Name of the OAuth2 token Secret in Pod's namespace.
# * container: (string) Name of the container into which inject the token.
# * owner: (integer) The token file will have its UID and GID set to this value.
#
# See docs at https://kubernetes.docs.cern.ch/docs/security/credentials/#oauth2
# for complete list of available parameters (link restricted for internal access only).
oauth2-refresh-controller.cern.ch/to-inject: |
[
{
"secretName": "hello-token",
"container": "token-printer",
"owner": 0
}
]
spec:
containers:
- name: token-printer
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |
while true; do
cat /tmp/oauthtk_0
date
sleep 20m
done
The controller will now automatically refresh the access token inside the secret before it expires, as well as the token files in your pods.
$ kubectl exec -it token-printer-pod -- sh
root@token-printer-pod# ls -l /tmp
total 4
-r-------- 1 root root 6 Apr 5 09:40 oauthtk_0
root@token-printer-pod# cat /tmp/oauthtk_0
eyJhb...<rest of the access JWT>
Be sure to check the full documentation (internal only) to find out more!
Example usage with EOS
You can set the oauth2-refresh-controller.cern.ch/to-inject
annotation to make the token compatible with EOS-OAuth2 authentication.
kubectl create configmap oauth-token-eos-template \
--from-literal template='oauth2:$(ACCESS_TOKEN):auth.cern.ch/auth/realms/cern/protocol/openid-connect/userinfo'
apiVersion: v1
kind: Pod
metadata:
name: token-printer-pod
annotations:
oauth2-refresh-controller.cern.ch/to-inject: |
[
{
"secretName": "hello-token",
"container": "token-printer",
"owner": 0,
"templateConfigMapName": "oauth-token-eos-template"
}
]
spec:
volumes:
- name: eos
hostPath:
path: /var/eos
containers:
- name: eos-printer
image: busybox
imagePullPolicy: IfNotPresent
volumeMounts:
- name: eos
mountPath: /eos
env:
- name: OAUTH2_TOKEN
value: FILE:/tmp/oauthtk_0
command:
- /bin/sh
- -c
- |
while true; do
cat /tmp/oauthtk_0
ls -l /eos/home-r/rvasek
sleep 20m
done
Here we have created a ConfigMap with a template of the token file and then instructed the oauth2-refresh-controller to use it by specifying the templateConfigMapName
parameter in the annotation. The template contains an EOS-compatible OAuth2 access token format. $(ACCESS_TOKEN)
will be expanded by the oauth2-refresh-controller to the actual token value. Lastly we add the OAUTH2_TOKEN
environment variable that’s needed by the eosxd client, and we’re all set!
Plans for the future
In next iterations of the component we plan to improve and optimize the access to the Kubernetes API to lessen the number of calls needed. We would also like to hear from you, what uses you might have for this tool, and how we can improve it to suite your needs. Stay tuned!