From 28cf55061b5cd2552f3137c21091a4217b050a47 Mon Sep 17 00:00:00 2001 From: Keith Irwin Date: Fri, 7 Apr 2023 14:40:11 -0600 Subject: [PATCH] #9 Wrote ns and bind installation instructions --- INSTALL.md | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 370 insertions(+), 6 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 268fbca..8efbe98 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,7 +6,7 @@ Before installing wagon, the server needs to be set up with the following basic - bind9 - openssl ca -Theoretically, wireguard, bind9, and wagon could all live in docker containers, or none of them. If wagon is in docker but either wireguard or bind9 *aren't*, then the wagon container has to use `host` network mode. +Theoretically, wireguard, bind9, and wagon could all live in docker containers, or none of them. If wagon is in docker but either wireguard or bind9 *aren't*, then the wagon container has to use "host" network mode. Almost all commands in this guide need to be run as root/admin. @@ -52,7 +52,7 @@ net_num=99 # Match what's on the server srv_listenport=58395 # Match what's on the server srv_pubkey='XXXXXXXXXXXXXX' # The public key we copied above psk='YYYYYYYYYYYYYY' # The psk we copied above -srv_endpoint='1.2.3.4' # The server's public IP address +public_ip='1.2.3.4' # The server's public IP address ``` Create the interface on the client and add the server as a peer: @@ -62,7 +62,7 @@ our_privkey="$(wg genkey)" ip link add dev "${net_name}" type wireguard ip addr add dev "${net_name}" "10.${net_num}.1.1/8" echo "${our_privkey}" | wg set "${net_name}" private-key /dev/stdin \ -peer "${srv_pubkey}" allowed-ips "10.0.0.0/8" endpoint "${srv_endpoint}:${srv_listenport}" persistent-keepalive 25 +peer "${srv_pubkey}" allowed-ips "10.0.0.0/8" endpoint "${public_ip}:${srv_listenport}" persistent-keepalive 25 ip link set up dev "${net_name}" ``` @@ -85,12 +85,376 @@ Make sure the client can ping the server with `ping 10.${net_num}.0.1` and the s ## 2. bind9 -TODO +These instructions are adapted from [Digital Ocean: How To Configure BIND as a Private Network DNS Server on Ubuntu 22.04 (2022-08)](https://www.digitalocean.com/community/tutorials/how-to-configure-bind-as-a-private-network-dns-server-on-ubuntu-22-04) + +On the server, open a root shell and [install bind9 nameserver](https://kb.isc.org/docs/aa-00648). Set some variables to make the rest easier. + +```sh +tld='mynet' # The same as $net_name (see below) +net_num='99' # Match what's used with wireguard +public_ip='1.2.3.4' # The server's public IP address, as above +``` + +The `$tld` should match the `$net_name`, which is the wireguard interface name. Probably using a different `$tld` could be possible in a future version. Ok, let's configure bind. In older documentation, you'll see reference to `named.conf`. In newer versions of bind, this file just includes `named.conf.options` and `named.conf.local`. We will follow this convention too. + +If you're already running a nameserver, you can add these configurations alongside your existing settings. + +**`/etc/bind/named.conf.options`** +```named.conf +// Access control lists +acl "mynet_acl" { + // change 99 to $net_num + 10.99.0.0/16; +}; +acl "intervpn_acl" { + 10.0.0.0/8; +}; + +options { + + // This is bind's default location for zonefiles + // Just make sure you include it in backups. + directory "/var/cache/bind"; + + // https://serverfault.com/a/381923 + notify explicit; + + // Add listen-on-v6 if using IPv6 + // Listen on your public IP too if you are + // also running a public nameserver + listen-on { + 127.0.0.1; // localhost + 10.99.0.1; // VPN + }; + + // Whom we provide nameservice to + // https://tldp.org/HOWTO/DNS-HOWTO-6.html#ss6.2 + recursion yes; + allow-recursion { + localhost; localnets; + intervpn_acl; // or mynet_acl to be more restricitve + }; + + // Enable DNSSEC validation for forwarded queries + dnssec-validation auto; + + // Keep these settings restricted and change them + // per-zone in named.conf.local. + allow-transfer { none; }; + allow-query { + localhost; localnets; + }; + + // If we don't know a domain, forward to these nameservers + // A good list of public nameservers by country: + // https://dnschecker.org/public-dns + forwarders { + 1.1.1.1; + 8.8.8.8; + }; + +}; +``` + +That wasn't so bad, was it? Now we'll set the per-zone settings. wagon requires reverse DNS so stop complaining and just do it. I spent a while searching for a good tool to turn IP addresses into their respective rDNS domains before I realized that `nslookup` does that. `nslookup` and `nsupdate` should have been installed with bind; if not, install them. + +Ok, let's use nslookup to grab the rDNS domain for our IP: + +```sh +nslookup 10.99.0.1 +** server can't find 1.0.99.10.in-addr.arpa: NXDOMAIN +``` + +Now we know that to provide rDNS for all the IPs in `10.99.0.0/16`, we must serve on the entire zone `99.10.in-addr.arpa`. Do this for IPv6 if needed to get a zone ending in `.ip6.arpa`. + +**`/etc/bind/named.conf.local`** +```named.conf +// We'll create these keys in the next step +include "/etc/bind/keys/admin.key"; +include "/etc/bind/keys/wagon.key"; + +// mynet is your tld +zone "mynet" { + type master; + // "file" is relative to the "directory" we + // set in named.conf.options + file "mynet.db"; + allow-query { + localhost; localnets; + intervpn_acl; // or mynet_acl to be more restricitve + }; + // This should be include slave servers AND + // Any machine that will be running wagon + allow-transfer { localhost; 10.99.0.1; }; + // This should be set to slave servers + also-notify { localhost; }; + // Here we give two keys nsupdate permissions + update-policy { + grant admin zonesub ANY; + grant wagon zonesub ANY; + }; +}; + +// The rDNS zone we got from nslookup +zone "99.10.in-addr.arpa" { + type master; + file "10.99.db"; // relative to "directory" + // The next settings can be copied verbatim from above + allow-query {localhost; localnets; intervpn_acl; }; + allow-transfer { localhost; 10.99.0.1; }; + update-policy { + grant admin zonesub ANY; + grant wagon zonesub ANY; + }; +}; + +// Any existing zones will live happily alongside +// zone "example.com" { +``` + +Excellent. This file referenced four files that don't exist yet and must be created. Let's start with the keys. `nsupdate` uses symetric keys, so one copy will live on the bind server and the other will be copied to the nsupdate client. + +I like to keep my keys in `/etc/bind/keys`. Let's create this directory and the two keys named above. Actually you should rename the "admin" key as your username and give a different key to each admin. "wagon" is of course the key our future dashboard will use to update the nameserver. + +```sh +mkdir /etc/bind/keys +tsig-keygen -a hmac-sha512 admin >/etc/bind/keys/admin.key +tsig-keygen -a hmac-sha512 wagon >/etc/bind/keys/wagon.key +chown -R root:bind /etc/bind/keys +chmod 750 /etc/bind/keys +chmod 640 /etc/bind/keys/*.key +cat /etc/bind/keys/admin.key +``` + +Copy the key we just catted and paste it into an `admin.key` file on your pc. Now you will be able to modify DNS records by running `nsupdate -k admin.key` on your PC. When we set up wagon, we'll copy the value from `wagon.key` into the wagon config. But let's not get ahead of ourselves, we still have one more thing to do with bind: create the zonefiles. + +Firstly, let's assume some variables: + + - Our server's $HOSTNAME is `hn` and it's domain name will be `hn.mynet` + - The single wireguard client we set up at `10.99.1.1` is going to get the domain name `pc.myuser.mynet`. + +Start with fDNS: + +**`/var/cache/bind/mynet.db`** +```bind +$ORIGIN . +$TTL 604800 ; 1 week +mynet IN SOA hn.mynet. myuser.mynet. ( + 2 ; serial + 604800 ; refresh (1 week) + 86400 ; retry (1 day) + 2419200 ; expire (4 weeks) + 604800 ; minimum (1 week) + ) + NS hn.mynet. +$ORIGIN mynet. + +; Record for our servers +hn A 10.99.0.1 +*.hn CNAME hn.mynet. + +; Record for our user +$ORIGIN myuser.mynet. +pc A 10.99.1.1 +*.pc CNAME pc.myuser.mynet. +``` + +In the SOA line, there are two values that require explanation: + + - **`hn.mynet.`** is the default server `nsupdate` will send updates to. Of course it's our VPN domain, not a public domain name; we don't accept nsupdates from the internet. + - **`myuser.mynet.`** is actually the email `myuser@mynet` and should be set to the server admin. If you want to use a public email address, you can set it to something like `hostmaster.example.com.` for `hostmaster@example.com`. + + Now do one for rDNS (same thing goes for the SOA line here): + +**`/var/cache/bind/10.99.db`** +```bind +$ORIGIN . +$TTL 604800 ; 1 week +99.10.in-addr.arpa IN SOA hn.mynet. myuser.mynet. ( + 2 ; serial + 604800 ; refresh (1 week) + 86400 ; retry (1 day) + 2419200 ; expire (4 weeks) + 604800 ; minimum (1 week) + ) + NS hn.mynet. + +; Server records +$ORIGIN 0.99.10.in-addr.arpa. +1 PTR hn.mynet. + +; User records +$ORIGIN 1.99.10.in-addr.arpa. +1 PTR pc.myuser.mynet. +``` + +See, that wasn't so hard! Now start the nameserver and check that it doesn't throw any errors: + +```sh +systemctl start named +systemctl enable named +systemctl status named +``` + +If it's not working, fix it and then go back to your pc and check the lookups. + +```sh +nslookup pc.myuser.mynet 10.99.0.1 +nslookup hn.mynet 10.99.0.1 +nslookup 10.99.0.1 10.99.0.1 +nslookup 10.99.1.1 10.99.0.1 +``` +Each of these commands uses `10.99.0.1` as the nameserver by setting it as the second argument; you can also make that your default nameserver or the nameserver for the `mynet` TLD. Look into setting "search domains" for your VPN interface in your operating system. `systemd-resolved` users, for example, can run these commands: + +```sh +resolvectl dns mynet 10.99.0.1 +resolvectl domain mynet '~mynet' '~99.10.in-addr.arpa' +``` + +This will tell the OS to send `.mynet` queries to our vpn nameserver. Not all programs respect this setting though; `dig`, `ping`, and your browser will work but you'll still have to set the nameserver by hand for `nslookup` (as above) and `nsupdate` using the "server" command (even though we set it in our SOA): + +```sh +nsupdate -k admin.key +> server 10.99.0.1 +> add test.mynet 86400 TXT "hello" +> delete test.mynet TXT +> send +> quit +``` ## 3. Certificate authority -TODO +The last major step is to set up the certificate authority. Unlike wireguard and bind, this won't require running some background service; we just generate a few files and keep them safe. + +A good place to keep your SSL certs and keys is in `/etc/ssl/private/mynet`. Let's make things easier by setting some variables: + +```sh +tld='mynet' +crt_dir="/etc/ssl/private/${tld}" +ca_key="${crt_dir}/_ca.key" +ca_crt="${crt_dir}/_ca.crt" +``` + +Now we'll create the ca key and cert. You will be asked for some details about your organization; put whatever you want. You'll also be asked to create a passphrase: create and store one using the most secure methods! You'll need this passphrase for the `wagon` config later. + +Here we're setting `-days 3650` which will require re-signing and re-distributing the certificate every ten years. You can avoid that by setting it to 100 years with `-days 36500`. This field is required but I think there is no limit, so you can set it to `99999999` if you want. + +```sh +openssl genrsa -des3 -out "${ca_key}" 4096 +openssl req -x509 -new -nodes -key "${ca_key}" -sha256 -days 3650 -out "${ca_crt}" +ln -s "${ca_crt}" "/etc/ssl/certs/${tld}.pem" +``` + +The last step makes the cert available to verification from the host OS. This cert file, `/etc/ssl/private/mynet/_ca.crt` should be shared with everyone who will be accessing your network. One easy way to do this is to serve it on your public website at `https://www.example.com/ca.crt` so users can easily download it. It must be added to every user's OS and/or browser. How this is done will depend on the OS and browser... so you should provide instructions to your users! A sample of such instructions can be found at [www.gf4.pw/nebuchadnezzar/ca/](https://www.gf4.pw/nebuchadnezzar/ca/). + +We can use these CA files to sign certificates for hosts using our `mynet` domain. Let's sign one for the server first: + +```sh +org='My Cool Network' +tld=mynet +host=hn +domain="${host}.${tld}" +crt_dir="/etc/ssl/private/${tld}" +host_dir="${crt_dir}/${host}" +ca_crt="${crt_dir}/_ca.crt" +ca_key="${crt_dir}/_ca.key" +ips='IP:10.99.0.1' + +# Create a subdirectory for the host's files +mkdir -p "${host_dir}" + +# Generate the host's key +openssl genrsa -out "${host_dir}/server.key" 2048 + +# Set certificate configuration +# If /etc/ssl/openssl.cnf doesn't exist, look for +# openssl.cnf somewhere in your openssl installation +cat /etc/ssl/openssl.cnf \ + <(printf "\n[SAN]\nsubjectAltName=DNS:${domain},DNS:*.${domain},${ips}\n") \ + >"${host_dir}.cnf" + +# Now we'll create the certificate signing request +openssl req -new -sha256 -reqexts SAN \ + -key "${host_dir}/server.key" \ + -config "${crt_dir}/${host}.cnf" \ + -subj "/O=${org}/OU=${host}/CN=${domain}" \ + -out "${crt_dir}/${host}.csr" + +# Finally, sign the certificate +# This will request the CA passphrase set previously +# Set -days to whatever you want using the tips above +openssl x509 -req -sha256 -extensions SAN \ + -CAcreateserial -days "3650" + -CA "${ca_crt}" -CAkey "${ca_key}" \ + -in "${crt_dir}/${host}.csr" \ + -extfile "${crt_dir}/${host}.cnf" \ + -out "${host_dir}/server.crt" +``` + +That should do it! Let's check that the cert is valid for all domains and IPs: + +```sh +openssl x509 -text -noout -in "${host_dir}/server.crt" | grep -A1 'Subject Alternative Name' +``` + +That should return something like: + +```sh +X509v3 Subject Alternative Name: + DNS:hn.mynet, DNS:*.hn.mynet, IP Address:10.99.0.1 +``` + +It contains our domain, wildcard domain, and IP address. Since everything went well, we can delete the CSR and cnf file: + +```sh +rm -f "${crt_dir}/${host}.csr" "${crt_dir}/${host}.cnf" +``` + +One last thing: we need to generate a certificate and key for our pc. Everything is basically the same as with the server, except that our domain will be `pc.myuser.mynet` instead of `hn.mynet`. So let's breeze through this and check the comments from above if you get confused. + +```sh +org='My Cool Network' +tld=mynet +host='pc.myuser' +domain="${host}.${tld}" +crt_dir="/etc/ssl/private/${tld}" +host_dir="${crt_dir}/${host}" +ca_crt="${crt_dir}/_ca.crt" +ca_key="${crt_dir}/_ca.key" +ips='IP:10.99.1.1' +days=3650 + +mkdir -p "${host_dir}" + +openssl genrsa -out "${host_dir}/server.key" 2048 + +cat /etc/ssl/openssl.cnf \ + <(printf "\n[SAN]\nsubjectAltName=DNS:${domain},DNS:*.${domain},${ips}\n") \ + >"${host_dir}.cnf" + +openssl req -new -sha256 -reqexts SAN \ + -key "${host_dir}/server.key" \ + -config "${crt_dir}/${host}.cnf" \ + -subj "/O=${org}/OU=${host}/CN=${domain}" \ + -out "${crt_dir}/${host}.csr" + + +openssl x509 -req -sha256 -extensions SAN \ + -CAcreateserial -days "3650" + -CA "${ca_crt}" -CAkey "${ca_key}" \ + -in "${crt_dir}/${host}.csr" \ + -extfile "${crt_dir}/${host}.cnf" \ + -out "${host_dir}/server.crt" + +openssl x509 -text -noout -in "${host_dir}/server.crt" | grep -A1 'Subject Alternative Name' + +rm -f "${crt_dir}/${host}.csr" "${crt_dir}/${host}.cnf" +``` + +You might be thinking, this would all be easier as a script. A script that could add clients to wireguard and bind, then generate and server the ssl files. This is what `wagon` is designed to do. ## 4. Wagon -TODO +Now that we have this all set up, we can use wagon. Wagon will help us add clients to wireguard, give them a domain name in bind, and create SSL certificates for them in a single step on a nice GUI dashboard. + +TODO: Finish wagon setup instructions. \ No newline at end of file