0xB752 ddwrt + custom certificate

When I first installed dd-wrt (no support from openwrt) on my router, I enabled HTTPS-only access for the web UI.
When the nasty prompt from the self-signed certificate popped up, I looked for an option to upload my own cerficate (signed by the internal CA I use).
There was none…

Left it be for quite soem time, but finally decided to sort it out and, to my surprise, there’s no official documentation on it (or not easy to find, at least)…

Found this thread on their forum which basically set two options:

  • using firmware mod kit to embed your cert into the image before flashing (no need to recompile at least…)
  • enable JFFS, save the cert (and key) there and use a startup script to mount -o bind those on the default /etc/cert.pem and /etc/key.pem locations

I found it hard to believe that dd-wrt had no GUI setting for a custom SSL cert, even more that there wouldn’t be some nvram setting to store one, at least using ssh…

Both options would be ok, but I decided to take a quick look at dd-wrt code and found this:

#if defined(HAVE_OPENSSL) || defined(HAVE_MATRIXSSL) || defined(HAVE_POLARSSL)
		char *cert = nvram_safe_get("https_cert");
		char *key = nvram_safe_get("https_key");
		char *certfile = NULL;
		char *keyfile = NULL;
		if (*cert) {
			certfile = "/tmp/https_cert";
			writenvram("https_cert", certfile);
		}
		if (*key) {
			keyfile = "/tmp/https_key";
			writenvram("https_key", keyfile);
		}
		if (!certfile)
			certfile = nvram_safe_get("https_cert_file");
		if (!*certfile)
			certfile = CERT_FILE;
		if (!keyfile)
			keyfile = nvram_safe_get("https_key_file");
		if (!*keyfile)
			keyfile = KEY_FILE;
#endif

Taking a look at blame it points to rev 44703, so sort of recent.

It seems before it already supported specifying a custom location but only if was compiled with HAVE_CUSTOMSSLCERT 🤷

#ifdef HAVE_CUSTOMSSLCERT
		if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM)
		if (SSL_CTX_use_certificate_file(ctx, nvram_safe_get("https_cert_file"), SSL_FILETYPE_PEM)
#else
		if (SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM)
#endif

Anyway, I haven’t found these new nvram settings documented anywhere, so I’ll highlight them here:

  • https_cert / https_key - set them to the raw content of a cert / key and done, no need to store them anywhere in the router filesystem (so it does not require JFFS enabled)
  • https_cert_file / https_key_file - set them to the paths to each respective file, so you’ll have to enable JFFS (or embed in the image, though that wouldn’t make much sense - just overwrite the default one). I assume this might be helpful if Let’s Encrypt (or any other form of short-lived certs) are used and it’s easier to replace a file that push to nvram. Or maybe if a very long chain of trust is used in the cert…?

I went with the cleanest option of putting cert and key directly in nvram:

  • generate certificate with minica
minica -ca-cert .. -ca-key .. -domains myrouter.internal -ip-addresses 192.168.1.1
  • ssh to router and set the nvram keys:
$ nvram set https_cert='-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----'
$ nvram set https_key='-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
  • reload web UI
$ stopservice httpd
$ startservice httpd