Self made time capsule, part 2.

In my previous post I described the hardware components of my self made time capsule/home server. It consisted of the Intel NUC micro-PC, Netgear managed 1GBps switch and Edimax 802.1ac access point. Here I'll go over the basic config, necessary to achieve the functionality I've mentioned.

Linux

I'm using ubuntu 16.04 LTS (Long Term Support). It is a very decent Debian based distribution and works very well on the Intel NUC. In this post I'll assume that the Linux is already installed and all the hardware components are detected by the kernel (I had no issues whatsoever, it worked out of the box). The only issue that may perhaps be a problem on the NUC is when you have secure boot enabled in the BIOS, which should be disabled before you install Linux. Also make sure the boot sequence in the BIOS makes sense. After you install linux, it's a good idea to update to make sure all the installed packages are the latest.

Before we begin the setup it is good to install a few essentials before we screw up our Internet connection:

apt-get install openssh-server
apt-get install git
apt-get install vim
apt-get install dnsmasq
apt-get install vlan

Network

As noted in the previous post, the network configuration will be a crucial part. There are three networks in question: outside world (Internet), local secure network with time machine services and file/printer sharing and an isolated guest network that does however offer access to the Internet. Since there are three networks and only one physical network interface on the NUC there are two choices: buy 2x USB to ethernet adapters or use VLAN's (virtual LAN). I decided to go with VLAN.

Network - NUC

First of all we have to have the vlan support on the NUC. To achieve that, the 8021q module has to be loaded in the kernel (note all the commands are assumed to be executed from root account, otherwise you need to add sudo):

modprobe 8021q

If this command is successful, add a line to /etc/modules file to make sure this module loads by default on boot:

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
8021q

Next we can test vlan by creating a virtual interface with vconfig:

vconfig add enp3s0 10

Assuming enp3s0 is the default physical ethernet interface, this will create interface enp3s0.10 which will be exposed to tagged vlan 10. Note, since vlans are separated at the data link layer, each vlan will have a completely separate ip addressing. We'll need a one more tagged interface, I created enp3s0.11. To make this permanent you can edit /etc/network/interfaces:

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
# This will be the LAN interface
auto enp3s0
iface enp3s0 inet manual
address 192.168.8.1
netmask 255.255.255.0
# This will be the guest network interface
auto enp3s0.11
iface enp3s0.11 inet manual
address 10.10.10.1
netmask 255.255.255.0
vlan-raw-device enp3s0
# This will be the WAN interface
auto enp3s0.10
iface enp3s0.10 inet dhcp
vlan-raw-device enp3s0

Now all the virtual interfaces should be up after reboot.

Network - Switch

Now we have to populate same vlan's on the switch. I use Netgear. After basic configuration of the switch, (I configured a static IP on it) log in, go to VLAN->802.1G->Advanced:

Port 5 will be the WAN port, so it will have VLAN 10 available untagged. Therefore its PVID (default port vlan ID) set to 10.

In VLAN Membership tab one can set up which vlans are at which ports and whether they will be tagged or not. Follow the scheme at the top of the page for configuration.

Network - Access point

Now the access point. First in the basic configuration setup the device to act as an access point (Edimax can also act as a router and a repeater). Next simply define the wireless network both in 2.4 and 5GHz range.

Now for the guest network go to "Multiple SSID" tab, define the network and assign it to a vlan, in my case it is vlan 11.

Plug the access point to an appropriate port on the switch and you should have the wireless network running. Except we have not configured DHCP yet, so the network will only work if you manually set a reasonable address (e.g. 192.168.8.5/24). Also no routing is yet enabled so there won't be any access beyond the NUC.

DNSMASQ

We will use the popular DNSMASQ service to provide DHCP on our LAN and guest network. First install the service:

apt-get install dnsmasq

After the installation we will need to edit some config files. The tricky part here is that we have two interfaces, each one will need to be handled independently since they do not share IP address space. To do that, first edit /etc/dnsmasq.conf (there should a default file with plenty comments and instructions). Uncomment the lines:

# Include all files in a directory which end in .conf
conf-dir=/etc/dnsmasq.d/,*.conf

That way dnsmasq will look for config files in /etc/dnsmasq.d. Each such file may now refer to a different interface and everything is nice and clean. Create two files in /etc/dnsmasq.d, I called them dnsmasq-enp3s0.conf and dnsmasq-enp3s0.11.conf. The dnsmasq-enp3s0.conf file contains:

# Local network
interface=enp3s0
interface=lo
no-dhcp-interface=lo
server=8.8.8.8
dhcp-range=192.168.8.4,192.168.8.254,255.255.255.0,12h
# Several reserved static IP's
# 192.168.8.2 # Edimax Access point
# 192.168.8.3 # Netgear switch
# 192.168.8.4 # Reserved for future
dhcp-host=F4:DE:44:47:6F:EA,192.168.8.5 # HP Printer
# Put more lines like the one above if you want to assign
# static IP addresses to known devices

The file dnsmasq-enp3s0.11.conf contains:

# Guest network interfac
interface=enp3s0.11
server=8.8.8.8
dhcp-range=10.10.10.20,10.10.10.254,255.255.255.0,12h

The "server 8.8.8.8" means that the DNS queries from these networks will be forwarded to google DNS. You can liberally change that to anything else or skip altogether to use the default DNS provided by the internet provider.

Now given all our virtual interfaces are up we can run

service dnsmasq start

Also type:

service dnsmasq status

To check if the service is running.  If everything went well, devices connecting to the LAN should now receive IP information. We have not enabled forwarding yet, to do that type:

# Enable masquerade on LAN subnet and guest network
sysctl -w net.ipv4.ip_forward=1
/sbin/iptables -t nat -A POSTROUTING -s 192.168.8.0/24 -o enp3s0.10 -j MASQUERADE
/sbin/iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o enp3s0.10 -j MASQUERADE

Now the interface enp3s0.10 is the WAN interface. It should be configure with an IP address obtained from the Internet/cable provider. This may be a private address for now (e.g. from 192.168.0.0 subset). This will create a problem called a double-NAT, but this not a serious problem for now, we can fix that later. If everything went OK, we should now have Internet connectivity on both wireless networks. If there is no Internet address on that interface try the command

dhclient enp3s0.10

And if that did not work, check again if the cables are connected to the right ports in the switch (it happened to me and I wasted some time tracking an issue).

Netatalk

Netatalk (Apple Talk) is a software package that can emulate Time Capsule for us. The only problem with that is that we cannot just apt-get install it, since apt-get will install version 2.x while we need version 3. Therefore we'll have to compile it. Download and unpack the latest version from http://netatalk.sourceforge.net or clone the git repo. Next install a few dependencies:

apt-get install autoconf libtool automake build-essential libssl-dev libgcrypt11-dev libkrb5-dev libpam0g-dev libwrap0-dev libdb-dev libmysqlclient-dev libavahi-client-dev libacl1-dev libldap2-dev libcrack2-dev systemtap-sdt-dev libdbus-1-dev libdbus-glib-1-dev libglib2.0-dev tracker libtracker-sparql-1.0-dev libtracker-miner-1.0-dev
apt-get install libtdb-dev
apt-get install libevent-dev

Now follow the compile instructions:

./bootstrap 
$ ./configure \
--with-init-style=debian-sysv \
--without-libevent \
--without-tdb \
--with-cracklib \
--enable-krbV-uam \
--with-pam-confdir=/etc/pam.d \
--with-dbus-sysconf-dir=/etc/dbus-1/system.d \
--with-tracker-pkgconfig-version=0.16 
$ make 
$ sudo make install

or read some useful blog posts such as here if anything fails. Verify that everything is OK by typing:

afpd -V

This should execute and return a version >3.

Once this is done, we'll need to edit the afpd config file located in /usr/local/etc/afp.conf. The file should look approximately like this:

[Global]
afp listen = 192.168.8.1
hostname = capsule
mimic model = TimeCapsule6,106
log level = default:warn
log file = /var/log/afpd.log
hosts allow = 192.168.8.0/24

[Homes]
basedir regex = /home

[Backup]
path = /timemachine/
valid users = capsule_user
time machine = yes

[Shared Media]
path = /shared/
valid users = capsule_user

Assuming /timemachine and /shared directories exist. Next the capsule_user needs to be added with permissions to read and write /timemachine  and /shared directories. Adding a user and setting permissions on network shares is important, afpd will fail otherwise. After all that is done, try:

service netatalk start

and

service netatalk status

If everything went OK, you should now see a device in your mac network and a disk for time machine should also be available. We still have a few more things to take care of.

Disks

The funny thing about the setup I describe is that the mac backup/network shares can be easily stored on a Linux filesystem. I recommend ext4 since it has never failed me, but choose anything you want. I would not recommend using hfs+ file system for that since linux support for hfs is limited (no journaling supported which may be a problem for a USB attached drive). Once you formatted the drive as ext4 you can add a mount line to /etc/fstab, but before we do that, use the  "blkid" command to retrieve device UUID. Next we can add line in /etc/fstab:

UUID=[whatever blkid returned] /timemachine ext4 rw,exec,auto,nofail 0 0

"nofail" option will make the linux robust to mount failures on boot. Since the NUC will probably not have any screen/keyboard connected to it, I want it to be able to boot even if there is an error mounting the device, say because the device got disconnected. Otherwise the NUC may get bricked until you connect a screen and a keyboard. Add as many such lines as you want for all the drives you have connected. Using UUID instead of block device (such as /dev/sdc1) is also beneficial for USB drives, since it is not guaranteed that when you reboot, the drive which previously was /dev/sdc1 will not change to /dev/sdd1. UUID assures that the right device is always mounted in the right place.

Firewall

We should not forget about security, we need to set up rules for IP traffic. There are two main objectives here: secure the server from outside attacks and isolate the guest network from LAN. Here is a very basic setup:

# -----------------------------------------------
# Guest network isolation
# Forward guest only to the public WAN interface
/sbin/iptables -A FORWARD -i enp3s0.11 -o enp3s0.10 -j ACCEPT
/sbin/iptables -A FORWARD -i enp3s0.11 ! -o enp3s0.10 -j DROP
# Allow DNS since DNSMASQ offers that service
/sbin/iptables -A INPUT -p udp -i enp3s0.11 --dport 53 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -i enp3s0.11 --dport 53 -j ACCEPT
# Allow DHCP on guest net as well
/sbin/iptables -A INPUT -i enp3s0.11 -p udp --dport bootps -j ACCEPT
# Drop everything else
/sbin/iptables -A INPUT -i enp3s0.11 -j DROP
# -----------------------------------------------
# Main firewall
# Allow established
/sbin/iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
/sbin/iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow local traffic
/sbin/iptables -I INPUT 1 -i lo -j ACCEPT
# Allow ping response
/sbin/iptables -A INPUT -p icmp --icmp-type 8 -s 0/0 -i enp3s0.10 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Forward TCP:192.168.8.6:8080 to public interface
/sbin/iptables -t nat -A PREROUTING -p tcp -i enp3s0.10 --dport 88 -j DNAT --to-destination 192.168.8.6:8080
/sbin/iptables -A FORWARD -p tcp -d 192.168.8.6 --dport 8080 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# Drop everything else
/sbin/iptables -A INPUT -i enp3s0.10 -j DROP

My example also contains a case of port forwarding from 8080 to 192.168.8.6:8080. Clone the two lines for any other port forwarding needs you may have.

Starting it all at boot time

Here comes the tricky part: staring it all in the right order at boot time. That proved to be a lot more challenging than I originally expected. Initially I populated all the default debian config files but noticed that every once in a while the device would not boot like it should. It seems there is a race condition between the network configuration and dnsmasq service. Sometimes dnsmasq would start before all the interfaces were initialised. That caused dnsmasq to crash and the whole device would not function. After somewhat long tracking of that issue, I eventually decided to write my own start script that would initialise everything in the right order. In Ubuntu 16 writing a start script takes two edits:

Firstly /lib/systemd/system/myscript.service. The file contains roughly:

[Unit]
Description=My script for fixing things

[Service]
Type=simple
ExecStart=/etc/startup.sh # Path to the actual script

[Install]
WantedBy=multi-user.target

And then the script itself, which contains much of what has already been discussed:

#!/bin/sh -e
#
# Flush iptables
/sbin/iptables -F
# Set up interfaces
/sbin/ifconfig enp3s0 192.168.8.1/24 up
/sbin/ifconfig enp3s0.11 10.10.10.1/24 up
# Start dnsmasq and netatalk services
/usr/sbin/service dnsmasq start
/usr/sbin/service netatalk start
# Enable masqurade on LAN subnet and guest network
/sbin/iptables -t nat -A POSTROUTING -s 192.168.8.0/24 -o enp3s0.10  -j MASQUERADE
/sbin/iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o enp3s0.10  -j MASQUERADE
# -----------------------------------------------
# Guest network isolation
# Forward guest only to the public WAN interface
/sbin/iptables -A FORWARD -i enp3s0.11 -o enp3s0.10 -j ACCEPT
/sbin/iptables -A FORWARD -i enp3s0.11 ! -o enp3s0.10 -j DROP
# Allow DNS since DNSMASQ offers that service
/sbin/iptables -A INPUT -p udp -i enp3s0.11 --dport 53 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -i enp3s0.11 --dport 53 -j ACCEPT
# Allow DHCP on guest net as well
/sbin/iptables -A INPUT -i enp3s0.11 -p udp --dport bootps -j ACCEPT
# Drop everything else
/sbin/iptables -A INPUT -i enp3s0.11 -j DROP
# -----------------------------------------------
# Main firewall
# Allow established
/sbin/iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow SSH
/sbin/iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow local traffic
/sbin/iptables -I INPUT 1 -i lo -j ACCEPT
# Allow ping response
/sbin/iptables -A INPUT -p icmp --icmp-type 8 -s 0/0 -i enp3s0.10 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
/sbin/iptables -A OUTPUT -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Forward TCP:192.168.8.6:8080 to public interface
/sbin/iptables -t nat -A PREROUTING -p tcp -i enp3s0.10 --dport 8080 -j DNAT --to-destination 192.168.8.6:8080
/sbin/iptables -A FORWARD -p tcp -d 192.168.8.6 --dport 8080 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# Drop everything else
/sbin/iptables -A INPUT -i enp3s0.10 -j DROP
exit 0

Next we enable the script:

systemctl enable myscript

Neither netatalk, nor dnsmasq should be enabled since they should only be started from the script above, so make sure to run:

systemctl disable netatalk
systemctl disable dnsmasq

Also remember to uncomment/add line in /etc/sysctl.conf:

net.ipv4.ip_forward=1

Now everything is finally ready and starts without any hiccups on every boot.

Final touch

Once everything is ready, you can reconfigure your cable router to assign public IP address to the NUC (exact config may vary depending on the router, but pretty much all of them have such an option). Look for DMZ (demilitarised zone settings), WAN IP mapping or similar. That will eliminate the problem of double NAT I mentioned before. Also now, since your Linux box with port 22 open is exposed to the Internet, you can remotely login via ssh (if you know your IP address obviously). Finally, it is a good idea to make sure that your firewall is up and filters all it should filter. You can check that by nmapping yourself from a remote host, say:

nmap -Pn [your IP]

Caution: don't do that from Amazon EC2 instance as you will get busted. 

If you don't have any handy remote hosts to perform the audit, use e.g. these guys: https://www.grc.com

Summary

With this basic config I achieved all the functionality I had with my previous Apple Time Capsule. But this is just the beginning, since the NUC equipped with a modern Linux has lots of other possibilities. Need an SMB file server? No problem. Need a network monitoring and reporting? No problem. Need to control some IOT devices at home? No problem. Need a guarded network with content filters for children? No problem. The possibilities are endless!

Update 09.2017

After I've written this article a new version of NetAtalk came out with a nice description of compilation process on ubuntu 16.04 available here:

http://netatalk.sourceforge.net/wiki/index.php/Install_Netatalk_3.1.11_on_Ubuntu_16.04_Xenial

Comments

comments

One thought on “Self made time capsule, part 2.

Leave a Reply