Tuesday, April 14, 2015

Basic filter rules with Netfilter (iptables)

If you understood how Netfilter works after reading my previous article, you are able to configure some basic rules to filter traffic between two or more networks.

Let's suppose the following scenario: You've got a system with two network interfaces connected to an Intranet (eth0) and to the outside (eth1) and you want only traffic from the former to the later to be allowed, but only through a local proxy.

I'll show you how to do that with some iptables commands stored in a shell script that will use variables so you can easily modify it for your own network.

The file with this shell script must have execution permission for the root and shouldn't be readable for any one else. Save it in the directory /etc/network/if-pre-up.d/ if you're working with Debian or Ubuntu, as it's my case.


# Variables
# Group 1

# Delete any previous ruleset
# Group 2
iptables -F
iptables -X
iptables -Z

# Set the default policy
# Group 3
iptables -P INPUT DROP
iptables -P FORWARD DROP

# Allow traffic for loopback interface
# Group 4
iptables -A INPUT -i lo -j ACCEPT

# Allow HTTP and HTTPS for the local proxy
# Group 5
iptables -A FORWARD -s $LOCALPROXY -p tcp --dport 80 -j ACCEPT
iptables -A FORWARD -d $LOCALPROXY -p tcp --sport 80 -j ACCEPT
iptables -A FORWARD -s $LOCALPROXY -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -d $LOCALPROXY -p tcp --sport 443 -j ACCEPT

# Allow DNS traffic (consider changing LOCALNET for LOCALPROXY)
# Group 6
iptables -A FORWARD -s $LOCALNET -p tcp --dport 53 -j ACCEPT
iptables -A FORWARD -d $LOCALNET -p tcp --sport 53 -j ACCEPT
iptables -A FORWARD -s $LOCALNET -p udp --dport 53 -j ACCEPT
iptables -A FORWARD -d $LOCALNET -p udp --sport 53 -j ACCEPT

# Masquerade local addresses
# Group 7

# Allow routing through network interfaces
# Group 8
echo 1 > /proc/sys/net/ipv4/ip_forward

The lines in group 1 have to be changed with your own settings. Next, group 2 makes the ruleset to be completely deleted. In group 3, the default policy is setup for each chain in the filter table (iptables works with the filter table if no other table is specified). Users trying to reach the outside network won't be able to get a connection and no message will be sent back due to the DROP policy in the FORWARD chain.

After that, group 4 allows any loopback interface traffic (i.e. connections to localhost). Next, in group 5, web traffic is allowed for the local proxy, so users will have to use it in order to navigate. Last filter rules, in group 6, allow dns queries for all the Intranet, but you can consider allowing it for only the local proxy depending on your security policy.

The rules in groups 6 and 7 are coupled, for one allows the traffic from the Intranet to the outside network while the other allows the traffic back to the local network.

The rule in group 7 is the only one in the nat table in this example. It makes the source IP address to be changed for the outside network interface IP address, so the local addresses are masqueraded. This kind of Network Address Translation is named source NAT or SNAT.

Finally, in group 8, the traffic is allowed to be routed between the network interfaces.

Once the shell script is executed, you should get the following output from the command iptables -nL:

Chain INPUT (policy DROP)
target     prot opt source               destination
ACCEPT     all  --  

Chain FORWARD (policy DROP)
target     prot opt source               destination
ACCEPT     tcp  --            tcp dpt:80
ACCEPT     tcp  --          tcp spt:80
ACCEPT     tcp  --            tcp dpt:443
ACCEPT     tcp  --          tcp spt:443
ACCEPT     tcp  --            tcp dpt:53
ACCEPT     tcp  --       tcp spt:53
ACCEPT     udp  --            udp dpt:53
ACCEPT     udp  --       udp spt:53

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Now you can try to establish some connections. Execute telnet www.google.com 80 from the local proxy:

And watch for the incoming and outgoing connexions through the firewall with the command:
watch -n 1 'sudo iptables -nvL | grep 80'

The first column is a counter of packets and the second is a counter of bytes that matched that rule.

Tuesday, April 7, 2015

Understanding how Netfilter (iptables) works

As I've read a lot of articles about Netfilter, aka iptables, and most of them are confusing and hard to read, I better explain in this article the basics of this powerful wide available firewall.

