Kubernetes (Minikube) – Certificates, Expiry and Logins

I was using Minikube and had created some users with some certificates (see a previous article), however upon trying to login the following day and attempt to perform some commands I got this error:

kubectl config use-context bob

When running a “get pods”, I see I’m not actually authenticated!

$ kubectl get pods
error: You must be logged in to the server (Unauthorized)

Hunting the Issue

Looking under the Minikube logs, and specifically at the API Server (being that is what i’m attempting to access) what did I find….?

minikube logs --file=logs.txt
==> kube-apiserver [8567a0d00751] <==
...
E0612 08:54:39.300791       1 authentication.go:73] "Unable to authenticate the request" err="[x509: certificate has expired or is not yet valid: current time 2026-06-12T08:54:39Z is after 2026-06-11T08:25:37Z, verifying certificate SN=67937780518389855383239215857675577693, SKID=, AKID=2F:24:CC:BD:09:6B:24:92:92:EF:40:37:16:B6:18:4B:29:25:0B:03 failed: x509: certificate has expired or is not yet valid: current time 2026-06-12T08:54:39Z is after 2026-06-11T08:25:37Z]"
E0612 08:54:39.377540       1 authentication.go:73] "Unable to authenticate the request" err="[x509: certificate has expired or is not yet valid: current time 2026-06-12T08:54:39Z is after 2026-06-11T08:25:37Z, verifying certificate SN=67937780518389855383239215857675577693, SKID=, AKID=2F:24:CC:BD:09:6B:24:92:92:EF:40:37:16:B6:18:4B:29:25:0B:03 failed: x509: certificate has expired or is not yet valid: current time 2026-06-12T08:54:39Z is after 2026-06-11T08:25:37Z]"
E0612 09:10:17.218577       1 authentication.go:73] "Unable to authenticate the request" err="[x509: certificate has expired or is not yet valid: current time 2026-06-12T09:10:17Z is after 2026-06-11T08:25:37Z, verifying certificate SN=67937780518389855383239215857675577693, SKID=, AKID=2F:24:CC:BD:09:6B:24:92:92:EF:40:37:16:B6:18:4B:29:25:0B:03 failed: x509: certificate has expired or is not yet valid: current time 2026-06-12T09:10:17Z is after 2026-06-11T08:25:37Z]"
E0612 09:12:18.527477       1 authentication.go:73] "Unable to authenticate the request" err="[x509: certificate has expired or is not yet valid: current time 2026-06-12T09:12:18Z is after 2026-06-11T08:25:37Z, verifying certificate SN=67937780518389855383239215857675577693, SKID=, AKID=2F:24:CC:BD:09:6B:24:92:92:EF:40:37:16:B6:18:4B:29:25:0B:03 failed: x509: certificate has expired or is not yet valid: current time 2026-06-12T09:12:18Z is after 2026-06-11T08:25:37Z]"
...

Ah ha, the certificate for my user has expired, let’s check it out:

$ openssl x509 -in dave.crt -noout -text | grep ' Not '
            Not Before: Jun 10 08:25:37 2026 GMT
            Not After : Jun 11 08:25:37 2026 GMT

Yep, the certificate has indeed expired I was attempting to connect at after 08:25 in the morning, so now I need to renew it.

Renew Certificate

I already have a key, so need to just generate a new CSR.

openssl req -new -key dave.key -out dave.csr -subj "/CN=dave/O=dev"

Now get it signed.

cat dave.csr | base64 | tr -d "\n"

Create (or update) the CSR yaml file, pasting in the Base64 encoded CSR.

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: dave
spec:
  request: <CSR Base64 String>
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400
  usages:
    - client auth

Notice the 86400 (1 day/24 hours) in the expirationsSeconds.

Apply that:

kubectl apply -f csr.yaml

Check on it.

kubectl get certificatesigningrequests

And we can see it indeed was a 24 hour request:

$ kubectl get certificatesigningrequests
NAME    AGE   SIGNERNAME                            REQUESTOR       REQUESTEDDURATION   CONDITION
dave     21s   kubernetes.io/kube-apiserver-client   minikube-user   24h                 Pending

We now need to approve it.

kubectl certificate approve dave

Now we see it is approved and issued.

$ kubectl get certificatesigningrequests
NAME    AGE     SIGNERNAME                            REQUESTOR       REQUESTEDDURATION   CONDITION
dave 5m17s   kubernetes.io/kube-apiserver-client   minikube-user   24h                 Approved,Issued

Get the certificate:

kubectl get csr dave -o jsonpath='{.status.certificate}' | base64 -d > dave.crt

Configure the credentials for the user to use the new certificate just issued, the “realpath” ensures a full path to the file is used.

kubectl config set-credentials dave --client-key $(realpath dave.key) --client-certificate $(realpath dave.crt)

If we now check it:

openssl x509 -in dave.crt -noout -text | grep ' Not '

We can see we are good for another day, time of running was June 12th 2026, 10:45, so good until the following morning.

$ openssl x509 -in dave.crt -noout -text | grep ' Not '
            Not Before: Jun 12 09:34:42 2026 GMT
            Not After : Jun 13 09:34:42 2026 GMT

Testing

Now we test to see if we can authenticate.

kubectl config use-context dave

And now query for something we know we have privilege’s for (within Dave’s role):

$ kubectl get pod -n dev
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          60m

Lovely, that’s that, we’re up and running again.

Conclusion

A potentially common problem, and a balance of certificate lifetime against risk of compromise. You can decide how long certificates should be issued for. Its obviously better to have some automated process to generate renewals for you.

You can also set the expirationSeconds within the CSR to something longer if you so wish to avoid having to re-run this process so frequently!

Quick Synopisis of the commands used:

openssl req -new -key dave.key -out dave.csr -subj "/CN=dave/O=dev"
cat dave.csr | base64 | tr -d "\n"
Update CSR yaml file with new Base64 encoded CSR.
kubectl apply -f csr.yaml
kubectl get certificatesigningrequests
kubectl certificate approve dave
kubectl get certificatesigningrequests
kubectl get csr dave -o jsonpath='{.status.certificate}' | base64 -d > dave.crt
kubectl config set-credentials dave --client-key $(realpath dave.key) --client-certificate $(realpath dave.crt)

Then test with:

kubectl config use-context dave
kubectl get pod -n dev

Additional Information

Leave a comment