This repo demonstrates how to implement a TLS certificate in a local development k8s
cluster.
There were a few issues I came across when I was integrating the current web app I'm working on with Azure AD and a few other web APIs:
- Needing
https://
for redirect URLs. - You can just provide a redirect URL of
https://<minikube-ip>
, but the browser "safety" warning messages due to an invalid certificate get pretty annoying. - While you can implement TLS on an IP address--it isn't a common practice--it is prohibited on non-public IPs (e.g.,
192.168.x.x
), which is likely whatminikube ip
will provide. - Most importantly: the need to encrypt data in transit when communicating with an external web API.
Rewriting the process as it has now been simplified:
- It used to involve using a
minikube --driver=
of the VM you prefer to use (hyperkit
,kvm
,hyperv
, etc.). However, the prefered method has now becomeminikube --drive=docker
and usingminikube tunnel
. It is more consistent across operating systems and involves fewer steps, but does require using an additional terminal forminikube tunnel
to run. - It used to involve modifying the operating system's
host
file to mapdomain.local
to theminikube ip
. This is no longer necessary and just useslocalhost
. - These steps should work for macOS (Intel and M1), Windows and WSL2, and Linux.
I was able to sort this out in large part due to this blog post by Sébastien Dubois.
What we'll do to get this working is the following:
-
Setting up our base project environment using (go to step):
react
(create-react-app
)docker
kubectl
minikube
skaffold
-
Once installed configuring the cluster (go to step):
- Setting up
minikube
- Setting up
ingress-nginx
- Setting up
-
Spinning up the cluster with
skaffold
to make sure the cluster is working (go to step). -
Installing and adding a TLS certificate to the cluster (go to step):
- Installing and setting up
cert-manager
- Installing and setting up
mkcert
- Installing and setting up
For brevity, you'll just want to clone what is basically just a fresh create-react-app
application, Dockerfile.dev
and .yaml
config files:
# clone the repo
git clone https://github.com/cheslijones/tls-minikube.git
# change into the directory
cd tls-minikube
Please follow the directions for your operating system here.
If you are using a Docker Desktop version, it just needs to be enabled in the Docker Desktop GUI. You can also refer to installation instructions for your OS here.
Please follow the directions for your operating system here.
Please follow the directions for your operating system here.
At the end of this, you should be able to type run the following commands and get a list of the acceptable sub-commands for each command.
# docker
docker run hello-world
# or
sudo docker run hello-world
# kubectl
kubectl version --client
# minikube
minikube
# skaffold
skaffold
If not, please refer to the documentation linked to above and follow the steps again.
It used to be preferred to use a VM driver like kvm
, hyperkit
, hyperv
, etc., but using the docker
driver tends to be more consistent across operating systems and doesn't require mapping the minikube ip
to domain.local
name in the host
file.
Crete the minikube
cluster with:
minikube start --vm-driver=docker
# or if you want to give the cluster a specific name to keep them separate
minikube start -p <cluster-name> --vm-driver=docker
Within several seconds to a minute, your minikube
should be up and running.
In the ingress-nginx
documentation, you don't use the directions for minikube
. Instead you use the Docker for Mac. It is counter-intuitive, but it does work whether you use Docker Desktop for Windows or macOS, or WSL2 and Linux:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/cloud/deploy.yaml
The repo comes with the ./ingress.yaml
, ./k8s/client.yaml
, ./client/Dockerfile.dev
, and ./skaffold.yaml
already configured. They will do the following:
./ingress.yaml
: This youringress-nginx
ingress controller configuration. It accepts traffic for our web application and then routes it to the appropriate running service../k8s/client.yaml
: This is the configuration for thecreate-react-app
client
deployment../client/Dockerfile.dev
: This is thedocker
configuration for how to build theclient
image../skaffold.yaml
: This configuration handles the building of thedocker
image and deploying tominikube
.
-
To spin the cluster up, run the following command:
skaffold dev
It can take around a minute for the
client
image to be built and deployed in the cluster. When it is done, you should see something like the following:Starting deploy... - ingress.networking.k8s.io/ingress-dev created - deployment.apps/client-deployment-dev configured - service/client-cluster-ip-service-dev configured Waiting for deployments to stabilize... - deployment/client-deployment-dev is ready. Deployments stabilized in 2.347 seconds Press Ctrl+C to exit Watching for changes... [client] [client] > client@0.1.0 start /app [client] > react-scripts start [client] [client] ℹ 「wds」: Project is running at http://172.17.0.4/ [client] ℹ 「wds」: webpack output is served from [client] ℹ 「wds」: Content not from webpack is served from /app/public [client] ℹ 「wds」: 404s will fallback to / [client] Starting the development server... [client] [client] Compiled successfully! [client] [client] You can now view client in the browser. [client] [client] Local: http://localhost:3000 [client] On Your Network: http://172.17.0.4:3000 [client] [client] Note that the development build is not optimized. [client] To create a production build, use npm run build. [client]
-
In a separate terminal run the following command. You should be asked to provide your admin credentials:
minikube tunnel $ minikube tunnel -p $(basename $PWD) ❗ The service ingress-nginx-controller requires privileged ports to be exposed: [80 443] 🔑 sudo permission will be asked for it. 🏃 Starting tunnel for service ingress-nginx-controller. [sudo] password for cheslijones:
-
Now, in your browser of choice, navigate to
http://localhost
. -
If everything is working, you should be greeted by the running
create-react-app
application.
Great! So we know our application is being deployed correctly into minikube
by skaffold
. You can press CTRL + C
to stop the cluster from running and skaffold
will automatically stop the cluster.
Of course, if you try to go to https://localhost
you'll be greeted with this:
Now we need to add the TLS certificate to address this error, and more importantly to encrypt our data in transit to external APIs.
NOTE: The instructions are slightly different for WSL2.
For WSL2, you do need to use choco
to install mkcert
into Windows itself with cmd
or PowerShell
and not in WSL2.
mkcert
, as the repo says, is a "... simple zero-config tool to make locally trusted development certificates with any names you'd like." It is, and does, just that.
Please refer to the documentation for instructions for your OS.
NOTE: The instructions are slightly different for WSL2.
For WSL2, you need to run mkcert -install
in cmd
or PowerShell
which will add the certificates to C:\Users\<username>
. You then need to copy these into your root project in WSL2.
Once installed, all you should need to do is the following:
$ mkcert -install
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
The local CA is now installed in the Firefox trust store (requires browser restart)! 🦊
$ mkcert localhost 127.0.0.1 ::1
Created a new certificate valid for the following names 📜
- "localhost"
- "127.0.0.1"
- "::1"
The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem" ✅
You'll see that localhost+2-key.pem
and localhost+2.pem
have been created in your project's root directory.
Next, we need to install cert-manager
using one of the following commands:
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
Run the following command to make sure the related Pods have been created and do not continue until they have.
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-86548b886-wcl6r 1/1 Running 0 29s
cert-manager-cainjector-6d59c8d4f7-hws49 1/1 Running 0 29s
cert-manager-webhook-578954cdd-hn749 1/1 Running 0 29s
When READY
is 1/1
for all three, you can continue.
The .pem
files that were created need to be added to our k8s
cluster secrets:
kubectl create secret tls tls-localhost-dev --key=localhost+2-key.pem --cert=localhost+2.pem
You should see the following if successful:
secret/tls-localhost-dev created
DO NOT COMMIT THESE. Either add *.pem
to .gitignore
or just delete them as they are not needed after the previous step.
We need to apply the tls.yaml
manifest to our cluster now so that the certificate can be aquired and added to all traffic:
kubectl apply -f k8s/tls.yaml
If successful, you should see:
issuer.cert-manager.io/letsencrypt-dev-issuer created
certificate.cert-manager.io/letsencrypt-dev-certificate created
Run skaffold dev
again.
Navigate to localhost
and if everything was done correctly, you should automatically be directed to https://localhost
and have no warning messages from your browser.
Please create an issue if you have any questions, issues running the repo, or have feedback on how I can improve this repo or correct something that is wrong. I'm always looking for ways to improve.