Self Hosted: Episode 3 - DNS/DHCP/NTP
Used Software:
- FreeBSD 14.2 (as jail)
- ISC Bind 9.2.0 (from ports)
- ISC DHCP 4.4 (from ports)
Introduction
It all begins with DNS...
Building the Jail environment
The DNS/DHCP server will be built using a FreeBSD jail. This is done for several reason. First, FreeBSD Jails come up nearly as quick as the hypervisor itself, meaning if everything is (re)started at the same time the DNS server should be the first service up an running. Second, is that FreeBSD jails provide isolation from other processes on the same system. Third, they are light weight.
What is a FreeBSD Jail (referred to as just a 'jail' from now on)? A jail is an isolated virtual space to run processes in that is separate from the host OS, and all other jails. From the Linux perspective jails are very close to Docker containers (although they pre-date Docker by a decade).
Setup the Jail sever
To setup the jail server first we need a space to store all the jails. Jails consist of the base FreeBSD OS version of our choice. We will be making use of ZFS snapshots to make deploying and updating jails quickly.
Create Jail from template
To create a jail from the template use the ZFS clone command.
zfs clone data01/jails/templates/14.2-RELEASE@base data01/jails/dns
command to clone a jail drive
Setup the DNS/DHCP server jail
Next we will create a jail configuration file. These files will be stored in the /etc/jails.conf.d directory. Each jail will have its own file. The file is used to setup the properties of the jail, what it is allowed to do within the host OS and any pre/port commands that need to be run in the host OS when the jail is started/stopped.
First we need to add/update the /etc/devfs.conf file. This file controls what resources are available to the jail. In this case we will need to expose the BPF devices to listen for DHCP requests. The following lines need to be added to the /etc/devfs.conf file
[devfsrules_jail_bpf=10]
add include $devfsrules_jail
add path bpf* unhide
partial /etc/devfs.conf
Now create the /etc/jail.conf.d/dns.conf file for defining and starting the jail.
# DNS/DHCP server jail file
dns {
# STARTUP / LOGGING
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# PERMISSIONS
allow.raw_sockets;
exec.clean;
mount.devfs;
devfs_ruleset = 10;
allow.reserved_ports = true;
# HOSTNAME / PATH
host.hostname = "${name}";
path = "/data01/jails/${name}";
# NETWORK
vnet;
vnet.interface = "${epair}b";
# NETWORKS/INTERFACES
$id = "10";
$ip = "192.168.30.${id}";
$mask = "255.255.255.0";
$gateway = "192.168.30.1";
$bridge = "net-dmz";
$epair = "epair${id}";
exec.prestart += "ifconfig ${epair} create up";
exec.prestart += "ifconfig ${epair}a up descr jail:${name}";
exec.prestart += "ifconfig ${bridge} addm ${epair}a up";
exec.start += "ifconfig ${epair}b ${ip} netmask ${mask} up";
exec.start += "route add default ${gateway}";
exec.start += "/bin/sh /etc/rc";
exec.poststop += "ifconfig ${bridge} deletem ${epair}a";
exec.poststop += "ifconfig ${epair}a destroy";
}
/etc/jails.conf.d/dns.conf
Start the jail
Once the configuration file has been complete the jail should be started.
service jail start dns
command to start the jail
Install the DNS/DHCP service in the jail
Once the jail has been started we need to update the FreeBSD package system and install the DNS and DHCP packages into the jail.
pkg -j dns -y update
command to update the packages in the jail
Setup the new jail
To enter the jail from the host OS use the jexec(8) command and specify the command to run inside the jail.
jexec -l dns /bin/sh
command to connect to the jail
Once in the jail you will be the root user. The jail itself will have no service running by default, not even SSH. Let's enable the SSH service... but first we need to create a user account because we don't want to SSH into the server as root... (or at least we shouldn't do this... it is up to you).
jexec -l dns pw user add alfred -m -s /bin/sh -u 1001 -G wheel
jexec -l dns passwd alfred
jexec -l dns passwd root
commands to create a jail user and set the passwords
Fix sshd to disallow root to login via SSH. Edit the /etc/ssh/sshd_config and explicitly disable root login.
jexec -l dns sed -i -r -e 's/^#?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
command to edit sshd_config to prevent root logins
Now lets enable and start the SSH service within the jail
service -j dns sshd enable
service -j dns sshd start
command to enable and start sshd
Now we test. SSH to 192.168.30.10 (remember no DNS yet). Once that is working we can move on to the DNS configuration.
DNS server setup discussion...
Before we configure the DNS settings lets create a list of items we need to accomplish to be successful.
- Resolve internal names from internal sources
- Resolve external names from internal sources
- Resolve DMZ names from external sources (future episode)
- Filter external names from internal sources (bad domains, advertisements, etc) (future episode)
- Provide Dynamic DNS updates from internal hosts
We can accomplish this is three different ways (there are probably more... but only three I'll mention). Each way has it's own complications.
The first way is to use DNS views (or DNS split view). Based on where the client request comes from the client will be presented with one view or another view of the domain. This can be tricky to troubleshoot.
The second method, that is easier to troubleshoot, is to use two different domains. One domain is used for internal applications and the other domain is used for external applications. This is easier to troubleshoot, but become complicated when we need to expose an internal resource externally.
The third method is to use a single domain but two authoritative name servers, and internal and external name server. This method requires the most work of all since we will need to maintain two separate servers, but from the client standpoint, it is very easy to understand. External clients connect to the external name server, Internal clients use the internal name server.
In series I will present the third option. While the goal is to "self host" everything, we will require internet facing services at predictable addresses. This will allow us to utilize one of the many Internet DNS providers out there while maintaining maximum control.
Configuring DNS server
(this section assumes that we are ssh'd into the jail)
On FreeBSD the configuration files are generally stored in the /usr/local/etc/namedb directory.
First we need to create the rndc.conf file.
rndc-confgen -a -k DNS_UPDATE
command to create the rndc.conf file
This will create a file in the /usr/local/etc/namedb/ directory.
Next we will crate the named.conf file.
//
options {
// All file and path names are relative to the chroot directory,
// if any, and should be fully qualified.
directory "/usr/local/etc/namedb/working";
pid-file "/var/run/named/pid";
dump-file "/var/dump/named_dump.db";
statistics-file "/var/stats/named.stats";
listen-on {
127.0.0.1;
192.168.30.10;
};
// These zones are already covered by the empty zones listed below.
// If you remove the related empty zones below, comment these lines out.
disable-empty-zone "255.255.255.255.IN-ADDR.ARPA";
disable-empty-zone "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";
disable-empty-zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";
# allow recursion
allow-recursion { 0.0.0.0/0; };
};
include "/usr/local/etc/namedb/rndc.key";
controls {
inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "DNS_UPDATE"; };
};
zone "." { type hint; file "/usr/local/etc/namedb/named.root"; };
zone "example.org" {
type primary;
file "/usr/local/etc/namedb/primary/example.org";
allow-update { key DNS_UPDATE; };
};
zone "10.168.192.in-addr.arpa" {
type primary;
file "/usr/local/etc/namedb/primary/10.168.192.in-addr.arpa";
allow-update { key DNS_UPDATE; };
};
zone "20.168.192.in-addr.arpa" {
type primary;
file "/usr/local/etc/namedb/primary/20.168.192.in-addr.arpa";
allow-update { key DNS_UPDATE; };
};
zone "30.168.192.in-addr.arpa" {
type primary;
file "/usr/local/etc/namedb/primay/30.168.192.in-addr.arpa";
allow-update { key DNS_UPDATE; };
};
zone "40.168.192.in-addr.arpa" {
type primary;
file "/usr/local/etc/namedb/primary/40.168.192.in-addr.arpa";
allow-update { key DNS_UPDATE; };
};
/usr/local/etc/namedb/named.conf
Finally we create the zone files stored in the /usr/local/etc/namedb/primary/ directory.
;
$TTL 60
$ORIGIN example.org.
@ IN SOA dns.example.org. noone.example.org. (
10
1h
3h
2h
4h
)
;
; NS records
IN NS dns.example.org.
; MX records
; A records
justatest IN A 192.168.10.254
dns IN A 192.168.30.10
firewall-dmz IN A 192.168.30.1
firewall-iot IN A 192.168.20.1
firewall IN A 192.168.10.1
; CNAME records
/usr/local/etc/namedb/primary/example.org
;
$TTL 60
$ORIGIN 10.168.192.in-addr.arpa.
@ IN SOA dns.example.org. noone.example.org. (
10
1h
3h
2h
4h
)
; NS records
IN NS dns.example.org.
; PTR records
254 IN PTR justatest.example.org.
1 IN PTR firewall.example.org.
/usr/local/etc/namedb/primary/10.168.192.in-addr.arpa
;
$TTL 60
$ORIGIN 20.168.192.in-addr.arpa.
@ IN SOA dns.example.org. noone.example.org. (
10
1h
3h
2h
4h
)
; NS records
IN NS dns.example.org.
; PTR records
1 IN PTR firewall-iot.example.org.
/usr/local/etc/namedb/primary/20.168.192.in-addr.arpa
;
$TTL 60
$ORIGIN 30.168.192.in-addr.arpa.
@ IN SOA dns.example.org. noone.example.org. (
10
1h
3h
2h
4h
)
; NS records
IN NS dns.example.org.
; PTR records
10 IN PTR dns.example.org.
1 IN PTR firewall-dmz.example.org.
/usr/local/etc/namedb/primary/30.168.192.in-addr.arpa
;
$TTL 60
$ORIGIN 40.168.192.in-addr.arpa.
@ IN SOA dns.example.org. noone.example.org. (
10
1h
3h
2h
4h
)
; NS records
IN NS dns.example.org.
/usr/local/etc/namedb/primary/40.168.192.in-addr.arpa
Once bind has been configured we need to enable it so that it starts automatically.
service named enable
service named start
command to enable and start named service
Confirm DNS server is working
To confirm that DNS is working the following commands can be run.
dig @127.0.0.1 google.com
dig @127.0.0.1 justatest.example.org
commands to test recursive and authoritative resolution
Both commands should succeed. Assuming it is working, update the /etc/resolv.conf file
nameserver 127.0.0.1
/etc/resolv.conf
DHCP server discussion
As with DNS server, let's take a moment to discuss what we want to accomplish with the DHCP server.
- Provide DHCP for the COMMON segment.
- Provide DHCP for the IOT segment.
- Dynamically update DNS records for the assigned IPs
One important thing to note is that DHCP clients use a broadcast to find a DHCP server. This broadcast does not extend past the local subnet (without help).
Install DHCP server software
First step is to install the DHCP server software
pkg install isc-dhcp44-server
command to install the dhcp server software
Configuring DHCP server
Use the following config for the DHCP server
#
# option definitions common to all supported networks...
option domain-name "example.org";
default-lease-time 86400;
max-lease-time 86400;
ddns-update-style standard;
deny client-updates;
ddns-updates on;
authoritative;
log-facility local7;
include "/usr/local/etc/namedb/rndc.key";
zone example.org. {
primary 127.0.0.1;
key DNS_UPDATE;
}
zone 10.168.192.in-addr.arpa. {
primary 127.0.0.1;
key DNS_UPDATE;
}
zone 20.168.192.in-addr.arpa. {
primary 127.0.0.1;
key DNS_UPDATE;
}
zone 30.168.192.in-addr.arpa. {
primary 127.0.0.1;
key DNS_UPDATE;
}
zone 40.168.192.in-addr.arpa. {
primary 127.0.0.1;
key DNS_UPDATE;
}
subnet 192.168.10.0 netmask 255.255.255.0 {
range 192.168.10.10 192.168.10.254;
option routers 192.168.10.1;
option domain-name-servers 192.168.30.10;
}
subnet 192.168.20.0 netmask 255.255.255.0 {
range 192.168.20.10 192.168.20.254;
option routers 192.168.20.1;
option domain-name-servers 192.168.30.10;
}
subnet 192.168.30.0 netmask 255.255.255.0 {
range 192.168.30.10 192.168.30.254;
option routers 192.168.30.1;
option domain-name-servers 192.168.30.10;
}
subnet 192.168.40.0 netmask 255.255.255.0 {
range 192.168.40.10 192.168.40.254;
option routers 192.168.40.1;
option domain-name-servers 192.168.30.10;
}
/usr/local/etc/dhcpd.conf
Next we enable the DHCP service
service isc-dhcpd enable
service isc-dhcpd start
commands to enable and start the dhcp server
Setup DHCP relay on Firewall
As mentioned above DHCP uses a link local broadcast to find a DHCP server. In order to provide the help necessary to forward the broadcasts from different LAN segments we will setup the firewall to DHCP relaying.
On the firewall we will enable and start the DHCP relay service.
ln -s /etc/rc.d/dhcrelay /etc/rc.d/dhcrelay_iot
ln -s /etc/rc.d/dhcrelay /etc/rc.d/dhcrelay_common
rcctl enable dhcrelay_common dhcrelay_iot
rcctl set dhcrelay_common flags -i vio2 192.168.30.10
rcctl set dhcrelay_iot flags -i vio1 192.168.30.10
rcctl start dhcrelay_common dhcrelay_iot
Conclusion
This setups up the initial DNS and DHCP services. However, as this series progresses we will add DNS records.