Before you continue reading, be sure you understand how communications work in TCP/IP networks such as Internet or, probably, your own Intranet.

I'm not going to bore you with the history of Netfilter and where it comes from. However, it's important to notice that this firewall is integrated in the Linux kernel, so it's available broadway with no need of extra packages to be installed. In addition, it's quick and strong enough to be used in a wide variety of scenarios.

Network packets can come from, go to or pass through your firewall. Depending on the path it follows, different rules can be applied. Rulesets are named tables and tables are associated to chains. You can create your own chains, but that's not the goal of this article.

In a packet's lifetime, it passes through different chains containing rules, which may change, accept or deny the packet somehow. A policy determines what happens to a packet. There is a default policy for each chain.

The following image shows some of the basic chains and flows:

Basic chains and flows

For a full description of filtering tables, please refer to this page.

Here are some examples of paths a packet could follow:

  • Packets from another system to the firewall: PREROUTING and INPUT.
  • Packets from the firewall to another system: OUTPUT and POSTROUTING.
  • Packets from one system to another through the firewall: PREROUTING, FORWARD and POSTROUTING.
The order of the chains is always the same. For instance, PREROUTING comes always before INPUT and not the other way round.

To interact with Netfilter you can use the command line tool iptables, though it's not the only way.

Please keep in mind that any changes made will be losed after reboot. To avoid that, you can:

  1. Save the tables state to a file with the command iptables-save and restore the rules from the file at startup with iptables-restore, or
  2. Create a shell script with direct iptables commands to be executed at startup.
I prefer the later, for it allows you to use shell variables and to add comments to make the it more readable.

Anyway, the file created one way or the other should have reading permissions for the root only and placed in a secure directory.

When reading or executing the file at startup, it has to be done before the network interfaces are configured, for security reasons. To do so, you can place the rules or a shell script that reads them in the directory /etc/init.d/if-pre-up.d if you're working with Debian.

Monday, July 8, 2013

Synchronizing servers with openntpd

Bad administrators don't pay attention to the date and time of servers until the have to correlate the logs due to some kind of incident and they realize there are differences between them.

If you don't want the forensics analysis to made you crazy, then you have to think about synchronizing the servers time using NTP (Network Time Protocol).

In this article, I'll show you how easy is this task with openntpd.

First of all, let's understand how it works: a client gets the time from a server using NTP, which works over UDP, and changes its time accordingly (just an small portion every time if the difference is too big).

Second, openntpd can be configured as a client, as a server or both. You can install it on all your servers (as clients by default) and they will synchronize with some time servers on Internet. However, I don't recommend you this configuration, for a lot of packets will go to Internet, taking some bandwidth and, in addition, this will depend on the port udp/123 to be opened, which may not work if there is a firewall or proxy.

Third, you may want to have a time server on your local network, so all the other servers will act as clients of this one, which in turn will act as the only client of the time servers on Internet.


In my case, I'm using a Linux Debian 7.0 wheezy box.

Install the openntpd package:

apt-get install openntpd

