local certs with reverse proxy (mkcert + nginx)
12.08.2023Creating local certs with mkcert
Why do this?
TLDR:
- Add a layer of security
- Make browsers happy
- Get rid of warnings once and for all (passwords, etc. Idk)
- That unlock icon in the address bar becomes a nice locked icon
- For learning purposes
Installing mkcert
for Linux
sudo apt install libnss3-tools
Install homebrew if it isn’t installed already:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Add to PATH
:
(echo; echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"') >> /home/<YOUR-USERNAME>/.zprofile
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
Install dependency stuff as root
: sudo apt-get install build-essential
Finally, brew install mkcert
.
Creating the cert
Straight from the docs:
$ 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)! 🦊
Create the cert:
mkcert home.arpa "*.home.arpa"
Created a new certificate valid for the following names 📜
- "home.arpa"
- "*.home.arpa"
Reminder: X.509 wildcards only go one level deep, so this won't match a.b.home.arpa ℹ️
The certificate is at "./home.arpa+1.pem" and the key at "./home.arpa+1-key.pem" ✅
It will expire on 12 November 2025
Nice. Next we need to create the nginx
entries. I knew I was going to have to do this multiple times, so…
Convenience script to generate reverse proxy
#!/bin/bash
while getopts "d:i:p:c:k:" opt; do
case $opt in
d) domain="$OPTARG" ;;
i) ip="$OPTARG" ;;
p) port="$OPTARG" ;;
c) cert_path="$OPTARG" ;;
k) key_path="$OPTARG" ;;
esac
done
if [ -z "$domain" ] || [ -z "$ip" ] || [ -z "$port" ] || [ -z "$cert_path" ] || [ -z "$key_path" ]; then
echo "Usage: $0 -d <domain> -i <ip> -p <port> -c <certificate_path> -k <key_path>"
exit 1
fi
nginx_config="/etc/nginx/sites-available/$domain"
# Define color codes
YELLOW="\033[33m"
RESET="\033[0m"
# Text
UNDERLINE=$(tput smul)
# Pretty print stuff
printf "${YELLOW}${UNDERLINE}Configuration Summary${RESET}\n"
printf "${YELLOW}Domain:${RESET} $domain\n"
printf "${YELLOW}IP:${RESET} $ip\n"
printf "${YELLOW}Port:${RESET} $port\n"
printf "${YELLOW}Certificate Path:${RESET} $cert_path\n"
printf "${YELLOW}Key Path:${RESET} $key_path\n"
read -p "Continue (y/n)? " CONT
CONT=${CONT,,} # Convert to lowercase
if [ "$CONT" = "y" ]; then
echo "server {
listen 80; # listen on IPv4
listen [::]:80; # listen on IPv6
server_name $domain;
location / {
proxy_pass http://$ip:$port;
include proxy_params;
}
listen 443;
listen [::]:443;
ssl_certificate $cert_path;
ssl_certificate_key $key_path;
}" >"$nginx_config"
ln -s "$nginx_config" "/etc/nginx/sites-enabled"
systemctl reload nginx
echo "nginx configuration created for $domain at $nginx_config"
else
exit 0
fi
Example usage:
sudo ./genproxy.sh -d site.home.arpa -i 192.xxx.x.xxx -p 3000 -c /path/to/home.arpa+1.pem -k /path/to/home.arpa+1-key.pem
Local DNS entry in /etc/hosts
# .arpa
192.xxx.x.xxx home.arpa
# End of section
I still don’t have local DNS set-up, and I might never get around to it.
If you’re accessing your sites / services from the same machine, that should be all there is to it. However, in my case, I’m using Linux as a server and want to access my services from my Windows machine. So… extra steps.
Installing CA on another machine
Move the .pem
file from host to target:
rsync -avzh /home/<YOUR-USERNAME>/.local/share/mkcert/rootCA.pem wsl:/home/<YOUR-USERNAME>/.local/share/mkcert
Now it exists on the target machine. mkcert -install
and curl
shows that it works successfully, but in order to have browsers recognize the cert - we need to install it for Windows (because the previous step was for WSL).
Installing on Windows
First, choco install mkcert
Next, run mkcert -CAROOT
to determine where the CA files should be stored. It will be something like C:\Users\<YOUR-USERNAME>\AppData\Local\mkcert
.
Move the .pem
file from WSL to Windows filesystem:
cp /home/<YOUR-USERNAME>/.local/share/mkcert/rootCA.pem /mnt/c/Users/<YOUR-USERNAME>/AppData/Local/mkcert/
On Powershell
(or whatever equivalent):
mkcert install
- Click on “Yes”
Based on my testing, this should be enough for Brave and Chrome. However, it seems we need to manually install the cert for Firefox.
Manual import of .pem
in Firefox
- On the top right, click on the hamburger icon > Settings
- Privacy & Security > Certificates > View Certificates
- Click on “Import” and add the
.pem
manually
References