IoT devices on the home network rely on the router to be accessible from the internet. It functions as a remote proxy and translates between HTTPS used on the internet and HTTP on the local area network.
Wildcard CNAME record
A CNAME record maps a domain name to another domain name. In this case the “other” domain name is the DDNS name of our router. By using a wildcard CNAME, such as
*.home.domain.com, we can match requests for non-existent sub-domain domain names to
home.domain.com. If we would open
https://ir.home.domain.com in a browser, the DNS server will give the IP address of
home.domain.com to the browser. The browser will then sent an HTTP request to that IP address requesting the page
Add the CNAME records to your DNS configuration
home.domain.com 3600 IN CNAME dyndns.com *.home.domain.com 3600 IN CNAME dyndns.com
Remember to replace
domain.com with your domain name, and
dyndns.com with the dynamic DNS address of your router.
Access Router (pound)
The wildcard CNAME is only part of the equation. Since we configured
home.domain.com to map to the IP address of the access router, this router will need to forward HTTP requests to the real IR device on our home network. We do this using a reverse proxy.
A reverse proxy uses the
Host: field from the HTTP header, to determine where to forward the request to. This allows a server may use a single IP address or interface to accept requests for multiple DNS host names.
We choose the
pound implementation because it seemed versatile. We installed
pound on the
/jffs partition, so that it will survive firmware updates. This guide is written for a Kong build of DD-WRT, but should work with any firmware that has access to
lighttpd. You will need a JFFS partition and need to use command line tools via ssh/telnet.
- Enable JFFS partition. From the router’s GUI, enable JFFS (Administration » Management). On first use, check the box to “clean internal flash storage”. Reboot as needed.
Pound binaries. Our router’s firmware didn’t include
poundbinaries, so we copied it from an older firmware (kongac’s r33010M)
scp rtr2:/usr/sbin/pound rtr2:usr/sbin/poundctl /jffs/sbin/ scp rtr2:/usr/lib/libssl.so.1.0.0 rtr2:/usr/lib/libcrypto.so.1.0.0 /jffs/lib
Install startup script. Copy the script
/jffs/sbin/pound.shand its helper
Configure port to listen on. The file
/jffs/etc/pound/pound.pt1specifies what port to listen at. The example below uses port 81 since port 80 is already in use for the DD-WRT UI.
TimeOut 120 Alive 30 Control "/tmp/pound.ctl" ListenHTTP Address 0.0.0.0 Port 81
Configure forward rules. The file
/jffs/etc/pound/pound.pt2specifies where to forward the HTTP request to. In the example below, we do route both on URL and Host.
Service "ir-url" # route home.domain.com/ir to 10.0.1.234:80 Url "/ir.*" BackEnd Address 10.0.1.234 Port 80 End End Service "ir-host" # route ir.home.domain.com to 10.0.1.234:80 HeadRequire "Host:.ir.home.domain.com.*" BackEnd Address 10.0.1.234 Port 80 End End End
Give it a spin by pointing a web browser to either
Transport Layer Security
With a way to contact our IR device from the internet, let’s secure the connection using TLS with a free certificates from Let’s Encrypt by loosely following Neilpang’s instructions. We use a slightly modified
acme.sh script that fixes a mistake in the regular expression [ticket968] and to avoid problems with binding to a non-existing IPV6 address, we added
-4 to the call to
openssl. The modified script can be found here.
Get CA certificates using
mkdir /jffs/opt; mount --bind /jffs/opt /opt # mount /opt on /jffs/opt ( nvram get rc_startup; echo "mount --bind /jffs/opt /opt" ) \ > /tmp/a; nvram set rc_startup="$(cat /tmp/a); nvram commit" bootstrap # install opkg (for CA-certificates) opkg update; opkg install ca-certificates # install CA certificates
- Make port 443 available. Disable the existing service on that port (Administration » Web Management » disable HTTPS for web access), and open port 443 on the firewall (Administration » Commands » Firewall » Edit; add the line “iptables -I INPUT 1 -p tcp -m state –state NEW -m tcp –dport 443 -j ACCEPT”).
Try to request a certificate. Remember to replace
domain.comwith your domain name.
cd /jffs/usr/ssl ./acme.sh --issue --tls -d home.domain.com \ --home /jffs/usr/ssl --ca-path /opt/etc/ssl/certs \ --pre-hook "/jffs/sbin/pound.sh stop" \ --post-hook "/jffs/sbin/pound.sh start" --testIf all goes well, remove the
--testparameter and request the certificate for real.
Automatically renew the certificate, by setting up a cron job to update the certificate before it expires (Administration » Management » Additional Cron Jobs).
# sundays @2:02am, renew/install SSL certificates if necessary (restarting pound) 2 2 * * 0 root /jffs/usr/ssl/acme.sh --cron --home /jffs/usr/ssl >/jffs/usr/ssl/cron.log 2>&1
host.pemby Combine the SSL Private Key, the signed x.509 certificate, and all the certificates in the chain of trust in
poundcan use them
./acme.sh --install-cert -d home.domain.com --home /jffs/usr/ssl \ --cert-file /jffs/etc/pound/host.crt \ --key-file /jffs/etc/pound/host.key \ --fullchain-file /jffs/etc/pound/fullchain.crt \ --reloadcmd "cat /jffs/etc/pound/host.key /jffs/etc/pound/host.crt \ /jffs/etc/pound/intermediate-ca.pem \ > /jffs/etc/pound/host.pem ; \ /jffs/sbin/pound.sh restart"
poundfor HTTPS instead of HTTP, by modifying the configuration file
TimeOut 120 Alive 30 Control "/tmp/pound.ctl" ListenHTTPS Address 0.0.0.0 Port 443 Cert "host.pem"
Give it a spin by pointing a web browser to either