Categories
Tech

Run a home server at subdomain.yoursite.com

As we discussed here, you may host your website on an inexpensive VPS with a static public IP address. Most VPS providers charge a few euro per month. However, the machines they provide are pretty basic, suitable for a simple static website or even a CMS like WordPress but unfit for more demanding purposes. In this article, I will try to list some alternative ways to use a home server (like an old dismissed PC or a raspberry pi) the same way you would use that VPS.

Why should I use a local machine?

There may be many reasons to host a web service on a home server:

  • you have got some recycled or cheap hardware which is way better than what is offered by your VPS provider (especially in terms of disk space);
  • you want your data to be stored in a safe place, where nobody except for you has physical access;
  • you need a custom hardware configuration to ensure reliability and prevent data loss (e.g. RAID 5);
  • you could use a faster connection from your LAN, when you are connecting from home;
  • a combination of the above, and many more.

What do I need to make my server available on the internet?

There are many ways to allow clients to find your server from anywhere on the internet. You may choose one of these options according to the service(s) you own and the one(s) you are willing to acquire.

Your IP address is dynamic
Your IP address is dynamic
No
No
Yes
Yes
No
No
Yes
Yes
Are you behind a NAT?
Are you behind a NAT?
Your IP address is public
Your IP address is public
Set up a VPN server on your VPS; your home server must connect to VPN as client
Set up a VPN server on you…
Register a DNS record and set port forwarding
Register a DNS record and…
Yes
Yes
No
No
Have you got your own DNS server?
Have you got your own DNS server?
Register a record with a short TTL, and update it periodically
Register a record with a s…
You need a VPS with a public static IP address. Register *.yoursite.com to point to your VPS
You need a VPS with a publ…
Configure nginx to forward all incoming traffic for subdomain.yoursite.com to your home server via VPN
Configure nginx to forward…
Configure nginx to forward all incoming traffic for subdomain.yoursite.com to your public dynamic IP address (using a local resolver)
Configure nginx to forward…
Option 1
Option 1
Option 2
Option 2
Option 3
Option 3
Option 4
Option 4
Have you got a public static IP address at home?
Have you got a public static IP address at home?
Viewer does not support full SVG 1.1

In the flowchart above, if option n is feasible then you may opt for all options m, where m > n. To help you navigate the flowchart and understand the pros and cons of each option, here is a comparison of these solutions.

  1. Public static IP address, DNS record and port forwarding
  2. Public dynamic IP address, host your own public DNS server on a VPS
  3. Public dynamic IP address, host a local private resolving service on a publicly-available VPS
  4. You are behind a NAT, use a tunneled connection to the VPS and redirect all traffic through it
Your home IP addressVPSVPNDNS
1Public staticExternal
2Public dynamicYESOwn, public
3Public dynamicYESOwn, local
4Dynamic and behind NATYESYES
Requirements of each option
ProsCons
1Easiest to set up, always available, managed DNS records, direct connectionObtaining a public static IP address is not always feasible
2No need to have a public static IP address, direct connection with clientsVPS needed, need to run a DNS server yourself, some ISPs won’t give you a public IP address
3No need to have a public static IP address or to run a public DNS serverVPS needed, indirect connection with clients (all traffic must be forwarded), a dynamic public IP address is still needed
4No need to have a public static or dynamic IP address or to run a DNS server; always availableVPS needed, own VPN needed, indirect connection with clients (all traffic must be forwarded)
Pros and cons of each option

Option 1

Flowchart representing option 1: the clients query external DNS servers to resolve subdomain.yoursite.com. They get w.x.y.z, so they contact your router that forwards incoming requests to your home server via port forwarding.

Obtaining a public static IP address is not so simple and cheap: since IPv4 addresses are limited in number (232 total addresses, i.e. about 4 billion) and many have already been taken, internet service providers (ISPs) usually assign dynamic addresses to their clients, and they often put many routers behind the same NAT, which means that even knowing the IP address of your machine at this moment does not make it available at that address from the outside. Things might change as soon as IPv6, with its 2128 addresses, takes over, but that moment is not scheduled yet.

