Introduction
Rancher is a web gui for administering kubernetes clusters. We often use it to maintain single node K3S clusters. We have an automation in ansible to set up a new VM with K3S, cert-manager, and Rancher. When the stack is installed, we want to tweak the Rancher installation a bit and, for example, configure the Active Directory integration for sysadmin login.
Today I learned that:
- The Rancher API only accepts API tokens for the authentication, no username/password is allowed
- The default way to go at this and create API tokens, is to log in to Rancher and create the tokens through the web gui
This does not really help for a fully automated installation.
After some digging around, I decided to try an old trick to fake a user session in the web gui. I used curl and tried to save a session cookie using the URL that I found in the Chrome Developer Mode.
This DOES NOT work as intended:
function getPassword(){
#--- fetching the default password used in the installation
kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}{{ "\n" }}'
}
RANCHER_URL=https://k3s.example.com
USERNAME=admin
PASSWORD=$(getPassword)
curl -s -c my_cookies.txt -k -X POST "$RANCHER_URL/v3-public/localProviders/local?action=login" \
-H "Content-Type: application/json" \
-d '{
"username": "'"$USERNAME"'",
"password": "'"$PASSWORD"'"
}' > /dev/null 2>&1
$ cat my_cookies.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
This was very annoying, but then I actually looked at the output from the curl call. It looked like the web application gets a token returned in the json string:
curl -s -c my_cookies.txt -k -X POST "$RANCHER_URL/v3-public/localProviders/local?action=login" -H "Content-Type: application/json" -d '{
"username": "'"$USERNAME"'",
"password": "'"$PASSWORD"'"
}' | jq .
{
"authProvider": "local",
"baseType": "token",
"clusterId": null,
"created": "2024-11-10T20:20:57Z",
"createdTS": 1731270057000,
"creatorId": null,
"current": false,
"description": "",
"enabled": true,
"expired": false,
"expiresAt": "",
"id": "token-98qdh",
"isDerived": false,
"labels": {
"authn.management.cattle.io/kind": "session",
"authn.management.cattle.io/token-userId": "user-hrvl6",
"cattle.io/creator": "norman"
},
"lastUpdateTime": "",
"links": {
"self": "https://k3s.example.com/v3-public/tokens/token-98qdh"
},
"name": "token-98qdh",
"token": "token-98qdh:v7x8g7js7tmn679pc9rx4kjqnslhfkh7nhnl7qrtrvvzccsqp4rgft",
"ttl": 57600000,
"type": "token",
"userId": "user-hrvl6",
"userPrincipal": "map[displayName:Default Admin loginName:admin me:true metadata:map[creationTimestamp:<nil> name:local://user-hrvl6] principalType:user provider:local]",
"uuid": "0e876481-8671-40b5-aa15-51cd19d94f7d"
}
So I tried again:
$ curl -s -c my_cookies.txt -k -X POST "$RANCHER_URL/v3-public/localProviders/local?action=login" -H "Content-Type: application/json" -d '{
"username": "'"$USERNAME"'",
"password": "'"$PASSWORD"'"
}' | jq .token
"token-l8sj6:ptt2lvz95b44ln6gtz2tbwkw58clmtm8vdjd46x5qsmwvv9p8pmslp"
Wow, this token could eventually be useful! I tried it with the /v1/schemas
path, and it actually worked:
token=token-l8sj6:ptt2lvz95b44ln6gtz2tbwkw58clmtm8vdjd46x5qsmwvv9p8pmslp
curl -s -k -u $token $RANCHER_URL/v1/namespaces | jq '.data[]| .metadata.name'"cattle-fleet-clusters-system" | head -2
"cattle-fleet-local-system"
"cattle-fleet-system"
Result
So, here it is, a small script to get a token for the API without logging in to the GUI.
cat ./k3s-get-admin-token-by-password
#!/bin/bash
function getPassword(){
#--- fetching the default password used in the installation
kubectl get secret --namespace cattle-system bootstrap-secret -o go-template='{{.data.bootstrapPassword|base64decode}}{{ "\n" }}'
}
function getToken(){
USERNAME=admin
PASSWORD=$(getPassword)
curl -s -k -X POST "$RANCHER_URL/v3-public/localProviders/local?action=login" \
-H "Content-Type: application/json" \
-d '{
"username": "'"$USERNAME"'",
"password": "'"$PASSWORD"'"
}' | \
tail -1 | jq -r '.token'
}
#=======================================
# MAIN
#=======================================
RANCHER_URL="${RANCHER_URL:-https://k3s.example.com}"
getToken
And a quick demo:
RANCHER_URL=https://k3s.example.com
token=$(./k3s-get-admin-token-by-password)
$ curl -s -k -u $token ${RANCHER_URL}/v1/namespaces | jq '.data[]| .metadata.name' | head -2
"cattle-fleet-clusters-system"
"cattle-fleet-local-system"
References
- https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade/install-upgrade-on-a-kubernetes-cluster
- https://github.com/rancher/rancher/issues/32504 # <— unresolved ticket concerning AD integration through the API. This actually works.