Effectively Managing Kubernetes Access from the Terminal

Customizing Your Terminal With Kubectx and Kube-ps1

Chris Pisano
Capital One Tech
8 min readJul 16, 2019

--

There are lots of ways to manage access to a Kubernetes cluster. None of them are difficult, but what if you have to manage multiple users and contexts on a single cluster? That becomes a little challenging. To add to that, I’m sure many of us are managing multiple clusters, and some of us probably multiple users and contexts on multiple clusters. Managing all of this can become quite cumbersome and really eat into our time.

I’ll be covering how I personally decided to give myself a better experience managing my Kubernetes clusters. I’ll cover the highs, the lows, and everything in between, all based on my personal preferences.

Research, Break, Fix, Repeat

In my work at Capital One, I interact with around three clusters and potentially three users and contexts per cluster on a daily basis. Eventually changing environment variables and running long kubectl commands ate away at my patience. Naturally, being the sysadmin that I am, I started to write some simple bash functions to put in my .bashrc. This worked for a while, but it wasn’t perfect. I had to run multiple commands to change kubeconfigs and contexts/users. This satisfied me for a little bit, but I wanted a better solution since it still struck a nerve every time it didn’t quite work as expected.

Eventually I told myself I was going to spend an afternoon (ended up being more like a day+) to put a good solution in place for my needs. I mean, if I spent who knows how long writing Ansible playbooks to manage all my dot files and package installs, this should be easy right?

Spoiler… it wasn’t.

After researching tools to manage Kubernetes access I decided on kubectx; it seemed to fit my needs. It manages namespace switching, is brew installable, and has 4k+ GitHub stars. As I was going through the README, like any good user of somebody else’s project, I noticed a reference to another tool which I quickly decided to also implement: kube-ps1. This is a nifty little application that allows you to add your Kubernetes cluster and namespace information to your PS1 line (your terminal command prompt). This has significantly fewer stars, but I’m a sucker for tinkering with my terminal, happy day for me.

Now I needed to figure out what to do about my actual access problems. I have three clusters, three users per cluster, and two different authentication methods. Two of the users are not named uniquely between clusters, but the clusters are named uniquely. Mapping credentials to users should have been straight forward.

Spoiler… it wasn’t.

I first started down the official path looking at the Kubernetes docs for managing multiple clusters. This got me probably 80% of the way there, but it didn’t have an answer for identically named users across clusters. I kept breaking my kubeconfig because the context didn’t know where to look for the user authentication.

Frustration ensued. Furious Google searching ensued. Failure set in.

As I started reaching the end of my line and going down with the ship, about to call my attempt a failure and resign myself to disappointment, I pulled out a last ditch effort and asked in the MagicCityTech#kubernetes Slack channel (my Birmingham buddies) if anyone had figured this out. I got one reply, the same guy that answers all the Kubernetes questions, and it’s a link which solved everything for me.

Getting it All to Work

The first thing I did was retrieve all my kubeconfigs and service account tokens out of my gopass repository; if you don’t have gopass you’re seriously missing out. From there I started building out my “master” kubeconfig, which I would later use for all of my accesses. It ended up looking a little something like this:

apiVersion: v1
clusters:
- cluster:
certificate-authority-data: <data>
server: <server>
name: dev-ue1
- cluster:
certificate-authority-data: <data>
server: <server>
name: qa-ue1
- cluster:
certificate-authority-data: <data>
server: <server>
name: qa-uw2
contexts:
- context:
cluster: dev-ue1
namespace: kube-system
user: dev-ue1-admin
name: dev-ue1-admin
- context:
cluster: dev-ue1
namespace: admin-services
user: dev-ue1-admin-services
name: dev-ue1-admin-services
- context:
cluster: dev-ue1
namespace: user
user: dev-ue1-user
name: dev-ue1-user
- context:
cluster: qa-ue1
namespace: kube-system
user: qa-ue1-admin
name: qa-ue1-admin
- context:
cluster: qa-ue1
namespace: admin-services
user: qa-ue1-admin-services
name: qa-ue1-admin-services
- context:
cluster: qa-uw2
namespace: kube-system
user: qa-uw2-admin
name: qa-uw2-admin
- context:
cluster: qa-uw2
namespace: admin-services
user: qa-uw2-admin-services
name: qa-uw2-admin-services
kind: Config
preferences: {}
users:
- name: dev-ue1-admin
user:
client-certificate-data: <client certificate>
client-key-data: <client key>
- name: dev-ue1-admin-services
user:
token: <token>
- name: dev-ue1-user
user:
token: <token>
- name: qa-ue1-admin
user:
client-certificate-data: <client certificate>
client-key-data: <client key>
- name: qa-ue1-admin-services
user:
token: <token>
- name: qa-uw2-admin
user:
client-certificate-data: <client certificate>
client-key-data: <client key>
- name: qa-uw2-admin-services
user:
token: <token>

