Post

My NAS - Part 2

Dynamic DNS, ACME and SSL Web Servers

My NAS - Part 2

Define Our Variables

We’re going to define a few variables to make this easier for you. Replace the value of each variable before continuing:

1
2
3
4
5
6
export DOMAIN=example.com
export USERNAME=username
export PASSWORD=password
export DYNU_CLIENT_ID=REDACTED
export DYNU_SECRET=REDACTED
export EMAIL=username@example.com

Dynamic DNS (DDNS) service

I signed up for free domain name via DynU. You can too! The DDNS service that I am listing in this part of my NAS series uses DynU as the DDNS host. Alternative hosts require a different configuration than what is shown.

Let’s install the DDNS service, as well as the LUCI app:

1
apk add luci-app-ddns bind-host

Because my NAS doesn’t sit at the gate of my home network like my router does, I can’t rely on using the IP address of an interface for the dynamic DNS address. So I have to tell the service to check http://checkip.dyndns.com instead. With this in mind, we need to configure the service, then restart the service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
uci -q del ddns.global.upd_privateip
uci set ddns.global.ddns_rundir='/var/run/ddns'
uci set ddns.global.ddns_logdir='/var/log/ddns'
uci -q del ddns.myddns_ipv4
uci -q del ddns.myddns_ipv6

uci set ddns.Home=service
uci set ddns.Home.enabled='1'
uci set ddns.Home.lookup_host=${DOMAIN}
uci set ddns.Home.domain=${DOMAIN}
uci set ddns.Home.username=${USERNAME}
uci set ddns.Home.password=${PASSWORD}
uci set ddns.Home.use_https='1'
uci set ddns.Home.cacert='/etc/ssl/certs'
uci set ddns.Home.use_logfile='0'
uci set ddns.Home.check_interval='1'
uci set ddns.Home.update_url='http://api.dynu.com/nic/update?hostname=[DOMAIN]&myip=[IP]&username=[USERNAME]&password=[PASSWORD]'
uci set ddns.Home.force_interval='2'
uci set ddns.NAS.ip_source='web'
uci set ddns.NAS.ip_url='http://checkip.dyndns.com'
uci set ddns.Home.use_syslog='2'
uci set ddns.Home.check_unit='minutes'
uci set ddns.Home.force_unit='minutes'
uci set ddns.Home.retry_unit='seconds'
uci commit
service ddns restart

Get a SSL Certificate from Let’s Encrypt

Let’s install the ACME program and LUCI app:

1
apk add luci-app-acme acme-acmesh-dnsapi

