Skip to content

Exposing kube-apiserver

This is done on your own peril but if you want to mimic managed kubernetes solutions where you can connect to an external URL/address over the internet to manage your cluster then this is how you can do it.

I won't go into detail on each step as this is just a personal memo for myself until I can automate the process in my Ansible setup.

Install Traefik

Traefik is best for this, if you already have nginx-ingress then you need another IP for Traefik. Or migrate all your nginx ingresses over to Traefik.

I use the traefik/traefik Helm chart from their own repo, just make sure you know which public IP Traefik is using.

Reason I use Traefik is because I need TCP passthrough because kube-apiserver handles its own TLS. At this moment in time I am not aware how to handle TLS termination for kube-apiserver.

Create a domain

I usually create a domain like kube-apiserver.production.zone.my-domain.tld, and point its DNS to your Traefik IP.

Add a SAN (Subject Alternative Name) in the TLS cert

You must repeat this step on each control node because they all run an instance of kube-apiserver. Then it's up to you to balance connections to them from some external load balancer like HAproxy.

The two files /etc/kubernetes/pki/apiserver.{crt,key} contain the cert generated by kubeadm init when you installed the control node.

Move them to a backup location

mv /etc/kubernetes/pki/apiserver.{crt,key} /var/backups/

Dump the current kubeadm-config ConfigMap to a file

kubectl -n kube-system get configmap kubeadm-config -o jsonpath='{.data.ClusterConfiguration}' > kubeadm.yaml

Edit the kubeadm.yaml config

There's a list called certSANs where you add any names you need, kubeadm will self-sign these so go nuts. There should be an IP there that belongs to your control node, leave it.

apiServer:
  certSANs:
  - 10.12.13.11
  - kube-apiserver.production.eu-central-1.my-domain.tld

Run kubeadm init

Now we only run one phase of the kubeadm init process which will create a new cert based on the config we modified.

sudo kubeadm init phase certs apiserver --config kubeadm.yaml

Expose kube-apiserver as a Service

Here's an example manifest based on what I use;

apiVersion: v1
kind: Service
metadata:
  name: kube-apiserver
  namespace: kube-system
  labels:
    component: kube-apiserver

spec:
  type: ClusterIP
  ports:
    - port: 443
      targetPort: 6443
      protocol: TCP
      name: apiserver

  selector:
    component: kube-apiserver

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
  name: kube-apiserver
  namespace: kube-system

spec:
  routes:
  - match: HostSNI(`*`)
    services:
    - name: kube-apiserver
      port: 443

  tls:
    passthrough: True

Last update: August 12, 2021