How to connect to Kubernetes cluster resources(services, pods) using OpenVPN for local development

Friday, Oct 19, 2018| Tags: kubernetes, helm, openvpn, local development, development workflow, developer productivity

I wanted to do development locally on my laptop and I had some services that were running in my kubernetes cluster. I was trying to figure out the easiest way to access those resources without exposing them via a loadbalancer or nodeport for the specific service. This is when I thought if I could vpn into the cluster then it would allow me to access cluster resources from my laptop.

Note: I will be using an EKS cluster on AWS. You could use the same steps on other cloud providers too.

Let’s get on to the steps to get it up and running. Our goal would be following:

  1. Access cluster resources locally from my laptop. e.g. I should be able to access a ClusterIP service nginx from my local laptop by doing:

$ curl nginx.default.svc.cluster.local
To achieve the above goal we will do following:

  1. set up openvpn in the cluster
  2. setup an openvpn client on my laptop.
  3. Connect to cluster using the openvpn client
  4. Create an nginx deployment and a service
  5. Access the nginx service using curl and browser

Step 1 - set up openvpn in the cluster

To make our life easier we will use helm. (You can look at this article on how to install helm if you don’t already have it installed). Let’s search for a helm chart for openvpn:

$ helm search openvpn


stable/openvpn	3.9.1        	1.1.0      	A Helm chart to install an openvpn server inside a kubern...

Now let’s install openvpn:

$ helm install stable/openvpn --name=openvpn --namespace=openvpn

This will deploy openvpn and will give you the steps that you need to follow to create an OpenVPN configuration file.

Now let’s check if our service is up and running:

$ kubectl get svc -n openvpn


NAME      TYPE           CLUSTER-IP       EXTERNAL-IP                                                              PORT(S)         AGE
openvpn   LoadBalancer   443:31015/TCP   3m

Now let’s follow the steps provided by the helm chart:

$ POD_NAME=$(kubectl get pods --namespace "openvpn" -l "app=openvpn,release=openvpn" -o jsonpath='{ .items[0] }')
$ SERVICE_NAME=$(kubectl get svc --namespace "openvpn" -l "app=openvpn,release=openvpn" -o jsonpath='{ .items[0] }')
$ SERVICE_IP=$(kubectl get svc --namespace "openvpn" "$SERVICE_NAME" -o go-template='{{ range $k, $v := (index .status.loadBalancer.ingress 0)}}{{ $v }}{{end}}')
$ kubectl --namespace "openvpn" exec -it "$POD_NAME" /etc/openvpn/setup/ "$KEY_NAME" "$SERVICE_IP"
$ kubectl --namespace "openvpn" exec -it "$POD_NAME" cat "/etc/openvpn/certs/pki/$KEY_NAME.ovpn" > "$KEY_NAME.ovpn"
This will give you a file kubeVPN.ovpn in your current folder.

Step 2 - Install OpenVPN client on your laptop

Since I am on mac I will use Tunnelblick. For windows get client at OpenVPN client.

Step 3 - Connect to the VPN using openvpn client

Use the generated kubeVPN.ovpn file to connect to VPN.

Step 4 - Create an nginx deployment and a service

To test if we are able to connect to cluster services from our laptop let’s create an nginx service.

$ kubectl create deployment nginx --image=nginx
$ kubectl expose deployment/nginx --port=80

Let’s take a look at the nginx service that we create by running:

$ kubectl get svc nginx


Name:              nginx
Namespace:         default
Labels:            app=nginx
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Session Affinity:  None
Events:            <none>

Step 5 - Access the nginx service using curl and browser

Now let’s try to connect to nginx service:

$ curl nginx.default.svc.cluster.local

This will give you the output. You could also check this in browser:

Service in browser

We have successfully accessed our kubernetes ClusterIP service from our laptop.

You could access any service in your kubernetes cluster using this mechanism during your development from your laptop where your code needs to access those services.