Setting up a Kubernetes CI/CD Service Account
As Docker containers are becoming more and more the norm of software deployment, Kubernetes clusters across the world are taking off as a convenient infrastructure alternative to plain basic servers.
With deployed applications becoming more resilient thanks to Kubernetes, we should also not forget about our CI/CD routine, as still the most resilient application sometimes fails, we'd not want to be hard-pressed to deliver a fix in time due to self-imposed deployment obstacles.
So let's create a CI/CD user for our cluster, which allows us to keep the cluster credentials safe, and the deployment routine fast.
Creating the service account
First steps first, let's create the plain service account, without any permissions or roles assigned to it, this is fairly straight forward.
SERVICE_ACCOUNT_NAME="cicd-user"
NAMESPACE="default"
kubectl create sa "${SERVICE_ACCOUNT_NAME}" --namespace "${NAMESPACE}"
After the command returns, kubectl
, should report serviceaccount/cicd-user created
. Good!
Creating the target folder
Next up we create a temporary folder, where later on we want our generated config to be put in.
TARGET_FOLDER="$HOME/tmp/kube"
mkdir -p "${TARGET_FOLDER}"
Extracting secret name and public key
After having created the folder successfully, we need to fetch the secret name of our previously created service account, and extract the public key information of it.
SECRET_NAME=$(kubectl get sa "${SERVICE_ACCOUNT_NAME}" --namespace="${NAMESPACE}" -o json | jq -r .secrets[].name)
kubectl get secret --namespace "${NAMESPACE}" "${SECRET_NAME}" -o json | jq -r '.data["ca.crt"]' | base64 --decode > "${TARGET_FOLDER}/ca.crt"
Mind you, we are using jq here to parse the json output from kubectl.
Fetching the usertoken
Not far until we are done, the next step will be to fetch and save the usertoken out of the service account secret.
USER_TOKEN=$(kubectl get secret --namespace "${NAMESPACE}" "${SECRET_NAME}" -o json | jq -r '.data["token"]' | base64 --decode)
Bringing it all together
Having gathered all the necessary information in the previous steps, it's now time to put it all together and generate our long desired config file for our service account.
KUBECFG_FILE_NAME="$HOME/tmp/kube/k8s-${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-conf"
context=$(kubectl config current-context)
CLUSTER_NAME=$(kubectl config get-contexts "$context" | awk '{print $3}' | tail -n 1)
ENDPOINT=$(kubectl config view \
-o jsonpath="{.clusters[?(@.name == \"${CLUSTER_NAME}\")].cluster.server}")
kubectl config set-cluster "${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--server="${ENDPOINT}" \
--certificate-authority="${TARGET_FOLDER}/ca.crt" \
--embed-certs=true
kubectl config set-credentials \
"${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--token="${USER_TOKEN}"
kubectl config set-context \
"${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--cluster="${CLUSTER_NAME}" \
--user="${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--namespace="${NAMESPACE}"
kubectl config use-context "${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}"
Assigning a Role via RoleBinding
One little thing is still to do, since we only created a config file and a service account in the previous steps, our service account still doesn't have any permissions to do stuff a CI/CD user should be allowed to do, like deploying for example.
To fix this we'll need to apply the following role and role binding configuration:
Save the above yaml, and then apply it using:
kubectl apply -f cicd-role-config.yaml
But again, mind you, we are referencing the name of the generated service account in the yaml file, if you changed the name during the first step, you ought to change it here too!
Testing it out
The final steps are done, now it's time to check if the effort was worth it.
KUBECONFIG=${KUBECFG_FILE_NAME} kubectl get pods
Congratulations!
Well done! Now you have your cluster setup with a second account for deployment, so that your Cluster administrator can keep his config for himself, you put your generated config to your preferred CI/CD solution, and enjoy hands-free deployments of your projects