Paranoid Penguin - Running Network Services under User-Mode Linux, Part II
Listing 1. Setting Up Bridged Networking
root@host# bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward' root@host# apt-get install bridge-utils uml-utilities root@host# ifconfig eth0 0.0.0.0 promisc up root@host# brctl addbr uml-bridge root@host# brctl setfd uml-bridge 0 root@host# brctl sethello uml-bridge 0 root@host# brctl stp uml-bridge off root@host# ifconfig uml-bridge 192.168.250.250 netmask 255.255.255.0 up root@host# brctl addif uml-bridge eth0 root@host# tunctl -u umluser -t uml-conn0 root@host# chgrp uml-net /dev/net/tun root@host# chmod 660 /dev/net/tun root@host# ifconfig uml-conn0 0.0.0.0 promisc up root@host# brctl addif uml-bridge uml-conn0
The first command enables IP forwarding on your host. Although, technically, bridging happens at a lower level than IP forwarding, they amount to the same thing from the kernel's perspective. Accordingly, if you have a local iptables policy on your host, you'll need to add rules to the FORWARD table to enable traffic to and from the tunnel interfaces you attach to the host's bridge.
The second command (apt-get install...), obviously, installs the Debian packages bridge-utils and uml-utilities. bridge-utilities provides the brctl command, and uml-utilities provides the tunctl command. For these commands to work, your host kernel needs to have been compiled with 802.1d Ethernet bridging, IP tunneling, Bridged IP/ARP packet filtering and Universal TUN/TAP device driver support.
The third command in Listing 1 (ifconfig eth0...) may seem a bit scary. It resets your host's Ethernet interface to a (temporarily) IP-free state. Be prepared for an interruption in local network functionality after you execute this command.
The subsequent six commands, however, will restore it by defining a new virtual bridge device (called uml-bridge), configuring it, assigning your host's IP address to it (192.168.250.250 in this example), and attaching eth0 to it as a virtual bridge port. If the IP address of eth0 on your host was 10.1.1.10 before you reset it to 0.0.0.0, after issuing the first four brctl commands you would use ifconfig uml-bridge 10.1.1.10 netmask 255.255.255.0 up. At this point, your host should be able to interact with the outside world in exactly the same way as it did before (unless of course your local iptables policy doesn't have appropriate FORWARD rules yet).
All right, our host system is now a bridge. All that remains is to attach a tunnel port to it. You should repeat the remaining steps in Listing 1 (starting with tunctl -u...) for each guest system you intend to run.
In the tunctl -u... command, umluser is the name of the unprivileged account you intend to use when executing guest kernels, and uml-conn0 is the name of the new tunnel interface you're creating.
In the subsequent chgrp and chmod commands, we're changing the permissions of the virtual tunnel device, always /dev/net/tun, to be readable and writable by our unprivileged account. In this example, therefore, the account umluser belongs to the group uml-net. (On my real-life test system, I instead used the the group wheel, which my unprivileged account mick belongs to.)
After setting the new tunnel interface's IP address to 0.0.0.0 (just like we did with eth0), we define it as another port on the local bridge with that last brctl command.
That's it! Now when we start the guest system, we add the option eth0=tuntap,uml-conn0 to our kernel command line, which tells the kernel to use the tunnel interface uml-conn0 as its virtual eth0. Our complete example command line, which unlike Listing 1, should be run by a nonprivileged user rather than root, looks like this:
umluser@host$ ./debkern ubd0=debcow,debroot root=/dev/ubda ↪eth0=tuntap,uml-conn0
After the virtual machine starts, you can assign an IP address to (virtual) eth0 via ifconfig, define a default route via route add... (using the same gateway IP that your host system uses), set DNS lookup information in /etc/resolv.conf, and, in short, configure it in precisely the same way that you'd configure a real Debian system.
Once your virtual machine is successfully communicating with your local LAN and beyond, you should immediately configure apt-get and use it to install the latest Debian patches on your virtual guest. You'll need apt-get working anyhow to install the network software you've just gone to all the trouble of building this virtual machine to run. In the case of our example virtual DNS server, these would probably be the Debian packages bind9 and maybe also bind9-doc. Remember, all of these changes will be made to your COW file, so be sure to specify the same COW file on subsequent startups (or merge it into your image via the uml_moo command).
Next time, we'll wrap up this series by discussing additional security controls you can use on your guest systems, a nifty COW file trick or two and, of course, how to create a custom root filesystem image. Until then, be safe!
Resources for this article: /article/9385.
Mick Bauer (firstname.lastname@example.org) is Network Security Architect for one of the US's largest banks. He is the author of the O'Reilly book Linux Server Security, 2nd edition (formerly called Building Secure Servers With Linux), an occasional presenter at information security conferences and composer of the “Network Engineering Polka”.