Edit the configuration file /etc/openntpd/ntpd.conf and add the following line after all the other lines beginning with "listen on", which are commented (by default openntpd isn't executed as a server, thus you have to tell it to listen on some network interface):

listen on x.x.x.x

Substitute x.x.x.x for the IP address of the interface you want the daemon to listen on. I don't recommend you to use the wildcard * to listen on any interface, unless your server has got only one.

You'll see there many lines beginning with "servers". These tell the daemon where to connect to get the time to synchronize.

Now restart the service:

service openntpd restart

And look in the file /var/log/syslog for the line containing "listening on x.x.x.x" and many lines containing "adjusting local clock by" followed by an amount of seconds.

After a while, your server will be almost synchronized and then you can use it for your local network.


For the clients I'm using a Linux Debian 6.0 squeeze box. In this case, the package openntpd is located in the backports repository, so I had to add it to the /etc/apt/sources.list before proceeding with the installation.

Once installed, open the configuration file /etc/openntpd/ntpd.conf and comment all the lines beginning with "server" or "servers". Then, add a line similar to this after the line "server ntp.example.org", which is commented:

server x.x.x.x

Substitute x.x.x.x for the IP address of the server you already configured.

Now restart the service and watch the syslog. You have to see a message like "ntp engine ready" and after it you may see a line containing "reply from x.x.x.x: not synced, next query" followed by a number of seconds. This means that your server is not still synchronized itself so it won't tell you the right time. After the number of seconds indicated, the client will retry.

Once your server is ready to accept requests, you'll see in the client's syslog a line containing "peer x.x.x.x now valid" and then "adjusting local clock by" from time to time.

Saturday, March 30, 2013

RAID configuration with mdadm

In this article, I'll try to explain how to configure any RAID with mdadm in a Linux box with Debian squeeze 6.0.5.

There are many considerations to think about before starting:

  • RAID is not a backup system, it doesn't substitute your backup policies, so you'll have to back up your data anyway.
  • There are many RAID configurations and, in addition, you can combine them. Read about RAID, then make a choice.
  • The utility mdadm allows you to configure a software RAID, so you won't need a special motherboard, as is the case of hardware or firmware RAIDs.
  • The goal is to create a new virtual device file that, when used, will internally use the device file of each partition belonging to the RAID. That's transparent for the user and the system commands like mkfs, mount, etc.
  • The easiest way to begin is with two or more empty partitions. Configuring RAID in a pre-existing filesystem (let's say the one in /dev/sdb1) leads to create the RAID with the rest of the partitions, then copying the data from the original filesystem to the new RAID filesystem (for instance /dev/md1) and, finally, adding it (/dev/sdb1 to /dev/md1).
  • Configuring RAID using the root partition leads to at least one reboot (while the system's running, you can't unmount the original filesystem to mount instead the RAID filesystem).
There are a couple of pre-requisites, too:
  • All partitions must be created in order to have its device file available.
  • Before configuring a RAID for an existing filesystem, back up its data, just in case.
Example 1: configuring RAID 5 on 3 new disks

First of all, check that all 3 devices have a partition you want to use for the RAID 5. In this example, I'll use the first partition of the disks sdb, sdc and sdd.

fdisk -l /dev/sdb /dev/sdc /dev/sdd

Disk /dev/sdb: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x1739a12e

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1         130     1043201   83  Linux

Disk /dev/sdc: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xcdf40006

   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1               1         130     1043201   83  Linux

Disk /dev/sdd: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x257d9f1f

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         130     1043201   83  Linux

When possible, use disks with the same part number or model number. This will increase performance.

Then activate the kernel module for the RAID we want to configure:

modprobe raid5

And install the mdadm package:

apt-get install mdadm

When installing it, two questions will be asked. The first one asks about the MD (Multi Disk) arrays needed to bring up the root filesystem. Leave it blank, for we're not configuring a RAID for it, but for another filesystem.

The second question asks for the rest of MD arrays to be automatically started during the boot process. Answer 'yes'.

Now you're ready to create the RAID 5. Use the command bellow:

mdadm --create /dev/md5 --force --level=5 --raid-disks=3 /dev/sd[b-d]1

mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md5 started.

  • /dev/md5 is whatever name you want for the new device file.
  • force is telling mdadm to not to leave one of the devices in spare (read the mdadm's man page for more information on creating a RAID 5).
  • level is the number corresponding to the RAID you want.
  • raid-disks is the number of initial disks to use (this parameter must be equal to the number of devices plus the missing word used for those not listed).
  • /dev/sd[b-d]1 are the devices to use (write 'missing' if there are devices not listed).
Check that everything's working with:

cat /proc/mdstat

Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] 
md5 : active (auto-read-only) raid5 sdd1[2] sdc1[1] sdb1[0]
      2085888 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]
unused devices: <none>

Look at the UUU. If there was some problem, you'd see something like UU_ or _UU or U_U, depending of the failing disk.

Now a new /dev/md5 device has been created and can be used as any command line parameter. So, let's create the filesystem and the mount point:

mkfs.ext3 /dev/md5
mkdir /data

Edit the fstab to add the line shown bellow:

tail -1 /etc/fstab

/dev/md5 /data ext3 defaults 0 0

And mount the new filesystem, that should look like:

mount /data
df -h /dev/md5

Filesystem            Size  Used Avail Use% Mounted on
/dev/md5              2.0G   36M  1.9G   2% /data

Notice that only 2 GB of the 3 GB are available, for one block every three is the parity block and the other two the data blocks.

Finally, check again the RAID (the resync is already done):

cat /proc/mdstat

Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] 
md5 : active raid5 sdd1[2] sdc1[1] sdb1[0]
      2085888 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]