This kubeconfig allows me to use all of my contexts and users across all of my clusters via a single configuration file. This also allows me to access each of the clusters using the non-uniquely named service account admin-services. I can do that because in the kubeconfig I have uniquely named all of my users in my context. I can then look up the specific access token per user and use the certificate-authority-data for the specific cluster to authenticate. Without having the certificate-authority-data in the cluster block, this will not work. Now that the hard and complicated part is done, and stored securely in gopass, I can move on to actually making it useable.

The first thing I did was add a line to my .bashrc to export the KUBECONFIG environment variable to my newly created kubeconfig. I then brew installed kubectx and kube-ps1, and started tweaking my PS1 line and how things were displayed. Naturally, the stock kube-ps1 output wasn’t what I wanted, so it was time to start modifying. I should note that if you’re already using a tool to manage your PS1 line you might need to do something a little different to get this working.

My PS1 line was already pretty long; I have helpers for git status and branches, as well as Terraform workspaces. So when I saw that kube-ps1 displayed my entire context and namespace it just wasn’t going to work. Luckily, I’m a tmux user (thanks again to MagicCityTech for getting me on the tmux train) and have access to a nifty little status line at the bottom of my terminal. I decided I want to display the cluster and user in my tmux status line, and only the namespace in my PS1 line.

The custom config ensues.

First, I turned off the silly Kubernetes symbol kube-ps1 uses, removed the prefix and suffix, and removed the namespace text color configuration (I have my own color preferences). This was all easily done by reading through the README and setting some environment variables in my .bashrc.

KUBE_PS1_SYMBOL_ENABLE=false
KUBE_PS1_PREFIX=""
KUBE_PS1_SUFFIX=""
KUBE_PS1_NS_COLOR=""
export \
KUBE_PS1_SYMBOL_ENABLE \
KUBE_PS1_PREFIX \
KUBE_PS1_SUFFIX \
KUBE_PS1_NS_COLOR

Next I needed to write a custom function to get only the information I wanted and set my colors. In my .bashrc I wrote a simple function and executed it as part of my PS1 line.

kube_ns() {
purple='\033[0;35m'
nocolor='\033[0m'
kube_ps1=$(kube_ps1)
namespace=$(echo ${kube_ps1} | cut -d ':' -f 2)
echo -e "${nocolor}(${purple}${namespace}${nocolor})"
}
PS1="\$(kube_ps1) \[\033[32m\]\u\[\033[0m\] \[\033[36m\]\w\[\033[0m\]\$(git_prompt_info '\[\033[34m\]%b\[\033[0m\]') \$(terraform_workspace_info '\[\033[95m\]%b\[\033[0m\]')\n❯ "

Now that I had that in place, and a visually appealing terminal, I tried using kubectx.

Spoiler… it worked.

kubectx changing contexts and clusters

You can see that kube-ps1 is populating the (namespace) part of my command prompt which is exactly what I want. You can also see how easy it is to switch, not only context, but also clusters, with a single command.

kubectx also lets you change namespaces using kubens. This command will list all the namespaces and then change to whichever one you specify.

kubens changing namespaces within a context

Notice that my command prompt updates when I change namespaces but my context remains the same.

Remember when I said I was a tmux user? Well that’s additional configuration which was way simpler. In my .tmux.conf file I had to configure my tmux environment to know about my KUBECONFIG env var and my status-left-length and status-left; I use the right side to display the date. I keep all my configs in ${HOME}/.config.

set-environment -g KUBECONFIG ${HOME}/.config/kube-master.configset-option -g status-left-length 100
set-option -g status-left "#[fg=red]#(kubectl config current-context | cut -d '-' -f 1–2)#[fg=white]#(echo :)#[fg=blue]#(kubectl config current-context | cut -d '-' -f 3–4)"

Adjust your cut command as needed based on your context naming convention.

Those three lines give me the exact information I want in the bottom left corner of my terminal. It also will update as you change clusters/contexts/users with kubectx, just as you would expect.

tmux status line showing cluster and user information

Was It Worth It

As for whether you should you do this, the answer is up to you and depends on how much you want to invest in your local environment. This wasn’t required to do my job well, but it did allow me do it more efficiently. I invest a lot into my local config because I enjoy having a finely tuned development environment that fits all of my needs. If you are the same way, then this is something that might be for you as well.

Related:

DISCLOSURE STATEMENT: © 2019 Capital One. Opinions are those of the individual author. Unless noted otherwise in this post, Capital One is not affiliated with, nor endorsed by, any of the companies mentioned. All trademarks and other intellectual property used or displayed are property of their respective owners.

--

--

Chris Pisano
Capital One Tech

Highly opinionated systems engineer dabbling in networking and security writing code to make my life easier.