Skip to content

Create Service Account

There is currently a problem in our Kubernetes Management with the persistence of useraccounts created through the portal. This Page is dedicated to this problem with an workaround to create a service-accounts that are persistent.

Get Admin-Config from Portal

Log into the Cloud-Portal and go into your Kubernetes Dashboard.
If you already have an Kubernetes you are able to download the Kubeconfig for this Cluster. image
Click on Download Kubeconfig

image
Then click on Download Admin Kubeconfig

Download and Install Kubectl

Download kubectl from K8s.io

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

make kubectl usable

sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl    

Copy Admin Config to .kube/config

To access the cluster with kubectl you need to create a .kube Dir in your home and paste your config into it.

cd ~
mkdir .kube
cp admin-kube.yml .kube/config

Example with Bash Script

save bash script into addUser.sh

bash addUser.sh --user testuser --role cluster-admin
save output to your .kube/admin

Example Output from Script:

## KUBECONFIG generated on Wed Feb  5 09:10:03 CET 2025
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: XXXXXXXXXXX=
    server: https://api.kube5.ewt9qdm.gardener.stage.ewcs.ch
name: garden-414d97167c7345de93300fd42872e325--<clustername>-external
contexts:
- context:
    cluster: garden-414d97167c7345de93300fd42872e325--<clustername>-external
    user: <username>-garden-414d97167c7345de93300fd42872e325--<clustername>-external
name: garden-414d97167c7345de93300fd42872e325--<clustername>-external
current-context: garden-414d97167c7345de93300fd42872e325--<clustername>-external
kind: Config
preferences: {}
users:
- name: <username>-garden-414d97167c7345de93300fd42872e325--<clustername>-external
user:
    token: XXXXXX

Bash Script

Please install jq Package for the bash script

sudo apt install jq

    #!/bin/bash

    KUBECTL="/usr/local/bin/kubectl"

    usage() {
        echo "Usage: $0 --user USERNAME [--role ROLE] [--delete]"
        exit 1
    }

    SERVICE_ACC_NAME=""
    ROLE_NAME="view"
    DELETE_IT=false

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --user)
                SERVICE_ACC_NAME="$2"
                shift 2
                ;;
            --role)
                ROLE_NAME="$2"
                shift 2
                ;;
            --delete)
                DELETE_IT=true
                shift
                ;;
            *)
                usage
                ;;
        esac
    done

    if [ -z "$SERVICE_ACC_NAME" ]; then
        usage
    fi

    if $DELETE_IT; then
        $KUBECTL delete serviceaccount "$SERVICE_ACC_NAME"
        $KUBECTL delete secret "$SERVICE_ACC_NAME-secret"
        $KUBECTL delete clusterrolebinding "view-$SERVICE_ACC_NAME-global"
        exit 0
    fi

    $KUBECTL create serviceaccount "$SERVICE_ACC_NAME" -n default -o json

    cat <<EOF | $KUBECTL create -n default -o json -f -
    apiVersion: v1
    kind: Secret
    metadata:
    name: $SERVICE_ACC_NAME-secret
    annotations:
        kubernetes.io/service-account.name: $SERVICE_ACC_NAME
    type: kubernetes.io/service-account-token
    EOF

    SECRET_JSON=$($KUBECTL get secret "$SERVICE_ACC_NAME-secret" -o json)
    CA_CRT=$(echo "$SECRET_JSON" | jq -r '.data["ca.crt"]')
    USER_TOKEN=$(echo "$SECRET_JSON" | jq -r '.data.token | @base64d')

    KUBECONFIG_JSON=$($KUBECTL config view --minify -o json)
    CLUSTER_API=$(echo "$KUBECONFIG_JSON" | jq -r '.clusters[0].cluster.server')
    CLUSTER_NAME=$(echo "$KUBECONFIG_JSON" | jq -r '.clusters[0].name')

    cat <<EOF | $KUBECTL create -n default -o json -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: view-$SERVICE_ACC_NAME-global
    subjects:
    - kind: ServiceAccount
    name: $SERVICE_ACC_NAME
    namespace: default
    roleRef:
    kind: ClusterRole
    name: $ROLE_NAME
    apiGroup: rbac.authorization.k8s.io
    EOF

    echo "## KUBECONFIG generated on $(date)"
    cat <<EOF
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: $CA_CRT
        server: $CLUSTER_API
    name: $CLUSTER_NAME
    contexts:
    - context:
        cluster: $CLUSTER_NAME
        user: $SERVICE_ACC_NAME-$CLUSTER_NAME
    name: $CLUSTER_NAME
    current-context: $CLUSTER_NAME
    kind: Config
    preferences: {}
    users:
    - name: $SERVICE_ACC_NAME-$CLUSTER_NAME
    user:
        token: $USER_TOKEN
    EOF

Python Script

import sys
import json
import subprocess
import base64
from datetime import datetime

def exec_command(args, std_input=None):
    print("# " + " ".join(args))
    result = subprocess.run(args, input=std_input, text=True, capture_output=True)
    if result.returncode == 0:
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
            return result.stdout.strip()
    else:
        print(result.stderr, file=sys.stderr)
        sys.exit(1)

KUBECTL = "/usr/bin/kubectl"

if len(sys.argv) < 2 or sys.argv[1] == "--delete":
    print("please provide username as an argument, for example: python script.py USER_NAME [--delete]")
    sys.exit(1)

service_acc_name = sys.argv[1]
role_name = sys.argv[2] if len(sys.argv) > 2 else "view"
delete_it = len(sys.argv) > 3 and sys.argv[3] == "--delete"

if delete_it:
    exec_command([KUBECTL, "delete", "serviceaccount", service_acc_name])
    exec_command([KUBECTL, "delete", "secret", f"{service_acc_name}-secret"])
    exec_command([KUBECTL, "delete", "clusterrolebinding", f"view-{service_acc_name}-global"])
    sys.exit(0)

exec_command([KUBECTL, "create", "serviceaccount", service_acc_name, "-o", "json"])
exec_command([KUBECTL, "create", "-o", "json", "-f", "-"], f"""apiVersion: v1
kind: Secret
metadata:
  name: {service_acc_name}-secret
  annotations:
    kubernetes.io/service-account.name: {service_acc_name}
type: kubernetes.io/service-account-token""")

secret = exec_command([KUBECTL, "get", "secret", f"{service_acc_name}-secret", "-o", "json"])
ca_crt = secret["data"]["ca.crt"]
user_token = base64.b64decode(secret["data"]["token"]).decode()

kube_config = exec_command([KUBECTL, "config", "view", "--minify", "-o", "json"])
cluster_api = kube_config["clusters"][0]["cluster"]["server"]
cluster_name = kube_config["clusters"][0]["name"]

exec_command([KUBECTL, "create", "-o", "json", "-f", "-"], f"""apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name:  {role_name}-{service_acc_name}-global
subjects:
- kind: ServiceAccount
  name: {service_acc_name}
  namespace: default
roleRef:
  kind: ClusterRole
  name: {role_name}
  apiGroup: rbac.authorization.k8s.io""")

kubeconfig_output = f"""
## KUBECONFIG generated on {datetime.now()}
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: {ca_crt}
    server: {cluster_api}
  name: {cluster_name}
contexts:
- context:
    cluster: {cluster_name}
    user: {service_acc_name}-{cluster_name}
  name: {cluster_name}
current-context: {cluster_name}
kind: Config
preferences: {}
users:
- name: {service_acc_name}-{cluster_name}
  user:
    token: {user_token}
"""

print(kubeconfig_output)