unused devices: <none>

Example 2: configuring RAID 1 on system disk

To configure RAID 1 or mirroring on the system disk where the root filesystem is, while it's mounted and alive, you have to follow these general steps:
  1. Prepare the second disk (/dev/sdb) with the same layout and MBR (master boot record) as the first (/dev/sda).
  2. Create a RAID 1 with only the second disk (it will appear as degraded), create the filesystem for the RAID and mount it on a temporary mount point.
  3. Prepare the fstab to mount the RAID filesystem and GRUB to look for the kernel at this filesystem (instead of /dev/sda1 or /dev/sdb1).
  4. Copy all the data from the original root partition (/dev/sda1) to the new filesystem (/dev/md1 for instance).
  5. Once finished, reboot and try the new configuration.
  6. Add the root partition to the RAID 1. The syncronization process will start and may need hours to finish, depending on the size of the partition.

Step 1

Copy the partition layout from disk sda to disk sdb:

sfdisk -d /dev/sda | sfdisk --force /dev/sdb

Install mdadm and activate the kernel module raid1 (see the example above for more information). However, in this case, answer 'all' when asked for the MD (Multi Disk) arrays needed to bring up the root filesystem.

Step 2

Create a new RAID device, let's say /dev/md1, with the first real device missing (you'all add it later), as shown bellow:

mdadm --create /dev/md1 --level=1 --raid-disks=2 missing /dev/sdb1

Answer 'yes' when prompted about the boot loader if you're using GRUB.

They will start in a degraded state. You'll see it in an email you'll get in the root account (by default) and by showing the /proc/mdstat virtual file.

cat /proc/mdstat

Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] 
md1 : active (auto-read-only) raid1 sdb1[1]
      3966964 blocks super 1.2 [2/1] [_U]
unused devices: <none>

Now create a temporary mount point, make the filesystem and mount it:

mkdir /mnt/temp
mkfs.ext3 /dev/md1
mount /dev/md1 /mnt/temp

Repeat this step for each filesystem you want to configure RAID for, changing the mount point accordingly, then continue.

To create a RAID 1 for the swap space, check the partition number corresponding to it:

swapon -s

Filename Type Size Used Priority
/dev/sda5   partition 223224 0 -1

Then create the RAID, with a missing device again:

mdadm --create /dev/md/swap --level=1 --raid-disks=2 missing /dev/sdb5