We need to rebuild the ACME configuration file, since it contains garbage that I don’t want in the firmware I is building. Once completed, the ACME service needs to be restarted.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cp /dev/null /etc/config/acme
uci set acme.server=acme
uci set acme.server.account_email=${EMAIL}
uci set acme.server.debug='0'
export SECTION=${DOMAIN//\./_}
uci set acme.${SECTION}=cert
uci set acme.${SECTION}.enabled='1'
uci add_list acme.${SECTION}.domains=${DOMAIN}
uci add_list acme.${SECTION}.domains='*.'${DOMAIN}
uci set acme.${SECTION}.validation_method='dns'
uci set acme.${SECTION}.dns='dns_dynu'
uci add_list acme.${SECTION}.credentials='Dynu_ClientId="'${DYNU_CLIENT_ID}'"'
uci add_list acme.${SECTION}.credentials='Dynu_Secret="'${DYNU_SECRET}'"'
uci commit
service acme restart
service acme renew

Replace UHTTPD with NGINX

Let’s remove UHTTPD from the router and install NGINX. It’s going to be necessary to do some of the things I want to do:

1
2
opkg remove luci-light uhttpd-mod-ubus luci luci-ssl --force-removal-of-dependent-packages
opkg install nginx-full nginx-mod-luci luci-nginx luci-nginx

The initial /etc/config/nginx file contains templates and stuff that I don’t need. Let’s start over with that file!

1
2
3
cp /dev/null /etc/config/nginx
uci set nginx.global=main
uci set nginx.global.uci_enable='true'

We’ll define an default HTTP server that redirects HTTP requests to HTTPS:

1
2
3
4
5
uci set nginx.http_default=server
uci add_list nginx.http_default.listen='80 default_server'
uci add_list nginx.http_default.listen='[::]:80 default_server'
uci set nginx.http_default.server_name='_redirect2ssl'
uci set nginx.http_default.return='302 https://$host$request_uri'

We’ll define an HTTP server that redirects http://openwrt/, http://openwrt.lan, and http://openwrt.local to https://admin.example.com:

1
2
3
4
5
uci set nginx.http_router=server
uci add_list nginx.http_admin.listen='80'
uci add_list nginx.http_admin.listen='[::]:80'
uci set nginx.http_admin.server_name='openwrt.lan openwrt.local openwrt'
uci set nginx.http_admin.return='302 https://admin.'${DOMAIN}'$request_uri'

Default HTTPS redirects HTTPS requests to https://admin.example.com:

1
2
3
4
5
6
7
8
9
10
11
12
uci set nginx.https_default=server
uci add_list nginx.https_default.listen='443 ssl default_server'
uci add_list nginx.https_default.listen='[::]:443 ssl default_server'
uci set nginx.https_default.server_name='_lan'
uci add_list nginx.https_default.include='restrict_locally'
uci add_list nginx.https_default.include='conf.d/*.locations'
uci set nginx.https_default.ssl_certificate='/etc/acme/'${DOMAIN}'_ecc/'${DOMAIN}'.cer'
uci set nginx.https_default.ssl_certificate_key='/etc/acme/'${DOMAIN}'_ecc/'${DOMAIN}'.key'
uci set nginx.https_default.ssl_session_cache='shared:SSL:32k'
uci set nginx.https_default.ssl_session_timeout='64m'
uci set nginx.https_default.access_log='off; # logd openwrt'
uci set nginx.https_default.return='302 https://admin.'${DOMAIN}'$request_uri'

Let’s define the HTTPS server serving LUCI (https://admin.example.com):

1
2
3
4
5
6
7
8
9
10
11
uci set nginx.https_router=server
uci add_list nginx.https_admin.listen='443 ssl'
uci add_list nginx.https_admin.listen='[::]:443 ssl'
uci set nginx.https_admin.server_name='admin.'${DOMAIN}
uci add_list nginx.https_admin.include='restrict_locally'
uci add_list nginx.https_admin.include='conf.d/*.locations'
uci set nginx.https_admin.ssl_certificate='/etc/acme/'${DOMAIN}'_ecc/'${DOMAIN}'.cer'
uci set nginx.https_admin.ssl_certificate_key='/etc/acme/'${DOMAIN}'_ecc/'${DOMAIN}'.key'
uci set nginx.https_admin.ssl_session_cache='shared:SSL:32k'
uci set nginx.https_admin.ssl_session_timeout='64m'
uci set nginx.https_admin.access_log='off; # logd openwrt'

We need to commit these changes and restart NGNIX:

1
2
uci commit
service nginx restart

Adding a 403 error handler to NGINX

Gandalf

I’ve minified my version of the (IMHO: silly) error 403 html page from CodePen. Let’s download it for our router:

1
2
3
FILE=/www/error_403.html
wget https://xptsp.github.io/assets/files/error_403.template -O ${FILE}
echo ${FILE} >> /etc/sysupgrade.conf

We also need a 403 error handler block for NGINX. It converts any 403 error into a 404 error:

1
2
3
4
5
6
7
8
9
10
FILE=/etc/nginx/conf.d/error_403.locations
cat << EOF > ${FILE}
error_page 403 =404 /error_403.html;
location = /error_403.html {
	root /www;
	allow all;
	internal;
}
EOF
echo ${FILE} >> /etc/sysupgrade.conf

Restart NGINX for it to show up when unauthorized access occurs:

1
service nginx restart

Summary

Now we have a NAS web server that encrypts all communication by default when using the proper domain name. Yay!

Additional Information

This post is licensed under CC BY 4.0 by the author.

Trending Tags