However, if you happen to own a public static IP address for your home connection, you may run your home server at subdomain.yoursite.com with ease.

  1. First thing to do is to register DNS records associating your (sub)domain with your public static address. There are many hosting providers willing to sell you a domain, each of them with slightly different conditions. In general, you just need to associate a (sub)domain with the corresponding address, and this should be very cheap (10 – 15 euro per year at the time of writing). Additional services which may be useful but not necessary are DNS flood protection, redundancy on many DNS servers, IPv6-reachable DNS servers. Other services will increase the cost but are not impacting on the purpose of this small guide.
    • A record: *.yoursite.com > w.x.y.z
    • AAAA record: *.yoursite.com > a:b:c:d:e:f:g:h
    • You may want to register specific subdomain(s), e.g. www servicename othername. This is not necessary, since the wildcard record (*.) will include all subdomains of yoursite.com
  2. Then you have to set up your router to allow incoming connections and forward the ports of interest (typically 22 for SSH/SFTP, 80 for HTTP and 443 for HTTPS).
    • Go to your router configuration page (usually it is located at http://192.168.0.1 or http://192.168.1.1, but each router may have a different gateway address).
    • Allow incoming connections to the desired ports (there may be a “firewall” section in your router configuration page).
    • Assign a static address on the local network to your server: go to the MAC-IP Bind section, identify your server’s MAC and bind it to an address of your choice. Every time the home server will connect to the router, the latter will assign the same IP address to the former.
    • Set up port forwarding: the incoming traffic on port x will be redirected to your home server (there should be a “port forwarding” section in your router configuration page).
  3. Make sure to open the required ports on your server; if you are running Ubuntu server and use ufw as firewall, to open port 22 you have to type ufw allow 22.

When a user targets subdomain.yoursite.com, they will resolve the IP address of your router, which will forward the request to your server. Different subdomains may point to different routers, and each router may forward different ports to different machines; on the same machine, a web server (like nginx or apache) may respond to the requests or act as a reverse proxy and forward different (sub)domains to different sockets, local services or even other machines.

The traffic between the clients and your home server, after the initial DNS query (which may be cached), will be direct (except for the usual routing process) and whenever your machine is running, your router is connected to the internet and the DNS servers are working properly, your machine will be available at subdomain.yoursite.com

Option 2

Flowchart representing option 2: the clients query external DNS servers to resolve *.ys.com and query your public DNS nameserver to resolve sd.ys.com. They get w.x.y.z, so they contact your router that forwards incoming requests to your home server via port forwarding.

If you are a private customer, your ISP won’t probably sell you a public static IP address even if you are willing to pay for it. But if you are lucky enough to be assigned a public dynamic IP address, you may follow these instructions to make your home server available at subdomain.yoursite.com.

  1. Check that your IP is really public: visit your router configuration page and check that your WAN IP address does not start with 192, 10, 172, 1 or 255. Also check that the WAN IP is the same as shown by external services e.g. https://whatismyipaddress.com/. If so, congratulations! Otherwise, check out option 4.
  2. You need a VPS with a public static IP address; the cheapest machine will do, it will just work as a DNS authoritative nameserver for your domain and point to your home server. Beware that hosting your own nameserver exposes you to DNS flood attacks (among others); you should update your software, configure your firewall and be careful; however, if your project is just a little private initiative and you have not much to lose this way, this is not a big deal.
  3. You must register your VPS as authoritative DNS nameserver for your domain; make sure your domain name registrar lets you configure your own DNS servers before buying the domain
    • Example configuration: dns1.yoursite.com > m.n.o.p; dns2.yoursite.com > m.n.o.p (the registrar often requires at least two distinct DNS servers, but they may be located at the same IP address on the same machine). m.n.o.p must be the static public IP address of your VPS (you may register your IPv6 address as well).
  4. Set up a DNS authoritative nameserver on your VPS. If you aren’t familiar with that, see the section below.
  5. Make your home server update the DNS record automatically. The update interval must be similar to the time to live (TTL) of the record. The shortest these intervals, the shortest gap between IP changes and DNS record updates but the highest load of word for your DNS nameserver. Please note that there will always be a span of time during which your server will be unavailable when your IP changes. However, this should not occur so often (but it depends on your ISP and there is nothing you can do about it). If you don’t know how to do it, here is an example.
  6. Set up port forwarding (see above for details)

When a user targets subdomain.yoursite.com, they will be told the IP address of your VPS that is running a DNS service, which will provide a DNS record with a short TTL pointing to the current public IP address of your router at home. The router will forward all incoming traffic to your home server, which will continuosly update the DNS record on your VPS.

The traffic between the clients and your home server will be direct, but given the short time to live of the DNS records the clients will have to make many more DNS queries. Your machine will be available at subdomain.yoursite.com, except for the gaps between IP change, DNS update and previous record expiry.

Set up an authoritative DNS nameserver

The following instructions have been followed on Ubuntu server 20.04 LTS, but they should work for other distributions and operative systems with the due adaptations. This is meant to guide an inexpert user, if you can run a DNS server in any other way you may skip this section.

In the code blocks below, these placeholders are used:

  • m.n.o.p is your VPS public IPv4 address
  • i:j:k:l:m:n:o:p is your VPS public IPv6 address
  • w.x.y.z is your home server IPv4 address (local or public, depending on the DNS server type)
  • a:b:c:d:e:f:g:h is your home server IPv6 address (local or public)
  • yoursite.com is your domain
  • subdomain.yoursite.com is your subdomain
  • In bind9 zone file syntax, you will see some dots after your domain name in some (not all!) circumstances. Beware of dots!
  • In the SOA line, hostmaster.yoursite.com. is an email address (the @ is replaced by a dot in bind9 syntax)
# Install bind9
sudo apt install bind9 bind9-utils

sudo nano /etc/bind/named.conf.options  # See below
ddns-confgen -s yoursite.com  # Create a private key to edit your DNS record remotely. Follow the instructions printed by this command
sudo nano /etc/bind/named.conf.local  # See below
sudo -u bind nano /var/lib/bind/db.yoursite.com  # See below

sudo named-checkconf -z /etc/bind/named.conf  # Check your configuration; fix any error
sudo systemctl start bind9

These are the configuration and zone files mentioned above:

## == /etc/bind/named.conf.options == ##
options {
	directory "/var/cache/bind";
	dnssec-validation auto;
        listen-on    port 53  { any; };  # You can allow specific clients  to make requests
	listen-on-v6 port 53  { any; };  # IPv6 configuration
        allow-query           { any; };  # You may allow only local requests (but your nameserver won't be public then; do this if you just need a local resolver and not a public nameserver)
        recursion             yes;
};
## == /etc/bind/named.conf.local == ##
key "ddns-key.yoursite.com" {
algorithm hmac-sha256;
secret "thisisasecret";
};
zone "yoursite.com." {
type master;
file "/var/lib/bind/db.yoursite.com";
update-policy {
grant ddns-key.yoursite.com wildcard *.yoursite.com. ANY;
};
};
## == /var/lib/bind/db.yoursite.com == ##
$ORIGIN yoursite.com.
$TTL 60 ; time to live in seconds
@ IN SOA dns1.yoursite.com. hostmaster.yoursite.com. (
1 ; Serial number (increment after each manual change)
3600 ; Refresh [1h]
600 ; Retry [10m]
86400 ; Expire [1d]
3600 ) ; Negative Cache TTL [1h]
;
@ IN NS dns1.yoursite.com.
@ IN NS dns2.yoursite.com.
@ IN A w.x.y.z
AAAA a:b:c:d:e:f:g:h
;
dns1 IN A m.n.o.p
AAAA i:j:k:l:m:n:o:p
dns2 IN A m.n.o.p
AAAA i:j:k:l:m:n:o:p
subdomain IN A w.x.y.z
AAAA a:b:c:d:e:f:g:h
* IN A w.x.y.z
AAAA a:b:c:d:e:f:g:h
;

Update your DNS record remotely

On your home server, store the ddns key in a file (e.g. /home/user/ddns/yoursite.key) and create a script like the one below (e.g. /home/user/ddns/run_me.sh). You can create a systemd service or schedule a crontab task to update periodically your public dynamic IP address.

## == /home/user/ddns/run_me.sh == ##
# Store your current IP address in the variable $MYIP
MYIP=$(dig +short myip.opendns.com @resolver1.opendns.com)

# Set the appropriate parameters (see above)
KEY="/home/user/ddns/yoursite.key"
NS=yoursite.com
DOMAIN=subdomain.yoursite.com.
ZONE=yoursite.com.

# Update remotely using the key in keyfile
nsupdate -k $KEY -v << EOF
server $NS
zone $ZONE
update delete $DOMAIN A
update add $DOMAIN 30 A $MYIP
send
EOF
## == /home/user/ddns/yoursite.key == ##
key "ddns-key.yoursite.com" {
	algorithm hmac-sha256;
	secret "thisisasecret";
};
# This must be the same key as root@yourVPS:/etc/bind/named.conf.local
## == /etc/crontab == ##
# Update DNS record every minute forever
# [...] other cron jobs
* * * * *   user    /home/user/ddns/run_me.sh

Option 3

Flowchart representing option 3: the clients query external DNS servers to resolve sd.ys.com and get m.n.o.p, which resolves the subdomain with a local DNS nameserver and forwards incoming requests to your home router, that forwards certain ports to your home server on the LAN.

If you don’t want to run a publicly available DNS server (or maybe you can’t, possibly due to your VPS provider’s firewall rules or due to your registrar’s DNS policies) you can still use a reverse proxy to pass incoming traffic to you locally-resolved (sub)domain. Just like with option 4, all traffic will be redirected through your VPS (causing delays and counting as internet traffic, which is often metered by VPS providers), but unlike option 4 no VPN is required.

  1. Check that your home IP address is public (see above).
  2. You need a VPS with a public static IP address; the cheapest machine will do, it will just work as a local resolver for your subdomains and forward all incoming traffic to your home server.
  3. Register a wildcard DNS record with your domain provider to make all subdomains of yoursite.com point to your VPS. Just follow these instructions, with the public static IP of your VPS (you may run ifconfig on the VPS to get it).
  4. Run a local resolver to make subdomain.yoursite.com point to your home server’s current public IP address. If you don’t know how to do it, I suggest that you follow these instructions, remembering that there is no need to allow every client to query your nameserver, it just has to be available on the very machine it is running on.
  5. Make your home server update the DNS record automatically (see above for an explanation or below for an example).
  6. Make your reverse proxy redirect all the incoming traffic on the ports of interest for a given subdomain to your locally-resolved subdomain. If you need help doing this, see the section below. If a communication is encrypted (e.g. HTTPS on port 443), the decryption should happen only on the target (your home server), the VPS should forward packets as a black box, without even knowing anything about them but their destination.
  7. Set up port forwarding (see above for details)

When a user targets subdomain.yoursite.com, they will be told the IP address of your VPS. A reverse proxy will resolve the destination using a local DNS nameserver and forward all incoming traffic to the address resolved this way. The local nameserver will provide a DNS record with a short TTL pointing to the current public IP address of your router at home. The router will forward all incoming traffic to your home server, which will continuosly update the local DNS record on your VPS.

The traffic between the clients and your home server will be indirect, the VPS acting as a “relay”. Your machine will be available at subdomain.yoursite.com, except for the gaps between IP change, DNS update and previous record expiry.

Redirect traffic with a reverse proxy

These steps were tested using nginx 1.18.0 (compiled with ngx_stream_core_module, available for 1.9+) on Ubuntu server 20.04 LTS, but they could be followed on any OS with any reverse proxy, given the proper adjustments.

In this example, we will build a website with two domains (www.yoursite.com and subdomain.yoursite.com). The first domain will be a static website on the VPS server, the second domain a static website on the home server. The public DNS record will point to the VPS for all subdomains.

### === Configuration on the VPS server === ###
## == /etc/nginx/nginx.conf == ##
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# [...]
stream {
    include /etc/nginx/stream.d/*;
}
http {
        # [...]
	include /etc/nginx/mime.types;
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

## == /etc/nginx/stream.d/forward == ##
resolver 127.0.0.1 valid=1s;  # Replace with the address of your local nameserver; remove if no resolver is needed
map $ssl_preread_server_name $name {
    subdomain.yoursite.com   subdomain.yoursite.com:443;  # Replace with the name of an upstream block or with a static IP address (e.g. a local address or a VPN address)
    default                  backend;
}

upstream backend {
    server 127.0.0.1:4443;
}

server {
    listen      443;
    proxy_pass  $name;
    ssl_preread on;
}

## == /etc/nginx/sites-enabled/yoursite.com == ##
# Redirect HTTP traffic for subdomain to home server
server {
  listen 80;
  listen [::]:80;
  server_name subdomain.yoursite.com;
  location / {
    resolver 127.0.0.1:53;  # Replace with the address of your local nameserver; remove if no resolver is needed
    set $upstream_endpoint http://subdomain.yoursite.com;
    proxy_pass $upstream_endpoint$request_uri;  # If you remove the resolver, replace $upstream_endpoint with a static IP address (e.g. a local address or a VPN address)
  }
}

# Redirect all HTTP traffic to HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name www.yoursite.com;
  location / {
    return 301 https://$server_name$request_uri;
  }
}

# When a user browses to https://www.yoursite.com/a/b nginx will try to answer with the file /var/www/www.yoursite.com/a/b, then for file /var/www/www.yoursite.com/a/b/index.html and if none of them exists it will give a 404 error page
server {
  listen 4443 ssl http2;  # 443 is forwarded to 4443 for all domains but `subdomain`, see above
  listen [::]:4443 ssl http2;
  server_name www.yoursite.com;
  ssl_certificate /etc/letsencrypt/live/www.yoursitecom/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/www.yoursitecom.com/privkey.pem;
  root /var/www/www.yoursite.com;
  index index.html;
  location / {
    try_files $uri $uri/ =404;
  }
}

After setting up the VPS server, you can obtain a letsencrypt certificate for subdomain.yoursite.com on your home server. If you run certbot certonly --cert-name subdomain.yoursite.com, your certificate and key should be saved in /etc/letsencrypt/live/subdomain.yoursite.com/.

### === Configuration on the home server === ###
## == /etc/nginx/nginx.conf == ##
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# [...]
http {
        # [...]
	include /etc/nginx/mime.types;
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}
## == /etc/nginx/sites-enabled/subdomain == ##
# Redirect all HTTP traffic to HTTPS
server {
  listen 80;
  listen [::]:80;
  server_name subdomain.yoursite.com;
  location / {
    return 301 https://$server_name$request_uri;
  }
}
# When a user browses to https://subdomain.yoursite.com/a/b nginx will try to answer with the file /var/www/subdomain/a/b, then for file /var/www/subdomain/a/b/index.html and if none of them exists it will give a 404 error page
server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name subdomain.yoursite.com;
  ssl_certificate /etc/letsencrypt/live/subdomain.yoursite.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/subdomain.yoursite.com/privkey.pem;
  root /var/www/subdomain;
  index index.html;
  location / {
    try_files $uri $uri/ =404;
  }
}

Option 4

Flowchart representing option 4: the clients query external DNS servers to resolve sd.ys.com and get m.n.o.p, which forwards incoming requests to your home server via VPN.

What if your ISP puts you behind a NAT? This is all but uncommon, given the shortage of IPv4 addresses, and while it may protect your privacy as a client it certainly gets in your way as a server, since clients cannot reach you directly.

Here is where a VPN comes in handy: a tun or type virtual private network can link your home server to your VPS in a secure way, so that the VPS may redirect incoming traffic for subdomain.yoursite.com to your home server at its virtually local address.

You can follow these instruction without a public static IP address, without a public dynamic IP address, without any nameserver and without setting up port forwarding on your home router. All you need is a VPS and a DNS record associating its public static address with your (sub)domain.

  1. Register a wildcard DNS record with your domain provider to make all subdomains of yoursite.com point to your VPS. Just follow these instructions, with the public static IP of your VPS (you may run ifconfig on the VPS to get it).
  2. Run a VPN server on your VPS and VPN client on your home server. You may choose any VPS you like, but if you need help you can read below how to set up OpenVPN. Your home server must always be assigned the same IP address by the VPN server.
  3. Configure the reverse proxy on your VPS to forward traffic for subdomain to the VPN IP address of your home server. As an example, you may take a look here, eliminating the lines dealing with the resolver and replacing the subdomain to resolve with the IP address of your home server.
  4. Configure a web server on your home server, obtaining a certificate for the subdomain.

How to set up OpenVPN

OpenVPN is an opensource free full-featured SSL VPN. The company which developed and maintains it offers many other products, including an easier to manage business VPN called Access Server. This tutorial however will cover the community version of OpenVPN, which may be less user-friendly but is open-srouce and free to use.

### === On both your VPS and home server === ###
# Install openvpn
sudo apt install openvpn
### === On a trusted and secure machine === ###
# You can generate your pki in any other way. Just make sure that the server and the clients certificate requests are signed by the same CA.
# Install easy-rsa
sudo apt install openvpn easy-rsa
# Move to easy-rsa folder
cd /usr/share/easy-rsa

./easyrsa init-pki  # Initialize the primary key infrastructure (PKI)
./easyrsa build-ca nopass  # Create a CA root certificate and key (it will be self-signed and you will use it to sign other certificates)
./easyrsa gen-dh  # Generate Diffie-Hellman parameters
./easyrsa gen-crl  # Generate certificate revocation list
openvpn --genkey --secret pki/tc.key  # Generate a static key for TLS encryption

# Generate certificate requests for your VPN server and client(s) and sign them
./easyrsa gen-req myservername nopass
./easyrsa gen-req homeserver nopass
./easyrsa sign-req server myservername
./easyrsa sign-req client homeserver

# Set SSH so that you have root access from your trusted PC (generate a key pair on your PC and add it to .ssh/authorized_keys on the target server) and copy certificates and keys in the right places
VPS_SERVER="m.n.o.p"  # Replace this with your VPS public static IP address.
HOME_SERVER="w.x.y.z"  # Replace this with your home server's IP address on the LAN.
scp pki/{ca.crt,issued/myservername.crt,private/myservername.key,tc.key,dh.pem,crl.pem} root@"$VPS_SERVER":/etc/openvpn/server/
scp pki/{ca.crt,issued/homeserver.crt,private/homeserver.key,tc.key} root@"$HOME_SERVER":/etc/openvpn/client/
### === On your VPS === ###
cd /etc/openvpn/server
nano server.conf  # See below
mkdir ccd
nano ccd/homeserver  # See below
sudo systemctl enable openvpn-server@server.service  # OpenVPN server service will start at boot
sudo systemctl start openvpn-server@server.service  # Start service now

## == /etc/openvpn/server/server.conf == ##
local m.n.o.p  # Replace this with your VPS public static IP address.
port 1194  # You can change this
proto udp
dev-type tun
dev SubdomainVPN  # VPN name; feel free to change it
ca ca.crt
cert myservername.crt
key myservername.key
dh dh.pem
auth SHA512
tls-crypt tc.key
topology subnet
server 10.10.0.0 255.255.255.0  # You can change this
server-ipv6 fddd:1194:1194:1194::/64  # You can change this
push "redirect-gateway def1 ipv6 bypass-dhcp"
ifconfig-pool-persist ipp.txt
push "dhcp-option DNS 62.149.128.4"
push "dhcp-option DNS 62.149.132.4"
keepalive 10 120
cipher AES-256-CBC
user nobody
group nogroup
persist-key
persist-tun
status openvpn-status.log
verb 3
crl-verify crl.pem
explicit-exit-notify
# Client configuration directory: to assign static IPs to specific clients
client-config-dir /etc/openvpn/server/ccd

## == /etc/openvpn/server/ccd/homeserver == ##
ifconfig-push 10.10.0.5 255.255.255.0
### ===== On your home server ===== ###
cd /etc/openvpn
nano subdomain.conf  # See below
# To allow autostart, edit /etc/default/openvpn, find AUTOSTART="all" line and uncomment it. You can also replace "all" with the VPNs you want to start at boot (separated by a single space from each other).

## == /etc/openvpn/subdomain.conf == ##
client
dev-type tun
dev subdomain  # VPN name, change it if you wish
proto udp
remote m.n.o.p 1194  # Host and port, same as server configuration
resolv-retry infinite
nobind
persist-tun
remote-cert-tls server
auth SHA512
cipher AES-256-CBC
ignore-unknown-option block-outside-dns
block-outside-dns
verb 3
# Do not redirect all traffic through VPN
pull-filter ignore "redirect-gateway"
# Redirect traffic on 10.10.0.x (VPN local area network) through VPN
route 10.10.0.0 255.255.255.0 vpn_gateway
auth-nocache

<ca>
# Paste ca.crt here
</ca>
<cert>
# Paste homeserver.crt here
</cert>
<key>
# Paste homeserver.key here
</key>
<tls-crypt>
# Paste tc.key here
</tls-crypt>

You made it

If you followed these steps you should now be running a public service on a home server. If something went wrong, or a step was unclear or you spotted errors or have suggestions please let me know in the comments section or contact me.

Categories
Tech

Build a website

Do you want to run a website like this? This website is built with WordPress, hosted on a virtual private server (VPS) with nginx as a reverse proxy and MySQL as database management system (DBMS). A DNS record associates the domain (davte.it) to the public ip address of the VPS. A trusted certificate is provided by Let’s Encrypt.

You may purchase a WordPress hosting plan: your provider will take care of registering a DNS record, installing a reverse proxy (typically Apache) and a DBMS, and downloading and configuring WordPress; you will just have to run the WordPress installer to create an administrator account, then you can focus on the content.

This solution is a generally a little more expensive and easier to manage but gives you much less control on what you can do with your website. With a dedicated VPS, in fact, you may install any kind of online service, e.g. NextCloud or Jupyter or GoGS etc., and register a subdomain for each service. Furthermore, you may run your own script on this remote machine, for example a davtelepot Telegram bot.

Setup your VPS

When you buy a VPS, your provider will give you a static IPv4 address and a range of IPv6 addresses. Check that at least one static IP address is included in your VPS plan, since this is required to run a public website.

I suggest choosing Ubuntu as operating system; if available, you may select a pre-installed LEMP environment (if not available, you will have to install nginx, MySQL and php). Your provider will ask you for a root password, or generate one for you. You may log in via SSH, if your VPS comes with SSH installed and working; otherwise you will need to set it up using a remote terminal (follow your provider’s instructions).

sudo apt install openssh-server
sudo ufw allow ssh  # Firewall settings: allow incoming connections on port 22

Let’s say your IP address is 1.1.1.1. You may connect to your VPS with this command:

ssh root@1.1.1.1

Install required programs

A LEMP stack includes the reverse proxy nginx, the DMBS MySQL and the php interpreter. If your plan does not include a LEMP stack, you can install these programs individually.

sudo apt install nginx
sudo apt install mysql-server
sudo apt install php-fpm

To configure MySQL server follow these instructions.

sudo mysql_secure_installation utility

To use Let’s Encrypt as free certificate provider, install certbot for nginx.

add-apt-repository ppa:certbot/certbot # DEPRECATED
apt-get install python-certbot-nginx
sudo apt install certbot python3-certbot-nginx

Prepare WordPress

Download WordPress source code, set user and permissions to files and folders, copy the configuration sample.

cd /var/www
wget https://wordpress.org/latest.zip
unzip latest.zip
chown -R www-data:www-data wordpress  # Use nginx user or ftp user
find wordpress -type f -print0|xargs -0 chmod 664
find wordpress -type d -print0|xargs -0 chmod 775
cp wordpress/wp-config-sample.php wordpress/wp-config.php
chmod 660 wordpress/wp-config.php

Then you have to create a MySQL user and database and grant all privileges on the database to the user.

mysql
CREATE DATABASE wp_example;
CREATE USER 'aaa'@'localhost' IDENTIFIED BY 'password123';
GRANT ALL PRIVILEGES ON wp_example.* TO "aaa"@"localhost";
FLUSH PRIVILEGES;
EXIT

Once that is done, complete WordPress configuration by copying user, host, password, database name and a table prefix of your choice in wp-config.php.

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wp_example' );

/** MySQL database username */
define( 'DB_USER', 'aaa' );

/** MySQL database password */
define( 'DB_PASSWORD', 'password123' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', 'utf8_unicode_ci' );

/**#@+
 * Authentication Unique Keys and Salts.
 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
 * Paste the keys and salts here
*/
$table_prefix = 'wpe_';

Reverse proxy configuration

Assume your website is www.example.com. You want to redirect requests made to example.com to www.example.com, and allow only HTTPS requests.

HTTP requests are made on port 80 by default, whereas HTTPS requests on port 443. The former consist of plain text, the latter are encrypted with server’s certificate.

For now, we will use a self-signed certificate: only after registering a DNS record pointing to your IP address you may obtain a trusted third-party certificate.

# Generate a self-signed certificate and its key
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

Create an example.conf file in /etc/nginx/sites-available/ folder. This folder should contain one file per website you host (yes, the same machine may host several websites!).

# sudo nano /etc/nginx/sites-available/example.conf

server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;
  return 301 https://www.example.com$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  server_name example.com;
  return 301 https://www.example.com$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

  root /var/www/example/wordpress;
  server_name www.example.com;

  location / {
    index index.php;
    try_files $uri/ $fastcgi_script_name /index.php?$args $uri.html;
    expires max;
    # Transform non-php non-file requests into php requests
    if (!-e $request_filename) {
      rewrite ^.*$ /index.php last;
    }
  }

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    try_files index.html $fastcgi_script_name =404;
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;
    fastcgi_index index.php;
    include fastcgi.conf;
    fastcgi_pass unix:/var/run/php/phpn.m-fpm.sock;  # replace phpn.m with your version
  }

}

Link this file in the sites-enabled folder, test the nginx configuration and restart nginx.

sudo ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx.service

With this configuration, all HTTP(S) requests to (www.)example.com are redirected to https://www.example.com, the URL is converted to a file path relative to /var/www/example/wordpress; if a file is found, it is served by nginx; otherwise, the request is processed by php interpreter.

This nginx configuration supports both IPv4 and IPv6.

TLS configuration

HTTPS means “HTTP over SSL/TLS”, where SSL or TLS are security protocols. HTTP requests are plain text, readable by anyone; to convey security to the transmission of data, a cryptographic protocol is used to encrypt the exchanged data. At the beginning of the communication, the client makes sure it is speaking with the right server by encrypting a key using the server’s certificate: only the rightful owner of the server can decrypt with their private key what is encrypted using the public key stated in the trusted third-party certificate. The rest of the communication is encrypted symmetrically with an agreed password.

There are many cryptographic protocols available for HTTPS communications, but only TLSv1.2 and TLSv1.3 are currently considered secure. Supporting only these two may make your website inaccessible for outdated clients, but achieves a stronger security.

Create a /etc/letsencrypt/options-ssl-nginx.conf file and set HTTP header options and encryption accepted protocols and ciphers. Do not trust this example: check out currently secure protocols and ciphers here.

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;

add_header Strict-Transport-Security "max-age=31536000";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
ssl_session_tickets on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ecdh_curve auto;
keepalive_timeout   70;
ssl_buffer_size 1400;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=86400;
resolver_timeout 10;

This configuration file must be included in any nginx server block: if one single server block is missing these options, all blocks may fail to support TLSv1.3 (some versions of Nextcloud, with their own configuration files, tend to break this rule and you will need to fix them manually).

Example of server block including these settings:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    ...

WordPress installation

Simply visit your website’s homepage to run through the WordPress guided installation process.

Security advice

  • Do not install unnecessary or untrustworthy WordPress plugins; keep their number as low as possible
  • Keep your operating system, WordPress, php, nginx, MySQL, and all other software up to date
  • Reboot your server after major updates
  • Hide or protect your /wp-login directory to prevent brute-force attacks
  • Use strong passwords and keep them safe; do not use the same password for different services; change them whenever you suspect they may have been exposed
  • Support only secure protocols

Register a DNS record

  • A record: associates the domain (example.com) and its subdomains to one or more IPv4 addresses; when a client queries an IPv4 DNS for a specific subdomain, it will be directed towards the corresponding IP address.
  • AAAA record: the same as A record, but with IPv6 addresses; please notice that not all providers have an IPv6 DNS, so IPv6-only clients may be unable to find your website; this may become an issue as soon as IPv4 gets deprecated, but unfortunately this is not happening soon.
  • If your provider supports CAA records, you may associate your domain with one or more CAs and provide an email to report issues. Only certificates provided by those CAs will be accepted for your domain.
  • MX record: if your plan includes a mail service, this record is set for you by your provider; otherwise, you will have to provide information about mail server(s) associated with your domain; if your VPS is good enough, you may host your own mail server. This record is important to allow email verification.

Example of A record and AAAA record:

//A
//HostName,IPAddress
A,,1.1.1.1  // Your VPS IPv4 address
A,webmail,5.5.5.5 // Your mail server address
A,www,1.1.1.1

//AAAA
//HostName,IPV6Address
AAAA,,:::::::1  // Your VPS IPv6 address
AAAA,www,:::::::1

Get a Let’s Encrypt certificate

Let’s Encrypt is a trusted nonprofit Certificate Authority whose mission is to provide TLS certificates for free, to make the Internet a safer place.

The easiest way to manage certificates in a LEMP environment is using certbot: once you own a domain and control a VPS at the corresponding IP address, just run sudo certbot --nginx: it will take care of everything, from certificate issuing and renewing to nginx HTTPS configuration.

Categories
Tech

Ubuntu on IdeaPad

I run Ubuntu 20.04 LTS on my Lenovo IdeaPad S540. It is not officially supported, but it works fine.

Warning

No matter how hard I tried, the fingerprint reader could never be detected by Ubuntu.

Before installing

  • Change disk mode to AHCI
  • Disable safe boot (otherwise, wi-fi adapter will not work) This was true for Ubuntu 18.04 but solved in version 20

Suggestion

Install TLP for better battery management.

sudo apt install tlp

Fix wi-fi drivers after every system update

This is required under Ubuntu 18.04, not under 20.04.

# NOTE: you need an internet connection to perform these actions.
# You may share your smartphone connection via usb!

sudo apt update;
sudo apt install git build-essential;
git clone https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git;
cd backport-iwlwifi/ || exit;  # Exit in case cd fails
make defconfig-iwlwifi-public;
sed -i 's/CPTCFG_IWLMVM_VENDOR_CMDS=y/# CPTCFG_IWLMVM_VENDOR_CMDS is not set/' .config;
make -j4;
sudo make install;
sudo modprobe iwlwifi;

At BIOS update

I kept my Windows partition just in case, and it turns out pretty useful if you need to update your BIOS. Firmware encapsulation is likely feasible, but I didn’t explore it.

After the update, you will need to set BIOS settings again: change disk mode to AHCI (Storage > AHCI) and disable safe boot. Ignore warning about data loss (thrilling, isn’t it?).

Windows may need to restore the system after a couple blue screens, do not worry.