And make the new device a swap space (you'll add it later in the /etc/fstab):

mkswap /dev/md/swap

Step 3

Prepare the /etc/fstab to mount only RAID devices instead of disk partition devices. Yours may look like this:

grep md /etc/fstab

/dev/md1  / ext3 errors=remount-ro 0 1
/dev/md/swap none swap sw 0 0

Now it's time to prepare GRUB so the boot process will look for the kernel in the RAID instead of the disk partition. This will make your system bootable from any disk.

This part depends on the version of GRUB you're using. In my example, it's GRUB 2 (version 1.98).

First, copy the file 40_custom to whatever begins with 07, 08 or 09, so the option menu will be shown before the rest:

cp /etc/grub.d/40_custom /etc/grub.d/09_raid

Then add the following lines to it:

menuentry "Linux Debian squeeze - RAID" {
insmod raid
 insmod mdraid
 insmod part_msdos
 insmod ext2
 set root='(md/1)'
echo "Loading Linux kernel..."
linux /vmlinuz root=/dev/md1 ro quiet
echo "Loading initial ramdisk..."
initrd /initrd.img

It will make the system to go to the MD array number 1 (/dev/md/1) for the information in /boot.

Second, uncomment the line GRUB_DISABLE_LINUX_UUID=true in the file /etc/default/grub to disable the use of UUIDs.

Third, update the file /boot/grub/grub.cfg:


And, last but not least, install GRUB in the sdb disk:

grub-install /dev/sda
grub-install /dev/sdb

GRUB boot menu

To know more about GRUB 2, clic here.

Step 4

Copy all the data from the original filesystem (/dev/sda1 mounted on /) to the new one (/dev/md1 mounted on /mnt/temp):

cp -dpRx / /mnt/temp

This may take several minutes, so stay calm and wait. Once you get again the shell prompt, check that the used space in both filesystems match:

df -h /dev/sda1 /dev/md1

Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             3.8G  628M  3.0G  18% /
/dev/md1              3.8G  628M  3.0G  18% /mnt/temp

Repeat the copy process for each filesystem you are configuring RAID for.

Step 5

Now reboot your system.

The device files in /dev/md are simply symbolic links to /dev:

ls -l /dev/md

total 0
lrwxrwxrwx 1 root root 6 Mar 25 19:58 1 -> ../md1
lrwxrwxrwx 1 root root 8 Mar 25 19:58 swap -> ../md127

Check that the mounted filesystems are mirrored:

mount | grep md

/dev/md1 on / type ext3 (rw,errors=remount-ro)

Check that the swap space is mirrored:

swapon -s

Filename Type Size Used Priority
/dev/md127  partition 223212 0 -1

Step 6

After rebooting, the disk partitions in /dev/sda are not in use anymore and can be added to the corresponding RAID device.

Before doing it, change the type of partition to "Linux raid autodetect" or copy back the layout from sdb to sda (as we did in step 1):

sfdisk -d /dev/sdb | sfdisk --force /dev/sda

Let's begin with the root partition: 

mdadm --add /dev/md1 /dev/sda1

Now the swap space:

mdadm --add /dev/md/swap /dev/sda5

Finally, check the RAID status. You may see how it's syncronizing (it may take a very long time, probably hours):

cat /proc/mdstat

Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] 
md1 : active raid1 sda1[2] sdb1[1]
      3966964 blocks super 1.2 [2/1] [_U]
      [=========>...........]  recovery = 46.4% (1842560/3966964) finish=2.5min speed=13987K/sec
md127 : active raid1 sda5[2] sdb5[1]
      223220 blocks super 1.2 [2/1] [_U]
unused devices: <none>

Once the MD array in the system disk is detected, the file /boot/grub/grub.cfg is already configured to boot from the RAID (see its section 10_linux), so the section you added (09_raid) isn't necessary anymore.

You can remove the file /etc/grub.d/09_raid or simply make it not executable:

rm /etc/grub.d/09_raid

And reconfigure all the GRUB boot process:

grub-install /dev/sda
grub-install /dev/sdb

Monitoring the MD arrays

You can stop an MD array for maintenance with:

mdamd --stop /dev/md5

Then, you'll be able to remove its superblock (otherwise, the system will detect it at startup):

mdadm --zero-superblock /dev/sdb5
mdadm --zero-superblock /dev/sdc5
mdadm --zero-superblock /dev/sdd5

You can examine the MD arrays of any disc:

mdadm --examine --scan

About the configuration file /etc/mdadm/mdadm.conf, it looks like this by default:

# mdadm.conf
# Please refer to mdadm.conf(5) for information about this file.

# by default, scan all partitions (/proc/partitions) for MD superblocks.
# alternatively, specify devices to scan, using wildcards if desired.
DEVICE partitions

# auto-create devices with Debian standard permissions
CREATE owner=root group=disk mode=0660 auto=yes

# automatically tag new arrays as belonging to the local system
HOMEHOST <system>

# instruct the monitoring daemon where to send mail alerts

# definitions of existing MD arrays

# This file was auto-generated on Fri, 29 Mar 2013 13:45:51 +0100
# by mkconf 3.1.4-1+8efb9d1+squeeze1

There you can set up the e-mail address (MAILADDR) where to send any issue relating the MD arrays, the devices to scan (DEVICES)or the information about existing MD arrays. Refer to the man page for further information.

Tuesday, January 8, 2013

DHCP forwarding with a relay server

What if you have several local networks and you don't want a DHCP server on each? Don't worry about that! You only need a single DHCP server and many DHCP relay servers forwarding the requests to it.

I'll explain how to configure both servers using an example of two networks, on which is the main DHCP server, and, on which is the DHCP relay server, as shown in this figure:

Network map

The main DHCP server is an Ubuntu 12.04 precise and the DHCP relay server is a Debian 6.0.5 squeeze. The packages you need to install are:
  • The DHCP server: isc-dhcp-server
  • The DHCP relay server: isc-dhcp-relay
You are supposed to configure the main DHCP server for its own network and, in addition, you'll have to configure it for the other network(s). In my example, the end of the /etc/dhcp/dhcp.conf file looks like this:

option domain-name "local.net";
subnet netmask {
  option routers;
  option domain-name-servers,;
subnet netmask {
  option routers;

This is a very basic configuration and you might want to include more directives for your own networks.

When installing the package isc-dhcp-relay, the setup process will start automatically and it will modify the file /etc/default/isc-dhcp-relay. However, in case you might want to change something later, here's the content of the file for my example:

# Defaults for isc-dhcp-relay initscript
# sourced by /etc/init.d/isc-dhcp-relay
# installed at /etc/default/isc-dhcp-relay by the maintainer scripts

# This is a POSIX shell fragment

# What servers should the DHCP relay forward requests to?

# On what interfaces should the DHCP relay (dhrelay) serve DHCP requests?

# Additional options that are passed to the DHCP relay daemon?

You can specify many DHCP servers to relay to of the interface on which to bind for requests. Just read the man page for more information.

After the changes and restarting both DHCP servers, the clients in the same network as the DHCP relay server should be able to requests an IP address (try with sudo ifup eth0 on the client):

Listening on LPF/eth0/08:00:27:2b:4c:c3
Sending on   LPF/eth0/08:00:27:2b:4c:c3
Sending on   Socket/fallback
DHCPDISCOVER on eth0 to port 67 interval 4
DHCPREQUEST on eth0 to port 67
bound to -- renewal in 248 seconds.

Notice that the IP address was offered by the DHCP relay server, not the main DHCP server.

Now the interface is configured (type sudo ifconfig eth0 on the client):

eth0      Link encap:Ethernet  HWaddr 08:00:27:2b:4c:c3  
          inet addr:  Bcast:  Mask:
          inet6 addr: fe80::a00:27ff:fe2b:4cc3/64 Scope:Link
          RX packets:49 errors:0 dropped:0 overruns:0 frame:0
          TX packets:102 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:8373 (8.1 KiB)  TX bytes:17609 (17.1 KiB)

Just for curiosity, look at the end of the syslog on the main DHCP server and you will read something similar to this:

Jan  7 20:40:30 odin dhcpd: DHCPDISCOVER from 08:00:27:2b:4c:c3 via
Jan  7 20:40:30 odin dhcpd: DHCPOFFER on to 08:00:27:2b:4c:c3 via
Jan  7 20:40:30 odin dhcpd: DHCPREQUEST for ( from 08:00:27:2b:4c:c3 via
Jan  7 20:40:30 odin dhcpd: DHCPACK on to 08:00:27:2b:4c:c3 via
Jan  7 20:44:38 odin dhcpd: DHCPREQUEST for from 08:00:27:2b:4c:c3 via vboxnet0
Jan  7 20:44:38 odin dhcpd: DHCPACK on to 08:00:27:2b:4c:c3 via vboxnet0

Once again, it's the DHCP relay server who made the request on the client's behalf.

Saturday, August 25, 2012

Using views in bind 9

If you need a DNS server resolving different addresses for the same name depending on the IP address of the client that makes the request, then this article may be of your interest.

What you need is to configure views, which allow one single zone to have different information to be served.

This is very useful in the following cases:
  • A DNS server connected to two or more networks with different needs.
  • A DNS server connected to Internet and an Intranet, where the IP addresses are local and you might have more services for private use.
  • A DNS server connected to a single network but receiving requests from other networks for which you may need to resolve differently.
Let's see how views work. For my example, I'll assume you already read the article  Basic DNS configuration, for this is an evolution of the previous configuration.

The steps should be:
  1. Go to /etc/bind and backup the file named.conf.local (just in case, you never know):
    cp named.conf.local named.conf.local.20120825
  2. For every view you can decide which clients will match it with the directive match-clients, where you can specify the addresses directly or use an ACL, as in my example.
  3. The file named.conf.local could be similar to this one:
    // Do any local configuration here

    acl "company-net" {;; };

    view "private" {

        match-clients { "company-net"; };

        zone "yourcompany.com" {
            type master;
            file "/etc/bind/db.yourcompany.com-private";


    view "public" {

        match-clients { any; };

        zone "yourcompany.com" {
            type master;
            file "/etc/bind/db.yourcompany.com-public";


    // Consider adding the 1918 zones here, if they are not used in your
    // organization
    //include "/etc/bind/zones.rfc1918";
  4. In this case, request from clients on the networks and will get the information of the file db.yourcompany.com-private and the rest from the file db.yourcompany.com-public.
  5. Now copy the original file to db.yourcompany.com-private (just in case) and to db.yourcompany.com-public (this is the one we'll modify in a minute).
    sergio@zeus:/etc/bind$ sudo cp db.yourcompany.com db.yourcompany.com-private
    sergio@zeus:/etc/bind$ sudo cp db.yourcompany.com db.yourcompany.com-public
  6. We'll keep unmodified the private zone file and modify the public zone file to have less records and different IP addresses for the services. The result is:
    ; BIND direct file for yourcompany.com zone
    $TTL    86400
    @       IN    SOA    zeus.yourcompany.com. hostmaster.yourcompany.com. (
                      1        ; Serial
                  43200        ; Refresh (12h)
                   3600        ; Retry (1h)
                2419200        ; Expire (2 weeks)
                  86400 )      ; Negative Cache TTL
    @       IN    NS    zeus.yourcompany.com.
    zeus    IN    A
    atila   IN    A
    www     IN    CNAME atila
  7. Compare the two files to see the differences. Go to Basic DNS configuration to see the original.
  8. At this point, if you restart bind, you'll have trouble because ALL the zones MUST be in VIEWS.
  9. This means that all the includes in the file named.conf must lead to zones in views. Therefore, you should modify the file named.conf.default-zones accordingly to what has been explained (you could include all the default zones in a general view allowing the access to any client, for instance).
  10. Backup the file:
    sudo cp named.conf.default-zones named.conf.default-zones.20120825
  11. Modify it just adding the first 2 lines at the top and don't forget the closing }; at the bottom:
    view "general" {

    match-clients { any; };

    // prime the server with knowledge of the root servers
    zone "." {
        type hint;
        file "/etc/bind/db.root";

    // be authoritative for the localhost forward and reverse zones, and for
    // broadcast zones as per RFC 1912

    zone "localhost" {
        type master;
        file "/etc/bind/db.local";

    zone "127.in-addr.arpa" {
        type master;
        file "/etc/bind/db.127";

    zone "0.in-addr.arpa" {
        type master;
        file "/etc/bind/db.0";

    zone "255.in-addr.arpa" {
        type master;
        file "/etc/bind/db.255";

  12. Now restart the DNS server:
    sudo service bind9 restart
  13. And make some queries to check it. From the private network (using a client with IP address we can resolve www and ramses, getting their address in this network:
    sergio@odin:~$ sudo ifconfig vboxnet0
    vboxnet0  Link encap:Ethernet  direcciónHW 0a:00:27:00:00:00 
              Direc. inet:  Difus.:  Másc:
              Dirección inet6: fe80::800:27ff:fe00:0/64 Alcance:Enlace
              Paquetes RX:0 errores:0 perdidos:0 overruns:0 frame:0
              Paquetes TX:73 errores:0 perdidos:0 overruns:0 carrier:0
              colisiones:0 long.colaTX:1000
              Bytes RX:0 (0.0 B)  TX bytes:13181 (13.1 KB)
    sergio@odin:~$ nslookup
    > server zeus
    Default server: zeus
    > www.yourcompany.com
    Server:        zeus

    www.yourcompany.com    canonical name = atila.yourcompany.com.
    Name:    atila.yourcompany.com
    > ramses.yourcompany.com
    Server:        zeus

    Name:    ramses.yourcompany.com
    > exit
  14. However, from outside the private network we get the public IP address of www but not ramses, which wasn't in the public zone data file:
    coord@mudel:~$ sudo ifconfig eth0
    eth0      Link encap:Ethernet  HWaddr 08:00:27:d3:f8:c1 
              inet addr:  Bcast:  Mask:
              inet6 addr: fe80::a00:27ff:fed3:f8c1/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:1077 errors:0 dropped:0 overruns:0 frame:0
              TX packets:598 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:1269977 (1.2 MiB)  TX bytes:52897 (51.6 KiB)

    coord@mudel:~$ nslookup
    > server zeus
    Default server: zeus
    > www.yourcompany.com
    Server:        zeus

    www.yourcompany.com    canonical name = atila.yourcompany.com.
    Name:    atila.yourcompany.com
    > ramses.yourcompany.com
    Server:        zeus

    ** server can't find ramses.yourcompany.com: NXDOMAIN
    > exit

Sunday, August 19, 2012

Basic DNS configuration

Let's suppose your company acquired a new domain (mycompany.com) to address its Internet services and it's asking you to configure a DNS server to resolve the names of this services, which probably are www, mail, smtp, pop3, etc.

The steps you should follow are:
  1. Install the bind DNS server in your Linux box (a Debian 6.0.5 squeeze in my case). With
    sudo apt-get install bind9
    you will install the latest bind 9 release (9.7.3 in my case).
  2. Don't configure bind from empty files, for there are a lot of mistakes you will make even if you don't realize you made them. Avoid headaches. Instead, use the example file /etc/bind9/db.empty which already contains the SOA and NS records.
  3. From this file create both files, direct and reverse resolution:
    sergio@zeus:/etc/bind$ sudo cp db.empty db.yourcompany.com
    sergio@zeus:/etc/bind$ sudo cp db.empty db.56.168.192
    Note that in my case the LAN is
  4. Edit the direct resolution file (db.yourcompany.com). Modify the SOA record and add the other records you may need. Then, it should be similar to this one:
    ; BIND direct file for yourcompany.com zone
    $TTL    86400
    @       IN      SOA     zeus.yourcompany.com. hostmaster.yourcompany.com. (
                                  1         ; Serial
                              43200         ; Refresh (12h)
                               3600         ; Retry (1h)
                            2419200         ; Expire (2 weeks)
                              86400 )       ; Negative Cache TTL
    @       IN      NS      zeus.yourcompany.com.
    @       IN      MX      10 mail.yourcompany.com.
    zeus    IN      A
    atila   IN      A
    ramses  IN      A
    www     IN      CNAME   atila
    mail    IN      CNAME   ramses
    smtp    IN      CNAME   ramses
    pop3    IN      CNAME   ramses
    I have changed some of the parameters of the SOA record, although it's unnecessary if you don't have a secondary or slave server.
  5. Now edit the reverse resolution file (db.56.168.192). Modify the SOA record and add the PTR records. The result should be something similar to:
    ; BIND reverse file for 56.168.192.in-addr.arpa IPv4 zone
    $TTL    86400
    @       IN      SOA     zeus.yourcompany.com. hostmaster.yourcompany.com. (
                                  1         ; Serial
                              43200         ; Refresh (12h)
                               3600         ; Retry (1h)
                            2419200         ; Expire (2 weeks)
                              86400 )       ; Negative Cache TTL
    @       IN      NS      zeus.yourcompany.com.
    101     IN      PTR     zeus.yourcompamy.com.
    102     IN      PTR     atila.yourcompamy.com.
    103     IN      PTR     ramses.yourcompamy.com.
    Be aware of ending the FQDN with a dot if you don't want the name of the zone to be append.
  6. Edit the file named.conf.local, where you'll configure bind to access the files already created.
    // Do any local configuration here

    zone "yourcompany.com" {
            type master;
            file "/etc/bind/db.yourcompany.com";

    zone "56.168.192.in-addr.arpa" {
            type master;
            file "/etc/bind/db.56.168.192";

    // Consider adding the 1918 zones here, if they are not used in your
    // organization
    //include "/etc/bind/zones.rfc1918";
  7. Now you might want to modify the loopback resolution files (db.local for direct and db.127 for reverse) and the broadcast reverse resolution files (db.0 and db.255) the same way you did with your zones files. Don't forget to add them up to the file named.conf.local.
  8. Restart the service with the command
    sudo service bind9 restart
    where sudo is necessary if you're not root (which I would recommend).
  9. And try to ask the server about your configuration using nslookup:
    sergio@zeus:/etc/bind$ nslookup
    > server
    Default server:
    > www.yourcompany.com

    www.yourcompany.com    canonical name = atila.yourcompany.com.
    Name:    atila.yourcompany.com
    > ramses.yourcompany.com

    Name:    ramses.yourcompany.com
    Address:    name = atila.yourcompamy.com.
    > pop3.yourcompany.com

    pop3.yourcompany.com    canonical name = ramses.yourcompany.com.
    Name:    ramses.yourcompany.com
    > set q=MX
    > yourcompany.com

    yourcompany.com    mail exchanger = 10 mail.yourcompany.com.
    > exit
  10. If you want your DNS server to query itself, just configure the /etc/resolv.conf file to look like this:
    domain yourcompany.com
    search yourcompany.com