Background
My home server’s got quite a few services running on it. I wanted to set up an NGINX reverse proxy so I can give each of the services a memorable hostname instead of remembering port numbers. Additionally, I wanted to ensure that encryption was set up to prevent any nefarious devices on the network from snooping on the traffic and stealing my data.
I did some research on setting Certbot to use the NameCheap API for renewal, which turns out isn’t officially supported. Most of my personal stuff runs on AWS, so I figured I’d point the DNS nameservers there for one of my home network’s domain. I use dynamic DNS to set keep an A record updated on the domain pointing to my home network for remote access.
Then, I could also use Certbot’s Route53 plugin, to automatically generate a wildcard cert for my domain. It would allow me to prevent leaking the names of all my services by using a wildcard, and make it easier to manage.
Certbot Setup
First, I installed on the server Certbot via snap.
# install
sudo snap install --classic certbot
# symlink to /usr/bin
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# configure plugins
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-route53
You need to make sure your AWS credentials are set up correctly for the Route53 integration to work. I’d recommend doing this by first installing the awscli
package, and then running aws configure
to set up your credentials under the default profile in the root user.
A few notes on this setup:
- since Certbot runs as root, you must run
sudo aws configure
to ensure you set credentials up in the root directory and not your user home directory. - The Route53 plugin doesn’t currently accept custom profile names, so it must be under the
default
profile. This is kind of a bummer.
Next, I then ran the following command to generate a wildcard cert for my domain:
sudo certbot certonly --dns-route53 -d '*.home.example.com'
Nginx
I use docker for all the major services on my server. Nginx is no exception, so I updated my existing nginx config to have separate virtual hosts for each service.
Main Configuration
worker_processes auto;
events { worker_connections 1024; }
http {
server_names_hash_bucket_size 64;
include /etc/nginx/sites-enabled/*;
}
Example site configuration
For my homebridge install, this is the configuration file I’m using:
sites-enabled/homebridge.home.example.com
server {
charset UTF-8;
listen [::]:443 ssl;
listen 443 ssl;
server_name homebridge.home.example.com;
# Certbot keys
ssl_certificate /etc/letsencrypt/live/home.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;
# SSL config
include /etc/nginx/ssl.conf;
ssl_dhparam /etc/nginx/dhparams.pem;
location / {
proxy_pass http://127.0.0.1:8581/;
}
}
Running the server
There’s a couple prerequisites. Since we’re not letting Certbot manage the ssl configuration, we need to download the recommended arguments and also the Diffie-Hellman parameters from Mozilla.
cd $HOME/nginx
# get latest ssl config
curl https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > ssl.conf
# get dhparams
curl https://ssl-config.mozilla.org/ffdhe2048.txt > dhparams.pem
Now, we can actually run the server. I’m using the --network host
flag to bind to the host network, which is needed to easily connect to the upstream services which forward ports from their containers.
One thing to note - I’ve got all the nginx config stored in my home directory under nginx/
in this example, so customize the paths to match your setup.
docker run -p 443:443 --name nas-nginx \
--network host \
-v $HOME/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v $HOME/nginx/ssl.conf:/etc/nginx/ssl.conf:ro \
-v $HOME/nginx/dhparams.pem:/etc/nginx/dhparams.pem:ro \
-v $HOME/nginx/sites-enabled:/etc/nginx/sites-enabled:ro \
-v /etc/letsencrypt/:/etc/letsencrypt/:ro \
-d nginx
One thing to note: I’m mounting all the configuration files & the LetsEncrypt folder as read-only via :ro
. This is because I don’t want the container to be able to modify the config files.
If you update the config files, you can just run docker restart nas-nginx
to apply the changes.
Additional bind mounts for file server
I have a virtual host setup that is just a file server off of the raid array. To enable this, I add these flags when running the container.
To password protect the site, I also add a htpasswd file which you can generate with htpasswd -c .htpasswd user
. I put this file in my home directory under nginx/
, and mount it as read-only in the container to allow the config to access it.
These are added with the other bind commands in the run command above
-v /mnt/raid1:/usr/share/nginx/html:ro \
-v $HOME/nginx/.htpasswd:/etc/nginx/.htpasswd:ro \
Nginx conf for file server
I use this config to serve the files, along with indexes & the basic http auth.
server {
charset UTF-8;
listen [::]:443 ssl;
listen 443 ssl;
server_name files.home.example.com;
# Certbot keys
ssl_certificate /etc/letsencrypt/live/home.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;
# SSL config
include /etc/nginx/ssl.conf;
ssl_dhparam /etc/nginx/dhparams.pem;
location / {
root /usr/share/nginx/html;
autoindex on;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
include /etc/nginx/mime.types;
}
}
DNS
You’ll also need to point your chosen wildcard subdomain to your home server. The quickest solution is to just to give your server a static IP on your local network, then add a wildcard A
record to your domain.
For example, you could add the following as a DNS record:
Type | Name | Value |
---|---|---|
A | *.home.example.com | 192.168.1.123 |
You could also use something like bind9 to set up a local DNS server to direct clients on your LAN to the correct IP address within your network.