Created
November 30, 2025 23:20
-
-
Save ljack/37e407ac834e70d9e792f1e2d7a2e851 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| π Creating UniFi-Friendly TLS Certificates Using mkcert | |
| UniFi devices (UDM/UDM-SE/CloudKey) are unusually strict about TLS certificates. | |
| They require: | |
| A valid Common Name (CN) | |
| Correct Subject Alternative Names (SAN) | |
| A matching private key | |
| No missing SANs (or the UI will break) | |
| This guide shows how to create a fully working UniFi certificate using mkcert plus some OpenSSL refinements. | |
| 1. Install mkcert | |
| macOS: | |
| brew install mkcert nss | |
| mkcert -install | |
| Linux: | |
| sudo apt install mkcert | |
| mkcert -install | |
| mkcert installs its local CA into your trust store. | |
| 2. Generate a UniFi-compatible certificate | |
| UniFi requires a CN that looks like a public hostname, even if itβs internal. | |
| Example recommended CN: | |
| unifi.internal.test | |
| Generate a certificate with proper SANs: | |
| mkcert unifi.internal.test unifi.home.arpa dream-machine-se.local 10.0.0.1 | |
| This produces: | |
| unifi.internal.test.pem β certificate | |
| unifi.internal.test-key.pem β private key | |
| CN is the first argument, SAN contains all listed names. | |
| 3. (Optional) Fix or re-sign certificates using OpenSSL | |
| If mkcert omitted the CN or you want to adjust SANs without generating a new key, you can re-sign the certificate yourself. | |
| Step 3.1 β Create an OpenSSL config | |
| unifi-cert.conf: | |
| [ req ] | |
| default_bits = 2048 | |
| prompt = no | |
| default_md = sha256 | |
| distinguished_name = dn | |
| req_extensions = v3_req | |
| [ dn ] | |
| CN = unifi.internal.test | |
| [ v3_req ] | |
| keyUsage = critical, digitalSignature, keyEncipherment | |
| extendedKeyUsage = serverAuth | |
| subjectAltName = @alt_names | |
| [ alt_names ] | |
| DNS.1 = unifi.internal.test | |
| DNS.2 = unifi.home.arpa | |
| DNS.3 = dream-machine-se.local | |
| IP.1 = 10.0.0.1 | |
| Step 3.2 β Create a CSR from the key | |
| openssl req \ | |
| -new \ | |
| -key unifi.internal.test-key.pem \ | |
| -out unifi.csr \ | |
| -config unifi-cert.conf | |
| Step 3.3 β Re-sign using mkcert CA | |
| Find mkcert CA: | |
| CAROOT=$(mkcert -CAROOT) | |
| Sign: | |
| openssl x509 \ | |
| -req \ | |
| -in unifi.csr \ | |
| -CA "$CAROOT/rootCA.pem" \ | |
| -CAkey "$CAROOT/rootCA-key.pem" \ | |
| -CAcreateserial \ | |
| -out unifi-final.pem \ | |
| -days 825 \ | |
| -sha256 \ | |
| -extfile unifi-cert.conf \ | |
| -extensions v3_req | |
| Now you have: | |
| unifi-final.pem β corrected certificate | |
| Same private key as before | |
| This version includes a valid CN and correct SANs. | |
| 4. Install into UniFi | |
| UI method: | |
| Settings β System β Advanced β Device SSH & Certificates β Custom Certificate | |
| Upload: | |
| Certificate β unifi-final.pem | |
| Key β unifi.internal.test-key.pem | |
| Then: | |
| unifi-os restart | |
| Your controller will now serve fully trusted HTTPS + WSS on all SAN hostnames. | |
| 5. Verify certificate + key match | |
| Certificate modulus: | |
| openssl x509 -in unifi-final.pem -noout -modulus | |
| Key modulus: | |
| openssl rsa -in unifi.internal.test-key.pem -noout -modulus | |
| If modulus values match β certificate + key are a valid pair. | |
| βοΈ Result | |
| You now have a UniFi-friendly TLS setup that: | |
| Works for multiple internal hostnames | |
| Fully satisfies Chrome/Safari/Firefox | |
| Avoids UniFiβs CN restrictions | |
| Ensures WebSockets (wss://.../events) work without warnings | |
| Is easily renewable with mkcert or OpenSSL |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment