This chapter covers
They tell us we live in a hyper-mobile world. Not that I’d know: I rarely leave my home office. I get to enjoy the comforts of my home office because all the server resources I could possibly need are available remotely. Apparently I’m not alone.
Almost everyone whose work touches IT will access their professional tools from remote locations from time to time. And given that the public networks through which you access those remote locations are by their very nature insecure, you’re going to want to carefully control those connections.
The previous chapter focused on making sure that the data consumed by your remote clients is reliably transferred and invisible to anyone who might be lurking on the connecting network. This chapter, by sharp contrast, will focus on making sure that the data consumed by your remote clients is reliably transferred and invisible to anyone who might be lurking on the connecting network. See the difference? Neither do I.
In fact, there are all kinds of technologies devoted to securing network communication, and the principle of defense in depth teaches us never to rely on one. Here’s where you’ll learn about adding new layers of protection for your remote activities.
In this chapter, you’ll revisit a couple of old friends. You’ll use VirtualBox VMs and encryption to build a virtual private network (VPN) tunnel to permit secure and invisible remote connections. And, in a separate project, you’ll design more sophisticated firewall architectures to strategically divide your network into isolated segments. Finally, you’ll build a virtual network environment within VirtualBox so you can test your configurations.
I’ve talked a lot about encryption already in this book. SSH and SCP can protect data transferred through remote connections (chapter 3), file encryption can protect data at rest (chapter 8), and TLS/SSL certificates can protect data in transit between websites and client browsers (chapter 9). But sometimes your requirements demand protection across a broader range of connections because, occasionally, you’ve got different kinds of work to do. For instance, perhaps some members of your team need to work from the road using public WiFi hotspots. It’s definitely not smart to assume that random WiFi access points are secure, but your people do need a way to connect with company resources—VPNs to the rescue.
A properly designed VPN tunnel provides a direct connection between remote clients and a server in a way that hides data as it’s transferred across an insecure network. But so what? You’ve already seen lots of tools that can do that using encryption. The real value of a VPN is that once you’ve opened a tunnel, it’s possible to connect remote networks as though they’re all together locally. In a way, you’re circumventing that dodgy coffee shop hot spot.
Using such an extended network, admins can get their work done on their servers no matter where they might happen to be. But more importantly, as you can see in figure 10.1, a company with resources spread through multiple branch offices can make them all both visible and accessible to all the teams who need them, wherever they are.
The mere existence of a tunnel alone doesn’t guarantee security. But one of a number of encryption standards can be incorporated into the design, making things a great deal better. Tunnels built with the open source OpenVPN package use the same TLS/SSL encryption you’ve already seen in use elsewhere. OpenVPN is not the only available choice for tunneling, but it’s among the best known. And it’s widely assumed to be a bit faster and more secure than the alternative Layer 2 Tunnel Protocol using IPsec encryption.
You’ll want your team to safely connect with each other from out on the road or between multiple campuses. For that, you’ll need to build an OpenVPN server to permit sharing applications and to access the server’s local network environment. To make it work, it should be sufficient to fire up two VMs or containers: one to play the role of a server/host and the other of the client. Building a VPN involves quite a few steps, so taking a few moments to think about the big picture of how this is going to work will probably be worthwhile.
Before getting started, here’s a helpful tip. If you’re going to follow along with this process on your own, and I strongly recommend that you do, you’ll probably find yourself working with multiple terminal windows open on your desktop, each logged in to a different machine. Take it from me, at some point you’re going to enter a command into the wrong window and totally mess up your environment. To avoid this, you can use the hostname command to change the machine name displayed on the command line to something that will visually remind you where you are. Once that’s done, you’ll need to exit the server and log back in again for the new setting to take effect. Here’s what it looks like:
ubuntu@ubuntu:~# hostname OpenVPN-Server ubuntu@ubuntu:~$ exit 1 <Host Workstation>$ ssh [email protected] ubuntu@OpenVPN-Server:~#
Following that approach to assign appropriate names to each of the machines you’re working with should help you keep track of where you are.
After using hostname, you might encounter annoying Unable to Resolve Host OpenVPN-Server messages when running subsequent commands. Updating the /etc/hosts file to match the new hostname should solve that.
Installing OpenVPN on your server requires two packages: openvpn and, to manage the encryption key-generation process, easy-rsa. CentOS users should, if necessary, first install the epel-release repository the way you did back in chapter 2. To give you an easy way to test access to a server application, you could also install the Apache web server (apache2 for Ubuntu and httpd on CentOS).
While you’re setting up your server, you might as well do it right and activate a firewall that blocks all ports besides 22 (SSH) and 1194 (the default OpenVPN port). This example illustrates the way that will work on Ubuntu’s ufw, but I’m sure you still remember CentOS’ firewalld from chapter 9:
# ufw enable # ufw allow 22 # ufw allow 1194
To permit internal routing between network interfaces on the server, you’ll need to uncomment a single line (net.ipv4.ip_forward=1) in the /etc/sysctl.conf file. This allows remote clients to be redirected as needed once they’re connected. To load the new setting, run sysctl -p:
# nano /etc/sysctl.conf # sysctl -p
The server environment is now all set up, but there’s still a ways to go before you’re ready to flip the switch. Here are the steps we’ll cover over the next pages of this chapter:
For simplicity, you’re going to set up your key infrastructure on the same machine that’s running the OpenVPN server. Security best practices, however, will usually suggest that a separate CA server be used for production deployments. In any case, figure 10.2 illustrates the process of generating and distributing encryption key resources for use on OpenVPN.
When you installed OpenVPN, a /etc/openvpn/ directory was automatically created, but there isn’t a whole lot in it just yet. Both the openvpn and easy-rsa packages come with sample template files that you can use as a base for you configuration. To jump-start the certification process, copy the easy-rsa template directory from /usr/share/ to /etc/openvpn/ and then change to the easy-rsa/ directory:
# cp -r /usr/share/easy-rsa/ /etc/openvpn $ cd /etc/openvpn/easy-rsa
Because the easy-rsa directory will now contain quite a few scripts, table 10.1 gives you a quick preview of the tools you’ll be using to bring your keys into existence.
Script name |
Function |
---|---|
clean-all | Removes old key files to prepare for new key generation |
pkitool | Frontend for OpenSSL (does most of the key-gen heavy lifting) |
build-ca | Uses the pkitool script to generate a root certificate |
build-key-server server | Uses the pkitool script to generate a key pair and certificate |
build-dh | Sets Diffie-Hellman authentication parameters |
These operations require root authority, so through sudo su, you’ll need to become root.
The first file you’ll work with is called vars, which contains environment variables that easy-rsa uses when it generates its keys. You’ll want to edit the file to substitute your own values for the sample defaults that are already there. Here’s what my file would look like.
export KEY_COUNTRY="CA" export KEY_PROVINCE="ON" export KEY_CITY="Toronto" export KEY_ORG="Bootstrap IT" export KEY_EMAIL="[email protected]" export KEY_OU="IT"
Running the vars file will pass its values to the shell environment from where they’ll be incorporated into the contents of your new keys. Why would sudo alone not work? Because the first step edits a script called vars and then sources it. Sourcing means that the vars file passes its values to the shell environment from where those will be incorporated into the contents of your new keys.
Make sure to run the file again using a new shell if you need to complete an unfinished process. When that’s done, the script will encourage you to run the clean-all script to delete any existing content in the /etc/openvpn/easy-rsa/keys/ directory:
$ cd /etc/openvpn/easy-rsa/ # . ./vars 1 NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/keys
Naturally, your next step will be to run that clean-all script followed by build-ca, which uses the pkitool script to create your root certificate. You’ll be asked to confirm the identification settings provided by vars:
# ./clean-all # ./build-ca Generating a 2048 bit RSA private key
Next is the build-key-server script. Because it uses the same pkitool script along with the new root certificate, you’ll be asked the same confirmation questions to generate a key pair. The keys will be given names based on the arguments you pass, which, unless you’re running multiple VPNs on this machine, will normally be server as in this example:
# ./build-key-server server [...] Certificate is to be certified until Aug 15 23:52:34 2027 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
OpenVPN uses parameters generated using the Diffie-Hellman algorithm (by running build-dh) to negotiate authentication for new connections. The file that’s created here doesn’t need to remain secret, but it must have been generated using the build-dh script against the RSA keys that are currently active. If you create new RSA keys at some time in the future, you’ll also need to update the Diffie-Hellman file:
# ./build-dh
Your server-side keys will now have been written to the /etc/openvpn/easy-rsa/keys/ directory, but OpenVPN doesn’t know that. By default, OpenVPN will look for them in /etc/openvpn/, so copy them over:
# cp /etc/openvpn/easy-rsa/keys/server* /etc/openvpn # cp /etc/openvpn/easy-rsa/keys/dh2048.pem /etc/openvpn # cp /etc/openvpn/easy-rsa/keys/ca.crt /etc/openvpn
As you’ve already seen, TLS encryption uses matching key pairs: one installed on the server and the other on a remote client. That means you’re going to need client keys. Our old friend pkitool is just the thing to cook some up. This example, run while still in the /etc/openvpn/easy-rsa/ directory, passes client as an argument to generate files called client.crt and client.key:
# ./pkitool client
The two client files, along with the original ca.crt file that’s still in the keys/ directory, will now have to be securely transferred to your client. Because of their ownership and permissions, this might be a bit complicated. The simplest approach is to manually copy the contents of the source file (and nothing but those contents) in a terminal running on your PC’s desktop (by highlighting the text, right-clicking over it, and selecting Copy from the menu), then pasting it into a new file of the same name you create in a second terminal logged in to your client.
But anyone can cut and paste. Instead, think like an admin, because you won’t always have access to a GUI where cutting and pasting is possible. Instead, copy the files to your user’s home directory (so a remote scp operation can access them) and then use chown to change the ownership of the files from root to your regular, non-root user so that remote scp action can work. Make sure your files are all settled in and comfy for now. You’ll move them over to the client a bit later:
# cp /etc/openvpn/easy-rsa/keys/client.key /home/ubuntu/ # cp /etc/openvpn/easy-rsa/keys/ca.crt /home/ubuntu/ # cp /etc/openvpn/easy-rsa/keys/client.crt /home/ubuntu/ # chown ubuntu:ubuntu /home/ubuntu/client.key # chown ubuntu:ubuntu /home/ubuntu/client.crt # chown ubuntu:ubuntu /home/ubuntu/ca.crt
With a full set of encryption keys ready for action, you’ll need to tell your server how you want to build your VPN. That’s done using the server.conf file.
Too much typing for your poor, tired fingers? Brace expansion can help reduce those six commands to two. I’m sure you’ll be able to study these two examples and figure out what’s going on. More importantly, you’ll be able to figure out how to apply the principles to operations involving dozens or even hundreds of elements:
# cp /etc/openvpn/easy-rsa/keys/{ca.crt,client.{key,crt}} /home/ubuntu/ # chown ubuntu:ubuntu /home/ubuntu/{ca.crt,client.{key,crt}}
How are you supposed to know what the server.conf file should look like? Well, remember the easy-rsa directory template you copied from /usr/share/? There are more goodies where that came from. The OpenVPN installation left a compressed template configuration file that you can copy to /etc/openvpn/. I’ll use the fact that the template is compressed to introduce you to a useful tool: zcat.
You already know about printing a file’s text contents to the screen with cat, but what if the file is compressed using gzip? You could always decompress the file, and cat will then be happy to print it, but that’s one or two steps too many. Instead, as you’ve probably already guessed, you can use zcat to load the decompressed text into memory all in one step. In this case, rather than print it to the screen, you’ll redirect the text to a new file called server.conf:
# zcat /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf $ cd /etc/openvpn
Leaving out the extensive and helpful documentation that comes with the file, here’s how it might look once you’re done editing. Note that a semicolon (;) tells OpenVPN not to read and execute the line that follows.
port 1194 # TCP or UDP server? proto tcp ;proto udp ;dev tap dev tun ca ca.crt cert server.crt key server.key # This file should be kept secret dh dh2048.pem server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "route 10.0.3.0 255.255.255.0" keepalive 10 120 comp-lzo port-share localhost 80 user nobody 1 group nogroup persist-key persist-tun status openvpn-status.log log openvpn.log 2 ;log-append openvpn.log verb 3 3
Let’s work through some of those settings, one at a time:
In addition, it’s also common to add client-to-client to the config file so multiple clients will be able to see each other in addition to the OpenVPN server. Once you’re satisfied with your configuration, you’re ready to fire up the OpenVPN server:
# systemctl start openvpn
Due to the evolving nature of the relationship between OpenVPN and systemd, starting the service might sometimes require a syntax like this: systemctl start openvpn@server.
Running ip addr to list your server’s network interfaces should now include a reference to a new interface called tun0. This will have been created by OpenVPN for the use of incoming clients:
$ ip addr [...] 4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc [...] link/none inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0 valid_lft forever preferred_lft forever
It’s possible that you’ll need to reboot the server before everything will fully function. Next stop: the client computer.
Traditionally, tunnels are built with at least two ends (otherwise we’d call them caves). Having OpenVPN properly configured on the server directs traffic into and out of the tunnel at that end. But you’ll need some kind of software running on the client side as well.
In this section, I’m going to focus on manually configuring a Linux computer of one sort or another to act as an OpenVPN client. But that’s not the only way you might want to consume the service. OpenVPN itself maintains client applications that can be installed and used on Windows or Mac desktop and laptops, or Android and iOS smartphones and tablets. See the https://openvpn.net website for details.
The OpenVPN package will need to be installed on the client machine as it was on the server, although there’s no need for easy-rsa over here because the keys you’ll use already exist. You’ll need to copy the client.conf template file over to the /etc/openvpn/ directory that the installation just created. This time, for some reason, the file won’t be compressed, so a regular cp will do the job just fine:
# apt install openvpn # cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/
Most of the settings in your client.conf file will be fairly obvious: they’ll need to match the values used by the server. As you can see from the next sample file, one setting that’s unique is remote 192.168.1.23 1194, which points the client to the server’s IP address. Again, make sure you use your server’s address. You should also force your client to verify the authenticity of the server certificate to prevent a possible man-in-the-middle attack. One way to do this is by adding the remote-cert-tls server line.
client 1 ;dev tap dev tun proto tcp remote 192.168.1.23 1194 2 resolv-retry infinite nobind user nobody group nogroup persist-key persist-tun ca ca.crt cert client.crt key client.key comp-lzo verb 3 remote-cert-tls server 3
Now you can move to the /etc/openvpn/ directory and pull those certification keys from the server. Substitute your server’s IP address or domain name for the one in the example:
$ cd /etc/openvpn # scp [email protected]:/home/ubuntu/ca.crt . 1 # scp [email protected]:/home/ubuntu/client.crt . # scp [email protected]:/home/ubuntu/client.key .
Nothing exciting is likely to happen until you start OpenVPN on the client. Because you’ll need to pass a couple of arguments, you’ll pull the trigger from the command line. The argument --tls-client tells OpenVPN that you’ll be acting as a client and connecting via TLS encryption, and --config points to your config file:
# openvpn --tls-client --config /etc/openvpn/client.conf
Read the command output carefully to make sure you’re connected properly. If something does go wrong the first time, it’s probably due to a setting mismatch between the server and client configuration files or perhaps a network connectivity/ firewall issue. Here are some troubleshooting steps:
If everything OpenVPN spat out at you as it loaded on the client looks fine, then you should move on to test your tunnel to confirm that it’s working and protecting your connection. Running curl from the client against the server address using the OpenVPN port should return the index.html file in your web root directory (/var/www/html/):
curl 192.168.1.23:1194
Test your setup by removing the port number and the colon preceding it. Assuming your firewall is running with the previous settings, curl shouldn’t work.
The truth is, that alone is not a useful test. After all, the goal of a VPN is to prevent your session activity from being visible to other people on the network, so being able to load the page proves nothing. Without going into that much detail, the following sections suggest a couple of ways (in no specific order) to confirm that your VPN is working properly.
Set up a regular GUI desktop PC as a client, and use your browser to open a server-based application that will require some kind of data entry. For the application, you could use a simple HTML form like the following.
<!DOCTYPE html> <html> <body> <form action="/action_page.php"> First name:<br> <input type="text" name="firstname" value="Your"> <br> Last name:<br> <input type="text" name="lastname" value="Name"> <br><br> <input type="submit" value="Submit"> </form> </body> </html>
You could save the file to the server’s web document root as, say, form.html. You’ll then access the page using the 192.168.1.23/form.html URL (substituting your server’s IP address).
To find out if the information you typed into the form would have been visible to unauthorized eyes, you can use network packet-sniffing software like Wireshark to capture and analyze the data. If the tunnel worked to encrypt the session, then the packets containing the names you entered would be unintelligible.
Unfortunately, a useful guide to setting up and using Wireshark for this purpose would require its own complete chapter. As that would be a significant detour away from our beloved Linux, that chapter will have to find a spot within a different book.
A second approach to testing your VPN involves setting up resources (servers, routers, and so on) within the server’s local network, the one to which you pushed clients from your server.conf file. Building infrastructure of one kind or another to make this work can get quite involved. But if you pull it off, and your client can access those resources, then you’ll know things are working. I’ve added a guide to using VirtualBox to build such an infrastructure at the end of this chapter.
VPNs, through the magic of encryption, are great for protecting session data. And firewalls can nicely control incoming and outgoing traffic by port, protocol, or routing information. In theory, that should be enough to protect your server networks. After all, if you’ve locked down all but a couple of approaches into your network and strengthened the locks to the few doors that are still there (using, for instance, passwordless key-pair authentication for SSH), then you should be safe, right? If only it were that simple.
No matter how good you are, there’s no such thing as 100% when it comes to IT security. You do your best to keep informed about new kinds of threats, patch your software, audit your processes, and fine-tune your firewall, but someone’s always going to find a way through the wall. Besides the risk of human error and buggy or intentionally compromised software, it’s only a matter of time before (quantum computing?) machines capable of cracking encryption algorithms become widely available.
Practically, the solution is to over-provision by adding as many layers to your security profile as possible so that even if the bad guys make it past one gate, there should still be two or three others standing between you and the abyss. (Earlier, we called this defense in depth.) After thinking things through, you may decide that one of those layers involves separating your resources into multiple isolated networks in the hope that if one is breached, the others might still be protected.
When everything is said and done, the primary goal is to safely expose all the services that should be exposed and to jealously protect everything else. Let’s say you’re running a web server hosting a publicly available application. Users’ browsers will load their pages from the web server, while user information and application data are handled by a second server running a database. You’ll want to give everyone on earth (along with anyone who might be enjoying the view in low earth orbit) access to your web server, but close off all public access to the database server. Let’s discuss how that particular magic trick is done.
One popular isolation architecture is known as a DMZ (a contraction of the phrase used to describe a geographic buffer between two distrustful nations: demilitarized zone). The idea is to divide your servers, workstations, WiFi routers, and other resources into separate networks. To make it work, each of your devices will need to be physically connected to a separate network interface.
One simple implementation of a DMZ, as illustrated in figure 10.3, is to use a single server as a router to redirect traffic between the internet and two internal networks. One of the networks might contain backend databases or the workstations and laptops used in your office. This network will be heavily protected by tight access rules. The other network will enjoy fairly direct and easy access to the outside world and might include public-facing resources like web servers.
Before moving on to building your own DMZ environments, you should at least be aware of a couple alternatives to the DMZ model. I’ll mention some in the following sections.
In addition to DMZs, which are reliable and flexible enough to be candidates for a wide range of use cases, there are other secure network configuration models that you might consider.
A jump server (sometimes referred to as a jumpbox or, perhaps, a bastion host) is a lightweight server open to the internet that’s given only one function: allow remote SSH clients to log in and then “jump” on to other servers running on the jump server’s private network. As shown in figure 10.4, the private servers on the network would have some kind of access controls configured to permit access to only remote logins coming from the jump server.
Jump servers (by definition, representing a possible single point of failure and an extra layer of complexity) aren’t as popular as they once were. As mentioned in chapter 9, proprietary hardware solutions provided by companies like Juniper and Cisco can be used to manage connectivity and security for larger enterprise deployments. But the costs of such hardware and the significant learning curve you’ll need to overcome before you’ll be able to competently use it can sometimes outweigh the benefits.
None of those systems will necessarily make all that much sense for hybrid cloud solutions, where company infrastructure is hosted both locally and on remote cloud platforms. But the underlying tools and design concepts are always going to be useful.
Next up, I’m going to discuss creating a DMZ using two widely used software firewall packages: iptables and Shorewall. Before I do, though, I should tell you that for all intents and purposes, they’re pretty much the same thing. Shorewall is a more user-friendly tool that does nothing more than manipulate iptables rules without you seeing it. Here are two reasons why I’d like you to see both those approaches:
Here we go. Warning: complex and involved topic ahead. Skip to the end of the chapter if you feel like it. Remember to head right back here the next time you’re asked to build this kind of infrastructure.
iptables is a tool for managing firewall rules on a Linux machine. iptables? But what about the ufw and firewalld command sets you learned about in the previous chapter? So, you don’t like choices? And would it spoil your day if I told you that there was a fourth tool out there called nftables?
OK, I’ll admit that the whole thing does smell a bit funny, so let me explain. It all starts with netfilter, which controls access to and from the network stack at the Linux kernel module level. For decades, the primary command-line tool for managing netfilter hooks was the iptables rule set.
Because the syntax needed to invoke those rules could come across as a bit arcane, various user-friendly implementations like ufw and firewalld were introduced as higher-level netfilter interpreters. ufw and firewalld are, however, primarily designed to solve the kinds of problems faced by standalone computers. Building full-sized network solutions will often require the extra muscle of iptables or, since 2014, its replacement, nftables (through the nft command-line tool).
iptables hasn’t gone anywhere and is still widely used. In fact, you should expect to run into iptables-protected networks in your work as an admin for many years to come. But nftables, by adding on to the classic netfilter tool set, has brought some important new functionality. Still, for the sake of simplicity, I’m going to use iptables to quickly show you how to handcraft a DMZ from the command line. When that’s done, we’ll move on to the somewhat more intuitive Shorewall implementation.
iptables should be installed by default. To confirm that, you can run iptables -L to list all the current rules. If you haven’t been playing around with it yet, you should see a completely open (ACCEPT) firewall. Note that the INPUT, FORWARD, and OUTPUT chains are all part of the default Filter table. The NAT table that also exists by default can be used to alter (or translate) a packet’s routing information so it can move between networks successfully. We’ll revisit NAT a bit later:
# iptables -L Chain INPUT (policy ACCEPT) 1 target prot opt source destination ACCEPT tcp -- anywhere anywhere tcp dpt:domain ACCEPT udp -- anywhere anywhere udp dpt:domain ACCEPT tcp -- anywhere anywhere tcp dpt:bootps ACCEPT udp -- anywhere anywhere udp dpt:bootps Chain FORWARD (policy ACCEPT) target prot opt source destination ACCEPT all -- anywhere anywhere ACCEPT all -- anywhere anywhere Chain OUTPUT (policy ACCEPT) target prot opt source destination
Once you’ve got an overall plan, which in this case is separating the kinds of servers you’re running using a DMZ setup, you should set a strict default policy.
Firewall admin rule #1: always know how it’s supposed to end before you start.
These rules form the baseline of a firewall by blocking (DROP) all incoming and forwarding traffic for all interfaces, but allowing outgoing:
# iptables -P INPUT DROP # iptables -P FORWARD DROP # iptables -P OUTPUT ACCEPT
Because their internet connection comes through the router server (perhaps via a network switch), all of the devices on your networks are automatically bound to these rules. Test it out for yourself. Again, feel free to use the virtual network infrastructure described as follows. We’ll assume that the three network interfaces attached to your firewall server are named as described in table 10.2.
Designation |
Purpose |
---|---|
eth0 | Connected to the internet |
eth1 | Connected to the DMZ |
eth2 | Connected to the local private network |
Once upon a time, you could have safely assumed that your Linux distro would assign simple and straightforward names for the physical network interfaces attached to your computer. The first ethernet interface recognized would be eth0, the second one, eth1, and so on; wireless interfaces would be wlan0 and wlan1 and so forth. (As you saw in chapter 9, virtual devices like tunnels and bridges can be given names using different conventions.) The point is that even if you didn’t happen to know an interface’s exact name, you could usually guess it pretty quickly. But run ip addr on your modern systemd machine, and you may come face to face with something like enp1s0. Or how about this abomination: wlx9cefd5fe4a18. Just trips off your tongue when you repeat it, no?
Here’s what’s going on. For many solid reasons, systems using predictable interface names are easier and safer to manage, even if there’s a bit of a convenience trade-off. An ethernet interface might be made up of en for ethernet, p1 to indicate bus 1, and s0 to indicate the first physical slot. And a different device might get wl (wireless LAN) and x9cefd5fe4a18, where the x indicates that the hexadecimal number that follows is the device’s MAC address.
As long as you know where a device is plugged in and/or what its MAC address is, you can safely predict its interface name. Nevertheless, for simplicity, I’ll use the older conventions for this chapter’s examples.
You’ll want the devices within your local network to be able to communicate with the public-facing servers, including the router itself, in the DMZ. These two rules added to the FORWARD chain in the Filter table will allow data packets to move (FORWARD) between the networks behind the eth1 and eth2 interfaces. -A indicates that what follows should be added as a new rule. This allows your web server in the DMZ to exchange data with the database server in the private network:
iptables -A FORWARD -i eth1 -o eth2 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT iptables -A FORWARD -i eth2 -o eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
This next rule will be added to the NAT table (-t nat). It uses the TCP protocol to handle all traffic coming through the eth0 interface that’s aimed at your application’s (fictional) public IP address (54.4.32.10) and specifies that it wants to use the HTTP port 80. Any traffic meeting all of those conditions will be rerouted to the internet IP address of your web server on the DMZ (192.168.1.20):
iptables -t nat -A PREROUTING -p tcp -i eth0 -d 54.4.32.10 --dport 80 -j DNAT --to-destination 192.168.1.20
Because you haven’t opened any access from the internet through the eth0 interface to either the DMZ or the private network, nothing coming from the outside can reach any local servers.
You may think you’re done, only you’re not. Wait a half hour or so, and you should start receiving angry emails from the developers on your team wondering why they can’t download critical software updates or cute cat videos. Be prepared to customize your firewall settings to accommodate unexpected special needs (like access to and from remote software repositories or cloud resources). Ideally, you should anticipate the things you’ll need before building the firewall, but the old “pull the trigger and see what breaks” approach also works.
As I mentioned before, iptables isn’t at the vanguard of firewalling technology any more, and this example was very bare-bones basic, but I think it does help illustrate how such things work. If you’re curious about how the big boys and girls get this kind of thing done on newer deployments, an official guide to putting together something similar using nftables is available at http://mng.bz/b0DM.
I’m sure you already know the drill: the first step is to get the software. Ubuntu users can use APT to install the shorewall package, although those on CentOS will need to get it through the epel-release repository:
# yum install epel-release # yum install shorewall
Unlike the command-line-based iptables, Shorewall is managed through configuration files. There’s something about having the solid visual representation of complex settings provided by config files that I personally find reassuring. Once you wrap your mind around the way Shorewall settings are spread across a half dozen or so files, you’ll probably find working with Shorewall syntax much less intimidating than iptables.
Shorewall startup options are controlled by the /etc/default/shorewall file, but the firewall configuration itself is normally handled by a number of files in the /etc/shorewall/ directory. /etc/shorewall/shorewall.conf sets the shell environment within which Shorewall will run. It’s possible that for simple projects, you won’t even need to touch either that file or the params and conntrack files that you’ll also find there. You will, however, need to create some other files for which templates exist in /usr/ share/doc/ shorewall/examples/ (or /usr/share/doc/shorewall-5.0.14.1/Samples on CentOS, where 5.0.x is the version number). If you list the contents of that examples/ directory, you’ll see four subdirectories that, in turn, contain sample configuration files covering a number of common scenarios:
# ls /usr/share/doc/shorewall/examples/ LICENSE.gz README.txt two-interfaces one-interface three-interfaces Universal
Even though the three-interfaces option looks like a good match for what we’re planning here, we’ll put together what we need from scratch so you can see clearly and exactly how the process works. Table 10.3 shows a quick rundown of the /etc/shorewall/ files you might find yourself using.
Filename |
Purpose |
Required |
---|---|---|
zones | Declares the network zones you want to create | Yes |
interfaces | Defines which network interfaces will be used for specified zones | Yes |
policy | Defines high-level rules controlling traffic between zones | Yes |
rules | Defines exceptions to the rules in the policy file | No |
masq | Defines dynamic NAT settings | No |
stoppedrules | Defines traffic flow while Shorewall is stopped | No |
params | Sets shell variables for Shorewall | No |
conntrack | Exempts specified traffic from Netfilter connection tracking | No |
There’s lots of good documentation available through man by invoking man shorewall-, along with the name of the particular file you’re looking for:
$ man shorewall-rules
Let’s get started using a text editor to create a zones file. The first line defines the Shorewall server as type firewall. Each of the three active zones you’ll create will use type ipv4 addressing (as opposed to IPv6). The net zone represents the public network (the one all the cool kids call the internet), dmz will be the public-facing zone within your infrastructure, and loc will be the private, local network for your backend servers.
fw firewall net ipv4 dmz ipv4 loc ipv4
Now you’ll need to create a file called interfaces where you’ll associate each of your new zones with one of the three network interfaces you’ve got attached to the Shorewall server. detect tells Shorewall that you want the network settings to be automatically detected; dhcp means that you want IP addresses automatically assigned to your interfaces by DHCP servers. And nosmurfs,routefilter,logmartians on the internet-facing interface will filter suspicious packets and source domains, and log-related events.
net eth0 detect dhcp,nosmurfs,routefilter,logmartians 1 dmz eth1 detect dhcp loc eth2 detect dhcp
The policy file establishes the default, baseline behavior you want. The first line in this example will silently delete all traffic coming from the internet (net) directed at any destination. Outbound traffic from the private zone (loc) to the internet is allowed, enabling local machines to receive software updates. The third line states that traffic originating from the firewall should be accepted everywhere. The final line rejects any packets not covered by other rules.
net all DROP 1 loc net ACCEPT fw all ACCEPT all all REJECT 2
The only other file you’ll need to create for this simple configuration is rules. A rules file can, as your needs change, become quite long and complex. For this exercise, however, you’ll need only a few lines. The primary goal of the rules file is to fine-tune the broad-strokes exclusions of the policy file.
For instance, because you’ll be running a web server in the DMZ zone, you’ll want to allow all traffic using the TCP protocol to access via either port 80 (insecure HTTP) or port 443 (secure HTTP). You’ll also want to open SSH access from your local servers to machines (including the Shorewall firewall server itself) in the DMZ. Without SSH, how will admin workstations on the local network be able to administer the firewall?
If necessary for remote SSH access, you might also open port 22 from the net network to the firewall. The Web(DNAT) rule allows port-forwarding access from the internet to your web server in the DMZ. Although it’s not a part of this chapter’s example, if you end up running a DNS server on your firewall machine, you’ll also open access for port 53 from your local network to the firewall machine.
ACCEPT all dmz tcp 80,443 ACCEPT net dmz tcp 22 ACCEPT loc dmz tcp 22 ACCEPT loc fw udp 53 Web(DNAT) net dmz:10.0.1.4
All that’s left is to start up Shorewall on the firewall machine:
# systemctl start shorewall
Don’t forget to restart Shorewall after making edits to the configuration files. And don’t think you won’t be making edits to the configuration files as you struggle to get everything working the way it’s supposed to.
You know as well as I do that you have to try a tool out for yourself before you can understand how it works. But trying out the deployments discussed in this chapter will require more than one or two virtual servers to test. To do a proper job here, you’ll need to set up multiple networks whose I/O access can be fully controlled.
If you’ve got a few spare machines, some cabling, and a couple of switches lying around then, great, roll up your sleeves and have at it. I for one really miss playing with server and networking hardware. But what can I do? My world is pretty much all virtual now: I can’t even remember the last time I opened up my own workstation.
If, like me, you’re stuck with whatever networks you can build for yourself on a command line, here’s how it can work. You’re going to use VirtualBox to create a couple of virtual NAT networks, provision network interfaces for each of the VMs you’ll launch, and manually associate the interfaces with the appropriate networks.
Bear in mind that the number of VirtualBox VMs you’ll be able to launch is limited by the amount of free physical memory and CPU power you’ve got on your VirtualBox host machine. With a Linux host running on 8 GB of RAM, you might be able to squeeze out three concurrent VMs. And don’t even think about trying that on a Windows host unless you’ve got at least 16 GB of RAM. Consider offloading one or two VMs on to a separate PC within your network.
You’ll use the vboxmanage command-line tool for the first step (creating the networks for a DMZ configuration). As far as I can see, there’s no way to do this from within the VirtualBox GUI. The command natnetwork add tells VirtualBox that you want to add a new NAT network. In the first example, the netname will be dmz, and the subnet will be 10.0.1.0/24. The second network will be called loc (for local), and its subnet will be 10.0.2.0/24. When the networks are created, you’ll start them using natnetwork start:
$ vboxmanage natnetwork add --netname dmz --network "10.0.1.0/24" --enable --dhcp on 1 $ vboxmanage natnetwork add --netname loc --network "10.0.2.0/24" --enable --dhcp on $ vboxmanage natnetwork start --netname dmz $ vboxmanage natnetwork start --netname loc
Due to a known bug, it might not be possible to create and run NAT networks on VirtualBox versions older than 5.1.
The next step is to create (or, better, clone) the VMs you’ll need. (Make sure you’ve got enough system memory and CPU power on your host machine to handle all the VMs you plan to launch.) Before you start the VMs, click once on the first one’s name in the VirtualBox list, then click the Settings button, and then click the Network item on the left side. If this VM is going to be, say, a Shorewall server, then attach its first network adapter to whichever interface you normally use to give your VMs full internet access.
Now click the Adapter 2 tab and then the Attached To drop-down menu, and select the NAT Network setting. Because you’ve already created a couple of NAT networks, both should be available as options (figure 10.5). Select one of them for Adapter 2, and then the other for Adapter 3.
To complete a Shorewall test environment, work through the Settings | Network section of your other two VMs and associate one with the dmz NAT network, and the other with loc. Those two VMs will have only one interface each.
With all that done, you can fire up your three VMs and log in. Run ip addr on each to confirm that the interfaces have been recognized and are connected to their networks. If you see the right kind of IP address on the inet line (like 10.0.1.5/24), then everything’s working:
$ ip addr 2: enp0s3:: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc [...] inet 10.0.1.5/24 brd 10.0.1.255/ scope global enp0s3 1 valid_lft forever preferred_lft forever
But if there is no inet address, then you’ll need to bring it up manually. Take the interface name (enp0s3, in the example) and use it as an argument for ifconfig up. That tells the interface that it had better wake up and get to work. Running dhclient will then request an IP address on the dmz network from a DHCP server. (Remember how you set --dhcp on when you created the NAT network? That was for a reason.)
# ifconfig enp0s3 up # dhclient enp0s3 1
Finally, run ip addr once again to make sure everything is as it should be. You’ve got yourself a test environment. If you’re not sure you’re quite clear on subnetting and NAT networks, all is not lost: hang on until chapter 14.
A properly configured OpenVPN tunnel can improve security by
- Applying firewall rules to control access between networks
- Isolating network-attached devices through subnetting
- Adding encryption to network connections
- Obscuring traffic moving through a public network
To enable internal routing on a server, you need to uncomment a line in the /etc/sysctl.conf file. Which one?
- net.ipv4.ip_forward=1
- net.ipv4.tcp_syncookies=1
- net.ipv6.conf.all.accept_redirects = 0
- net.ipv4.conf.all.accept_source_route = 0
After installing easy-rsa, where will you find the scripts you’ll use to generate your keys?
- /usr/share/easy-rsa/
- /usr/share/easy-rsa/scripts/
- /usr/share/easy-rsa/examples/
- /usr/share/docs/easy-rsa/
Which of the following scripts will do most of the work generating RSA scripts?
- vars
- build-key-server
- build.ca
- pkitool
After installing OpenVPN, where will you find configuration file templates?
- /usr/share/doc/openvpn/examples/sample-config-files/server.conf/
- /usr/share/doc/openvpn/sample-config-files/server.conf.gz
- /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz
- /usr/share/openvpn/examples/sample-config-files/server/
Which of the following values can be added to the /etc/openvpn/server.conf file to port-forward clients to a web server?
- port-share localhost 80
- proto tcp
- client-to-client
- push "route 10.0.3.0 255.255.255.0"
Which of the following iptables commands will silently block all traffic sent to an interface?
- iptables -P OUTPUT DROP
- iptables -P INPUT DROP
- iptables -P INPUT REJECT
- iptables -P FORWARD DROP
Which of the following Shorewall files is used to set a default access profile for your firewall?
- params
- interfaces
- rules
- policy
c
a
a
d
c
a
b
d
18.222.117.4