Search code examples
kubernetescertificatekubectlkubernetes-apiserver

Using a custom certificate for the Kubernetes api server with minikube


I have been trying to find how to do this but so far have found nothing, I am quite new to Kubernetes so I might just have looked over it. I want to use my own certificate for the Kubernetes API server, is this possible? And if so, can someone perhaps give me a link?


Solution

  • Ok, so here is my idea. We know we cannot change cluster certs, but there is other way to do it. We should be able to proxy through ingress.

    First we enabled ingres addon:

    ➜  ~ minikube addons enable ingress
    

    Given tls.crt and tls.key we create a secret (you don't need to do this if you are using certmanager but this requires some additinal steps I am not going to describe here):

    ➜  ~ kubectl create secret tls my-tls --cert=tls.crt --key tls.key
    

    and an ingress object:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-k8s
      annotations:
        nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    spec:
      tls:
      - hosts:
        - foo.bar.com
        secretName: my-tls
      rules:
      - host: foo.bar.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: kubernetes
                port:
                  number: 443
    

    Notice what docs say about CN and FQDN: k8s docs:

    Referencing this secret in an Ingress tells the Ingress controller to secure the channel from the client to the load balancer using TLS. You need to make sure the TLS secret you created came from a certificate that contains a Common Name (CN), also known as a Fully Qualified Domain Name (FQDN) for https-example.foo.com.

    The only issue with this approach is that we cannot use certificates for authentication when accessing from the outside.

    But we can use tokens. Here is a page in k8s docs: https://kubernetes.io/docs/reference/access-authn-authz/authentication/ that lists all possible methods of authentication.

    For testing I choose serviceaccout token but feel free to experiment with others.

    Let's create a service account, bind a role to it, and try to access the cluster:

    ➜  ~ kubectl create sa cadmin
    serviceaccount/cadmin created
    ➜  ~ kubectl create clusterrolebinding --clusterrole cluster-admin --serviceaccount default:cadmin cadminbinding
    clusterrolebinding.rbac.authorization.k8s.io/cadminbinding created
    

    Now we follow these instructions: access-cluster-api from docs to try to access the cluster with sa token.

    ➜  ~ APISERVER=https://$(minikube ip)
    ➜  ~ TOKEN=$(kubectl get secret $(kubectl get serviceaccount cadmin -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode )
    ➜  ~ curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure -H "Host: foo.bar.com"
    {
      "kind": "APIVersions",
      "versions": [
        "v1"
      ],
      "serverAddressByClientCIDRs": [
        {
          "clientCIDR": "0.0.0.0/0",
          "serverAddress": "192.168.39.210:8443"
        }
      ]
    }
    

    note: I am testing it with invalid/selfsigned certificates and I don't own the foo.bar.com domain so I need to pass Host header by hand. For you it may look a bit different, so don't just copypate; try to understand what's happening and adjust it. If you have a domain you should be able to access it directly (no $(minikube ip) necessary).

    As you should see, it worked! We got a valid response from api server.

    But we probably don't want to use curl to access k8s.

    Let's create a kubeconfig with the token.

    kubectl config set-credentials cadmin --token $TOKEN --kubeconfig my-config
    kubectl config set-cluster mini --kubeconfig my-config --server https://foo.bar.com
    kubectl config set-context mini --kubeconfig my-config --cluster mini --user cadmin
    kubectl config use-context --kubeconfig my-config mini
    

    And now we can access k8s with this config:

    ➜  ~ kubectl get po --kubeconfig my-config
    No resources found in default namespace.