Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – I

This post and two following ones are about some simple iptables exercises concerning Linux virtual bridges. Linux bridges are typically used in virtualization environments. However, guest systems or even the host attached to a Linux bridge may become targets of “man in the middle” attacks. During such attacks the guests and the bridge may be manipulated to send packets to the “man in the middle” system and not directly to the intended communication partners. My objective is to get a clearer picture of iptables’ contributions to defense measures against such attacks.

Some “Howtos” on the Internet warn explicitly against using iptables at all on Linux bridges – especially not with active connection tracking. An example is the “libvirt wiki”: http://wiki.libvirt.org/page/Networking. Some of the warnings refer to an original discussion published here: http://patchwork.ozlabs.org/ patch/ 29319/
See also: https://bugzilla.redhat.com/ show_bug.cgi?id=512206

I think these concerns justify a closer look at iptables rules with respect to bridge ports. Comments are welcome.

Scenarios, limitations and objectives

In our test case we work with a KVM host with one bridge and later on also with two linked Linux bridges. In this first article we use one of the Linux guests on one of the bridges to initiate a “man in the middle attack” [MiM] against other guests of the very same bridge. The attacks are based on ARP spoofing and packet redirection. We then define some reasonable iptables rules with the intention to block the redirected traffic (to the MiM) and analyze the impact of these rules.

In a 2nd and 3rd article we extend the game to 2 bridges and the host attached to a port of one of the bridges.

Limitations and restrictions
It is obvious that we cannot prevent ARP-spoofing itself with iptables. iptables works on network layers 3/4, but not on layer 2 (Ethernet). iptables, therefore, does not allow for direct restrictions regarding the ARP protocol. So, the prevention of ARP packets with false MAC addresses, which typically initiate a MiM attack is not the objective of this article. It requires ebtables and/or arptables to block ARP spoofing at its roots. So, do not misunderstand me:

I do not and would not recommend to base any packet filter security across a Linux bridge on iptables alone. If you must use netfilter on bridges always combine iptables with basic ebtables/arptables rules – and test thoroughly against different kinds of attacks which try to break guest isolation. Always be aware of the fact that a bridge creates a global context in which packets must be inspected and followed precisely in their changing role as outgoing or incoming with respect to the bridge itself and its virtual interface ports. Global connection tracking on the TCP/IP level may be dangerous. If you give the bridge itself an IP – a situation which I do not at all like from a security perspective – take extra care. Things get even more complicated with multiple bridges on one and the same host.

Objectives
Nevertheless I think that one can learn something even from academic and unusual test configurations with iptables alone in place:

If we cannot prevent ARP spoofing itself by iptables – can we at least use iptables rules to deal with some consequences of ARP spoofing? More precisely:
Can we block the redirection of packets between ARP poisoned guests over the MiM system by means of IPtables alone? What relations of IP addresses and port devices have to be defined? And would a tool like FWbuilder support us reasonably enough with this task?

If so: How would we extend IPtables rules to situations

  • where two Linux bridges are linked (by veth devices)
  • or when segregated network parts with all guests belonging to the same logical IP segment are coupled via STP and border Ethernet interfaces of a central Linux bridge?

In both cases the spoofed communication may pass border NICs of a Linux bridge.

In this first article on the topic we look at one bridge alone with three guests. In the following posts we shall consider linked bridges.

One bridge – 3 guests

Let us assume that we have 3 guests “kali3, kali4 and kali5” on a Linux bridge “virbr6”. The bridge device itself has no IP. The guest systems are attached to the bridge via standard tap device ports (vk63, vk64 and vk65, respectively). The virtual network can be created e.g. with the help of libvirt’s virt-manager. See article KVM/qemu, libvirt, virt-manager – persistent names for virtual network bridge ports of guest systems about how to set persistent names for the bridge sided end of “tap”-devices.

The corresponding Ethernet interfaces (eth0) of the guest operative systems – i.e. the guest side of the tap devices – are given the following IP addresses: 192.168.50.13 (eth0), 192.168.50.14 (eth0) and 192.168.50.15 (eth0), respectively.

bridge

How does the host see the bridge-ports?

mytux:~ # brctl showmacs virbr6
port no mac addr                is local?       ageing timer
  1     52:54:00:8e:f2:d7       yes                0.00
  2     5e:f4:32:30:f1:3a       yes                0.00
  2     aa:bf:ba:dc:52:31       no                 1.35
  3     fe:54:00:9f:5d:c1       yes                0.00
  4     fe:54:00:74:60:4a       yes                0.00
  5     fe:54:00:0f:34:4f       yes                0.00

5 ports instead of 3 ? Yeah, actually my virbr6 bridge is connected to another bridge (virbr4) by a veth pair. But we will ignore this connection most of the time ignore in this post. If you are interested in Linux bridge linking via “veth” devices see
Fun with veth devices, Linux virtual bridges, KVM, VMware – attach the host and connect bridges via veth

The veth pair explains the 2 MACs on port Nr. 2 of the bridge. A parallel look at the outcome of “ifconfig” or “ip link show” would show that port 3 actually corresponds to tap device “vk63”, port 4 corresponds to “vk64” and port 5 to “vk65”. And what about port 1? The Linux bridge itself could also work as an Ethernet device which could get an IP address on the host. We do not use this property here – nevertheless, there is an Ethernet port associated with the bridge itself.

How does the host see the (regular) IP-MAC relations so far? After pinging our 3 guests from the host we get:

mytux:~ # brctl showmacs virbr6
port no mac addr                is local?       ageing timer
  1     52:54:00:8e:f2:d7       yes                0.00
  2     5e:f4:32:30:f1:3a       yes                0.00
  2     aa:bf:ba:dc:52:31       no                 1.35
  3     fe:54:00:9f:5d:c1       yes                0.00
  4     fe:54:00:74:60:4a       yes                0.00
  5     fe:54:00:0f:34:4f       yes                0.00

mytux:~ # arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.50.15            ether   52:54:00:0f:34:4f   C                     vmh2
....
192.168.50.13            ether   52:54:00:9f:5d:c1   C                     vmh2
192.168.50.14            ether   52:54:00:74:60:4a   C                     vmh2

We recognize our tap devices attached to the bridge. [By the way: vmh2 is a device that connects the host to one of the bridges (virbr4).]

Addendum 24.02.2016: Note a small, but decisive difference in the HW/MAC addresses
The first digit pair in the Ethernet address of the port device (i.e. the bridge sided end of the tap device) has a “fe“, whereas the Ethernet device of the Linux guest (i.e. the guest sided end of the tap device) has a “52“. The rest of the digits being the same. Logically, and also from the perspective of the bridge, these are 2 different (!) devices (though incorporated in one virtual tap). From the point of view of the bridge multiple MACs or even a new bridge may be located at the Ethernet segment behind a port.

Be aware of the fact that the so called “forward database” of a bridge [FDB], which relates MACs to ports, keeps track of the relation of our Linux guest MACs to their specific ports. Whereas the port MAC (with the leading “fe”) is permanently associated with the bridge, the MAC of the guest may disappear from the FDB after a timeout period, if no packets are received at the bridge from this guest MAC address.

In addition we make the following settings:

mytux:~ # brctl setageing virbr6 30
mytux:~ # brctl setageing virbr4 30

to be sure that the bridge works in a switch like mode and not as a hub.

Note that this defines a timeout period for the bridge’s FDB – i.e. after this period “stale” entries in the FDB of the bridge may be deleted. So the bridge may no longer know at which port the deleted MAC is located – and therefore temporarily flood all ports with packets. Therefore, bridge flooding is a situation we may need to cover with iptables-rules later on.

If you want to monitor changes of the bridges’ FDB or monitor general changes over all bridge links use the following commands:

bridge monitor all

and

bridge -statistics fdb show

The continuous output of the first command will show you directly when a stale MAC entry in the FDB is deleted. If you issue the second command twice with a reasonable time period in between you may search the output for missing or new MAC entries of guests.

Note further that we did not give the bridge itself any IP address! The bridge may therefore be called “transparent”. As “virbr6” has no IP address the guests (kali3 to 5) can not directly communicate with the host through the bridge itself as an Ethernet device. Just for information: In our scenario the host can only be reached indirectly over a transparently linked second bridge (virbr4) and a further veth pair there which leads to an external Ethernet device with address 192.168.50.1.

ICMP packages and regular pinging – what do we allow?

First we shall have a look at ICMP packages, only. Our basic policy with iptables is that we deny everything that is not explicitly allowed. Regarding further rules we should be aware of the following:

When setting up iptables rules on bridges we must be precise and specific with respect to the packet direction across the involved bridge port interfaces.
Note: It is the perspective of the bridge and NOT the perspective of the guest that counts.
I always use a 3D picture to be sure: Assume the bridge and its ports to be located above the guests. Then a packet going up is incoming, a packet moving downwards is outgoing.

Let us assume you want to ping from kali3 to kali5. From the point of view of the bridge there are 2 packet directions involved: We first get an incoming ICMP (type 8) packet via bridge interface “vk63”, which then is directed (or “forwarded”) outwards through “vk65”. To allow for the pinging we would need rules of the logical form

bridge vibr6 rule : src 192.168.50.13, dest 192.168.50.15 – ICMP in via vk31, out
via vk65 => ALLOW

and analogously for the other guests and interfaces. Actually this rule may be split up into 2 subsequent rules:

bridge vibr6 rule :  src 192.168.50.13, dest 192.168.50.15 - ICMP in via vk31 => ALLOW  
bridge vibr6 rule :  src 192.168.50.13, dest 192.168.50.15 - ICMP out via vk65 => ALLOW  

which are to be considered in the basic chains of iptables. This leads us to the next question: Which of the iptables chains is relevant here?

In our example it is the FORWARD chain. For the interaction of netfilter components (ebtables/iptables) in kernels with activated netfilter see the following link: http://ebtables.netfilter.org/ br_fw_ia/ br_fw_ia.html

That we need to set up FORWARD rules is also logical as the bridge does nothing else than forwarding packets between its ports and thus transfers the packets to attached destination guests or into segregated network parts behind some of the ports (with the “spanning tree protocol” STP set to ON).

ARP spoofing and the bridge

Consider a situation in which guest “kali4” acts as a “man in the middle”, who wants to sniff or even manipulate the traffic (e.g. for “secrets”) between kali3 and kali5. A user with root rights on kali4 would use a ARP spoofing tool like “dsniff” to (arp-) poison its neighbouring guests via the following command sequence:

root@kali4: ~# echo 1 > /proc/sys/net/ipv4/ip_forward
root@kali4: ~# iptables -A OUTPUT -p icmp --icmp-type redirect -j REJECT
root@kali4: ~# arpspoof -i eth0 -t 192.168.50.13 192.168.50.15 & 2> /dev/null    
root@kali4: ~# arpspoof -i eth0 -t 192.168.50.15 192.168.50.13 & 2> /dev/null   

The first command guarantees that redirected and sniffed packets are forwarded (routed) via the MiM system (kali4) to their original targets. The second command on the MiM-system avoids sporadic ICMP “redirect” answers to the poisoned and pinging guests – such answers would/could indicate to these guests that something is wrong. The 3rd and the 4th command eventually poison the internal ARP caching tables of the guests. I.e., these commands spoil the cached information on IP-MAC relations after some time.

Let us look at kali3 – before the attack:

kali3_arp

And during the attack:

kali3_arp2

In a previous post of this blog we saw that a Linux bridge learns about the relation of MAC addresses and bridge ports – and thus pins a specific communication down to just the 2 involved ports of a specific communication (basic guest isolation). The bridge normally does not spread communication packets over all ports (at least with a setageing parameter > 0).

Note that this does not help to prevent MiM attacks. As the bridge itself works on layer 2 it ignores IP-MAC relations during packet forwarding. (It may learn about IP-MAC relations only through the ARP protocol.) The bridge furthermore does not know whether routing may occur somewhere. And the guests themselves cannot ignore that situations where several IP addresses may be associated with one and the same MAC are possible. Because of all these reasons Ethernet packets are inevitably sent and forwarded across the bridge were the guests think they should be sent to – according to their own internal ARP tables, which are poisoned during the attack.

Therefore after ARP spoofing the bridge would receive 2 subsequent ping request packets from kali3 and from kali4 with the logical route

src 192.168.50.13, dest 192.168.50.15 - ICMP ping request in via vk63, out via vk64   
src 192.168.50.13, dest 192.168.50.15 - ICMP ping request in via vk64, out via vk65   

And the ping answers back via

src 192.168.50.15, dest 192.168.50.13 - ICMP ping answer in via vk65, out via vk64  
src 192.168.50.15, dest 192.168.50.13 - ICMP ping answer in via vk64, out via vk63  

A small side aspect: I should mention that despite the switch-like operational mode of the Linux bridge, I sometimes – very rarely – saw that even the KVM host reacted towards the ARP poisoning and showed some wrong entries in its internal ARP cache table – some time after the attack started. I have not clarified, yet, what the reason for this change of the hosts ARP table actually is. If some reader knows the reason please write me a mail. I suspect gratuitous packets, or (more likely) some rare hub like flooding situation on the bridge, but …

E.g. after a restart of all virtual machines, the begin of the ARP poisoning and after pinging the host continuously from the MiM system for a while, you may eventually find the following ARP table change on the KVM host:

mytux:~ # arp
Address                  HWtype  HWaddress           Flags Mask            Iface   
192.168.50.15            ether   52:54:00:0f:34:4f   C                     vmh2
192.168.50.13            ether   52:54:00:9f:5d:c1   C                     vmh2
192.168.50.14            ether   52:54:00:74:60:4a   C                     vmh2
mytux::~ # arp
Address                  HWtype  HWaddress           Flags Mask            Iface  
192.168.50.15            ether   52:54:00:74:60:4a   C                     vmh2
192.168.50.13            ether   52:54:00:74:60:4a   C                     vmh2
192.168.50.14            ether   52:54:00:74:60:4a   C                     vmh2

And the port-MAC-association? It remains as it was:

mytux:~ # brctl showmacs virbr6
port no mac addr                is local?       ageing timer   
  ...
  5     fe:54:00:0f:34:4f       yes                0.00
  4     fe:54:00:74:60:4a       yes                0.00
  3     fe:54:00:9f:5d:c1       yes                0.00

Be aware, however, of the fact that this information tells us nothing about the present state of the FDB table of the bridge! Actually, due to our “setageing” parameter certain MAC addresses of guests may drop out of the forward list of the bridge, if the guests are inactive with respect to network communication, and this in turn may result in a subsequent (temporary) bridge port flooding.

So, if you stop the ARP poisoning, reset the ARP tables and start the spoofing again, an ARP poisoning of the host itself it may not happen directly. It may, however, happen after some time. (By the way: Any direct pinging from the host to the guests will correct the ARP table to the real values again – at least for some time.)

Anyway and whatever the precise reason – it is interesting that there obviously are circumstances under which the local poisoning of bridge guests may impact even the ARP table on the bridge’s host itself. On the defense side this may give us a secondary chance (besides monitoring the violation of iptables and ebtables rules) to detect ARP spoofing attacks: by monitoring the host’s internal ARP table and analyzing its contents for implausible changes.

iptables rules to prevent misguided packets

To avoid part of the redirected packet transport across the Linux bridge we would require a rule of the logical form

bridge vibr6 rule :  src any, dest 192.168.50.15 - in any, out via vk64 => DENY  

We can reformulate the rule with a negation (!) in a more general way:

bridge vibr6 rule :  src any, !dest 192.168.50.14 - in any, out via vk64 => DENY  

In addition it is reasonable to forbid packets which (seem to) come from kali3 and are “outbound” to kali3:

bridge vibr6 rule :  src 192.168.50.13, dest any - in any, out via vk63 => DENY  

Also incoming packets via vk63 from sources not being kali3 make no sense:

bridge vibr6 rule :  ! src 192.168.50.13, dest any - in vk63, out any => DENY  

Actually, on our bridge we would have to cover analogous variants of all of the above DENY rules for all other guest ports and protocols.

Note that all these rules define fixed relations between each of the defined bridge ports, an associated IP and certain packet directions across the port: with iptables alone we are restricted to such types of relations.

Graphical help – FWbuilder

A problem with the relations above is that they are potentially many – depending at least quadratically on the number of guests on a bridge. An efficient administration requires either a tool or good scripting experience or both. A tool like FWbuilder at least supports us graphically:

fwb_4

The rules created for the shown conditions look like:

    # 
    # Rule 2 (vk63)
    # 
    echo "Rule 2 (vk63)"
    # 
    $IPTABLES -N Out_RULE_2
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk63 !  -d 192.168.50.13   -j Out_RULE_2    
    $IPTABLES -A Out_RULE_2  -j LOG  --log-level info --log-prefix "RULE 2 -- DENY "
    $IPTABLES -A Out_RULE_2  -j DROP
    # 
    # Rule 3 (vk64)
    # 
    echo "Rule 3 (vk64)"
    # 
    $IPTABLES -N Out_RULE_3
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk64 !  -d 192.168.50.14   -j Out_RULE_3    
    $IPTABLES -A Out_RULE_3  -j LOG  --log-level info --log-prefix "RULE 3 -- DENY "
    $IPTABLES -A Out_RULE_3  -j DROP
    # 
    # Rule 4 (vk65)
    # 
    echo "Rule 4 (vk65)"
    # 
    $IPTABLES -N Out_RULE_4
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk65 !  -d 192.168.50.15   -j Out_RULE_4
    $IPTABLES -A Out_RULE_4  -j LOG  --log-level info --log-prefix "RULE 4 -- DENY "
    $IPTABLES -A Out_RULE_4  -j DROP
    # 
    .....
    .....
    # Rule 6 (vk63)
    # 
    echo "Rule 6 (vk63)"
    # 
    $IPTABLES -N Out_RULE_6
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk63  -s 192.168.50.13   -j Out_RULE_6
    $IPTABLES -A Out_RULE_6  -j LOG  --log-level info --log-prefix "RULE 6 -- DENY "
    $IPTABLES -A Out_RULE_6  -j DROP
    # 
    # Rule 7 (vk64)
    # 
    echo "Rule 7 (vk64)"
    # 
    $IPTABLES -N Out_RULE_7
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk64  -s 192.168.50.14   -j Out_RULE_7
    $IPTABLES -A Out_RULE_7  -j LOG  --log-level info --log-prefix "RULE 7 -- DENY "
    $IPTABLES -A Out_RULE_7  -j DROP
    # 
    # Rule 8 (vk65)
    # 
    echo "Rule 8 (vk65)"
    # 
    $IPTABLES -N Out_RULE_8
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk65  -s 192.168.50.15   -j Out_RULE_8
    $IPTABLES -A Out_RULE_8  -j LOG  --log-level info --log-prefix "RULE 8 -- DENY "
    $IPTABLES -A Out_RULE_8  -j DROP
    # 
    ....
    ....
    # 
    # Rule 11 (vk63)
    # 
    echo "Rule 11 (vk63)"
    # 
    $IPTABLES -N In_RULE_11
    $IPTABLES -A INPUT -m physdev --physdev-in vk63 !  -s 192.168.50.13   -j In_RULE_11
    $IPTABLES -A FORWARD -m physdev --physdev-in vk63 !  -s 192.168.50.13   -j In_RULE_11
    $IPTABLES -A In_RULE_11  -j LOG  --log-level info --log-prefix "RULE 11 -- DENY "
    $IPTABLES -A In_RULE_11  -j DROP
    # 
    # Rule 12 (vk64)
    # 
    echo "Rule 12 (vk64)"
    # 
    $IPTABLES -N In_RULE_12
    $IPTABLES -A INPUT -m physdev --physdev-in vk64 !  -s 192.168.50.14   -j In_RULE_12
    $IPTABLES -A FORWARD -m physdev --physdev-in vk64 !  -s 192.168.50.14   -j In_RULE_12
    $IPTABLES -A In_RULE_12  -j LOG  --log-level info --log-prefix "RULE 12 -- DENY "
    $IPTABLES -A In_RULE_12  -j DROP
    # 
    # Rule 13 (vk65)
    # 
    echo "Rule 13 (vk65)"
    # 
    $IPTABLES -N In_RULE_13
    $IPTABLES -A INPUT -m physdev --physdev-in vk65 !  -s 192.168.50.15   -j In_RULE_13
    $IPTABLES -A FORWARD -m physdev --physdev-in vk65 !  -s 192.168.50.15   -j In_RULE_13
    $IPTABLES -A In_RULE_13  -j LOG  --log-level info --log-prefix "RULE 13 -- DENY "
    $IPTABLES -A In_RULE_13  -j DROP

Ignoring some optimization potential, this is actually what we need. You see the clue:
FWbuilder knows about the bridge situation (see below) and creates rules with options

-m physdev –physdev-in/out device

The documentation from http://www.fwbuilder.org/ 4.0/docs/ users_guide5/ host-interface.shtml says accordingly:

Bridge port: This option is used for a port of a bridged firewall. The compilers skip bridge ports when they pick interfaces to attach policy and NAT rules to. For target firewall platforms that support bridging and require special configuration parameters to match bridged packets, compilers use this attribute to generate a proper configuration. For example, in case of iptables, the compiler uses -m physdev –physdev-in or -m physdev –physdev-out for bridge port interfaces. (This object applies to firewall objects only.)

It requires, however, a special configuration of FWbuilder with respect to the defined interfaces and the bridges on the firewall system – i.e. the virtualization host in our test situation:

fwb_4

The same of course for bridge “virbr6”.

Note that our rules (produced by FWbuilder above) for the bridge ports vk63, vk64, vk65 would also work in case of a port flooding situation – if they are not circumvented by other leading rules. The latter being a point we shall come back to.

What packets do we allow?

On a firewall with a basic drop policies we need, of course, to define acceptance conditions for packets, too. Without going into details we need logical rules like:

bridge vibr6 rule :  src 192.168.50.13, dest 192.168.50.15, 192.168.50.14, any ICMP - in via vk31   => ALLOW  

An example is shown here:

fwb_5

# Rule 21 (vk63)
    # 
    echo "Rule 21 (vk63)"
    # 
    $IPTABLES -N In_RULE_21
    $IPTABLES -A FORWARD -m physdev --physdev-in vk63 -p icmp  -m icmp  -s 192.168.50.13   -d 192.168.50.1   --icmp-type any  -m state --state NEW  -j In_RULE_21   
    $IPTABLES -A INPUT -m physdev --physdev-in vk63 -p icmp  -m icmp  -s 192.168.50.13   -d 192.168.50.1   --icmp-type any  -m state --state NEW  -j In_RULE_21   
    $IPTABLES -N Cid8093X19506.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vk63 -p icmp  -m icmp  -s 192.168.50.13   --icmp-type any  -m state --state NEW  -j Cid8093X19506.0
    $IPTABLES -A Cid8093X19506.0  -d 192.168.50.12   -j In_RULE_21
    $IPTABLES -A Cid8093X19506.0  -d 192.168.50.14   -j In_RULE_21
    $IPTABLES -A Cid8093X19506.0  -d 192.168.50.15   -j In_RULE_21
    $IPTABLES -A In_RULE_21  -j LOG  --log-level info --log-prefix "RULE 21 -- ACCEPT "
    $IPTABLES -A In_RULE_21  -j ACCEPT

We need of course all variants for all the other bridge interfaces. To make life simpler you could define groups of recipients in a tool like FWbuilder.

Order of our rules

We eventually come to a trivial but important point: In which order must we arrange the discussed iptables DENY and ACCEPT commands? A little thinking shows:

We need the “DENY”-rules first before we allow anything else – i.e. we need the basic DENY rules discussed above as the leading rules in all affected chains!

If a packet is first allowed – e.g. due to some reasonable IN rule – then it definitely is allowed. To be on the safe side we, therefore, must probe the critical FORWARD rules for unacceptable outgoing and incoming packets over certain bridge ports, first.

A really critical aspect in the context is a potentially applied overall acceptance of packets for established connections (connection tracking). For most stateful inspection packet filters the general acceptance of incoming packets for established connections is a default.

E.g., in FWbuilder you have to turn this policy off explicitly, if you do not want to have it. Otherwise, FWbuilder will create general acceptance rules for all 3 basic chains ahead of all other rules:

    # ================ Table 'filter', automatic rules
    # accept established sessions
    $IPTABLES -A INPUT   -m state --state ESTABLISHED,RELATED -j ACCEPT   
    $IPTABLES -A OUTPUT  -m state --state ESTABLISHED,RELATED -j ACCEPT 
    $IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT   

Note, that these rules would cover ALL bridges and ALL related interfaces/ports on a virtualization host (global context of acceptance)! This makes such leading rules potentially dangerous on hosts with bridges! Both during ARP spoofing attacks, but also in port flooding situations – as the ports work in a promiscuous mode. Be aware of the fact that the attack pattern discussed above could in principle be extended to guests on other bridges on the host, if the attacker knew the relevant IP addresses.

On the other side acceptance rules for established connections actually can really be convenient. My conclusion is: Either you use a set of very general iptables rules that require no connection tracking on the bridge at all – and then your guest systems must establish their own firewalls. Or :

Whatever your FW-Tool generates: Edit the resulting script and move the acceptance rule for established connections after/below the set of critical “DENY” rules on the bridge interfaces discussed above. Check in addition that the DENY rules themselves really are set as stateless rules.

Testing

Let us say kali3 pings kali5 after ARP poisoning. What can the MiM on “kali4” really see then – if no firewall rules are implemented on the host? As expected all and everything:

kali4_wshark_1

You see the poisoning packets and the redirected (duplicated) messages between kali3 and kali5. The same would of course be true for any kind of real TCP/IP communication. So without any measures the MiM can follow all communication after spoofing.

Now let us implement the iptables rules discussed above. In our test case we expect our “rule 3” to block the redirected (misguided) traffic to kali4. And really:

kali3_ping1

And at the same time on the host:

mytux:~/bin # tail -f /var/log/firewall
...
...
2016-02-23T14:41:17.783163+01:00 mytux kernel: [33572.296587] RULE 3 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk64 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=837 DF PROTO=ICMP TYPE=8 CODE=0 ID=2401 SEQ=1    
2016-02-23T14:41:18.790152+01:00 mytux kernel: [33573.304717] RULE 3 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk64 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=869 DF PROTO=ICMP TYPE=8 CODE=0 ID=2401 SEQ=2   
2016-02-23T14:41:19.798127+01:00 mytux kernel: [33574.313685] RULE 3 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk64 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=977 DF PROTO=ICMP TYPE=8 CODE=0 ID=2401 SEQ=3    
 

Good! Defense is obviously possible – even on the IP-level – as soon as we relate bridge ports to IP information!

Stopping ARP spoofing – with potential port flooding on the bridge as an aftermath

At some point in time the MiM attacker may stop his spoofing by

root@kali4:~# killall arpspoof
root@kali4:~# echo 1 > /proc/sys/net/ipv4/ip_forward

Before the poisoning jobs terminate themselves they send some packets which try to correct the corrupted ARP information on the attacked guests. However, depending on the load of the guests and the host this correction may go wrong – on one or both poisoned guests – and the old spoofed information may remain in the guests’ ARP tables:

kali3_ping2

And even some seconds later:

kali3_ping3

kali3 still thinks that 192.168.50.15 is located at the MAC address of kali4! How long this wrong information is kept depends on the relevant timeout parameter for local ARP table cache entries – see the output of:

$ cd /proc/sys/net/ipv4/neigh/
$ cat default/gc_stale_time

For our Debian guests this parameter typically has a value of 60 secs.

The picture above shows that on kali3 first 9 ICMP request packets were sent which got no answer. Later on a second series of pinging requests work normally again. In this specific test case – with a remaining wrong ARP information on kali3 – actually 2 interesting things happened in parallel:

mytux:~/bin # tail -f /var/log/firewall
2016-02-23T16:18:32.972806+01:00 mytux kernel: [ 1909.777744] RULE 21 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk65 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1 
2016-02-23T16:18:32.972820+01:00 mytux kernel: [ 1909.777774] RULE 3 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk64 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1 
2016-02-23T16:18:32.972822+01:00 mytux kernel: [ 1909.777785] RULE 21 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1     
2016-02-23T16:18:32.972823+01:00 mytux kernel: [ 1909.777806] RULE 5 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vnet0 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1 
2016-02-23T16:18:32.972824+01:00 mytux kernel: [ 1909.777818] RULE 35 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vmw1 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1 
2016-02-23T16:18:32.972825+01:00 mytux kernel: [ 1909.777827] RULE 35 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vmh1 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=19648 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=1 
....
.....
2016-02-23T16:18:40.972821+01:00 mytux kernel: [ 1917.786358] RULE 21 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk65 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9 
2016-02-23T16:18:40.972847+01:00 mytux kernel: [ 1917.786378] RULE 3 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk64 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9 
2016-02-23T16:18:40.972850+01:00 mytux kernel: [ 1917.786385] RULE 21 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9      
2016-02-23T16:18:40.972852+01:00 mytux kernel: [ 1917.786397] RULE 5 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vnet0 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9 
2016-02-23T16:18:40.972854+01:00 mytux kernel: [ 1917.786404] RULE 35 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vmw1 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9 
2016-02-23T16:18:40.972856+01:00 mytux kernel: [ 1917.786410] RULE 35 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vmh1 MAC=52:54:00:74:60:4a:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=20666 DF PROTO=ICMP TYPE=8 CODE=0 ID=1373 SEQ=9 
.....
2016-02-23T16:19:07.924844+01:00 mytux kernel: [ 1944.768264] RULE 21 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vk65 MAC=52:54:00:0f:34:4f:52:54:00:9f:5d:c1:08:00 SRC=192.168.50.13 DST=192.168.50.15 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=25179 DF PROTO=ICMP TYPE=8 CODE=0 ID=1376 SEQ=1 
  

Where do the reactions at other ports than vk64 come from? The first part of the explanation is that the bridge temporarily flooded all its ports (vk64, vk65, vethb1) with the ping requests of kali3! This in turn lead to local denial reactions on virbr6 and also on our second bridge (vribr4). For the reason of the flooding see below.

The second strange thing is that during each of the nine ping trials a successful packet submission occurs through port vk65 – but there is no log entry for an answer packet. Why is this?

Port flooding means copying of packets for the submission over all bridge ports other than the port of the incoming packet. The, in our case, wrong destination MAC addresses of the packets included. The bridge “hopes” for an answer of the addressed MAC at one of the ports. But is this going to happen in our test situation – in which kali3 sends requests out still to the wrong MAC of kali 4?

No – because despite flooding and acceptance for transport over port vk65, kali5 rightfully ignores the copied packets due to their wrong destination MAC. On the other side kali4 will not receive anything due to the iptables rules and cannot react either. So, we end up in a situation where ICMP request packets are sent by kali3 – but no answer will return from any bridge port. This in turn leads to the fact that the bridge is not learning what it needs to learn to stop the flooding. This situation will at least remain until the ARP cache table on kali3 is corrected/updated.

So only with a subsequent new ping series – and after the ARP table of kali3 has been updated – everything will work again as expected.

Addendum, 24.02.2016:

Some reader asked me via mail to explain why flooding occurred at all. This is a good question – and I have therefore added relevant remarks into the text above. Due to our limited “setageing” parameter some “guest MAC – port” relation may be deleted from the bridges forward database (FDB) after some time. (In addition we may have impacts of the STP protocol.) With our setaging parameter and the active iptables rules kali5 will drop out of the FDB pretty soon (after 30 secs): the original pinging during the attack situation will not reach kali5, and kali5 otherwise remains passive. However, also kali4 drops out 30 secs after stopping the spoofing attack from the FDB. So, we may reach a situation where kali3 still has the wrong ARP information, but kali4’s MAC is no longer in the FDB. We ended up in a kind of race condition between timeouts of the bridge’s FDB and ARP table cache renewal on the guests.

Due to the fact that either of the spoofed guests may still have wrong ARP information after the spoofing was stopped by the attacker various strange situations may occur. kali3 may have the right ARP information, but kali5 not yet. Then answering packets may be created which try to reach kali4 instead of kali3. Such packets must not be allowed by any acceptance rules (including established relation rules) – hence again: we need the DENY rules first.

What have we learned from all this?

  1. The stop of the ARP spoofing can leave the bridge and some of the guests in a unconsolidated mode for some time – despite a few final packets from the attacker system to restore ARP information on the attacked systems. One or two of the attacked guests and the host may still keep wrong entries in its/their ARP table(s). The duration of such a situation depends on the local timeout parameter for the ARP caching table entries on the guest systems.
  2. With a limited “setageing” parameter of the bridge, port flooding is not improbable during a period after the end of a ARP spoofing attack. As a consequence, the firewall rules must prevent the consequences of port flooding, too. Therefore, we need to take care not only of guest ports, but also of border ports which lead to segregated parts of the net or to other bridges.
  3. In the course of port flooding, response packets may be directed to wrong recipients. This status will remain until the ARP tables of the guests are updated. During such phase the defined DENY rules must be probed first before any kind of acceptance rules.
  4. Regarding the competition of the different timeouts on ARP caching tables and bridge FDBs: A conclusion in case of relatively stable guest-port relations might be to set the FDB timeout (setaging parameter of the bridge) to reasonably large values (in the range of a few minutes) to avoid flooding situations. On the other side the timeout for local ARP caching could be reduced as long as this does not create unreasonable ARP traffic.

What about TCP/IP packets?

If we think a bit about the general rules discussed above, we may understand that they would work also for standard TCP/IP packets of general TCP protocols. Actually, we have defined the leading denial rules for wrong “IP/port/direction”-associations without any reference to a specific protocol. So, our rules should hold in the general case, too. The reader may test this by configuring one of the guests as a web server or by using “netcat” to set up a simple server on one of the bridge guests.

We shall investigate a related full TCP scenario in one of the next posts – where we shall follow packets across 2 bridges and to the host. So, be patient, if you do not want to perform experiments, yourself.

Summary

Obviously netfilter iptables rules can not prevent ARP spoofing and resulting “man in the middle attack” trials on virtual guest systems attached to Linux bridges of a virtualization host. However, properly designed iptables rules can intercept and interrupt the redirected traffic which a MiM system attached to the bridge wants to provoke.

Appropriate iptables rules testing predefined IP-port relations on bridges may therefore supplement and accompany additional measures on the ebtables/arptables level of netfilter. However, such rules should not be undermined by leading acceptance rules related to connection tracking.

Even an already stopped ARP spoofing attack may leave the bridge and its guests in an unconsolidated status for a while. In addition flooding of packets to all bridge ports may occur. Appropriate denial rules for guest ports and Ethernet border ports in STP situations must block the resulting improper traffic. The reduction of flooding situations may require an adaption of the “setageing” parameter to reasonably large values for predictably stable configurations of guests on a bridge.

Most important: General acceptance rules for established connections should only be applied in the sequence of firewall rules AFTER all critical (denial) rules regarding unacceptable traffic across certain ports have been tested for incoming/outgoing packets. This may require explicit changes of the scripts created by Firewall tools like FWbuilder.

A significant problem is the requirement that the association of IP addresses and ports must be known or determined at the time of the definition and/or application of the filter rules. This requires persistent port naming techniques and under certain circumstances also persistent MAC distribution techniques plus DHCP restrictions for the guests within the used virtualization environment.

In the next post of this series
Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – II
we discuss how we can extend our rules to scenarios with multiple bridges on one host – and discover that we need a special treatment of packets crossing bridge borders.

 

KVM/qemu, libvirt, virt-manager – persistent names for virtual network interfaces of guest systems

Whilst experimenting with KVM guests and netfilter iptables, I cloned some guest systems. The virtual network interfaces of the different guests (old ones and cloned ones) were created with reference to a “virtual network” – a “host only network” in my case. The corresponding bridge was defined via virt-manager.

I found that one cannot always rely on a persistent “N“-number in the name “vnetN” of the interface devices of my virtual guests throughout cloning, further system changes and a reboot. I am not quite sure whether there is also an impact of systemd/udev – as I installed a new version of systemd during my experiments, too. Whatever exact reason: There are obviously conditions under which the name of a previously defined virtual NIC device of a specific guest may change due to a vnet-renumbering.

In my case one of the guests originally had gotten a virtual NIC (tun device) with a defined MAC and a name of “vnet3”. Later on (after cloning and other system changes) the guest’s virtual NIC with the same MAC got a device name “vnet6” – although I had not touched its configuration. That the MAC was kept up is not surprising as it resides in the XML-definition file created for the guest when I configured it with the help of virt-manager.

Such a change of the name of an Ethernet interface may of course become a disaster for the application of NETFILTER rules – especially for IPTABLES rules which directly refer to device names via the “–physdev” option. Your precious firewall rules may become worthless after a renaming of your virtual NIC.

I had to search a while to find an information on how to make the names created by libvirt for tun interfaces of KVM/qemu guests persistent. Stupid me! I should have looked into the libvirt Wiki in the first place:
http://libvirt.org/formatdomain.html#elementsNICSVirtual
http://libvirt.org/formatdomain.html#elementsNICSTargetOverride

The trick is to place an additional tag inside the tag for the interface definition in the guest’s XML file. And to follow a naming rule …

In my case I used virsh (virsh edit kali3) to edit the configuration file “kali3.xml” og a guest named “kali3”. You find the guest configuration files in the directory “/etc/libvirt/qemu/” of your Linux host. In the editor I navigated to the relevant interface definition – see the last one in the list below – which I changed :

 
    <interface type='bridge'>
      <mac address='52:54:00:22:cf:63'/>
      <source bridge='virbr_vmw'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
    <interface type='network'>
      <mac address='52:54:00:96:db:01'/>
      <source network='kali2'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
    </interface>
    <interface type='network'>
      <mac address='52:54:00:b2:7c:1f'/>
      <source network='virbr6'/>
      <target dev='vk61'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/>
    </interface>

 
You see some interface definitions there. All interface definitions include a MAC definition. The MACs were statistically created and assigned when I added the virtual NICs to the guests’ configuration via virt-manager. None of the first definitions contains anything that resembles a device name
definition. However, the interesting supplement can be found in the last interface definition:

<target dev='vk61'/>

The “target” tag allows for the definition of a persistent device name (here: vk61). Note that the documentation says that the name chosen must NOT start with “vnet”.

To apply the changes save the modified configuration files for your guests and restart the libvirtd service :

systemctl restart libvirtd.service

I hope this helps other people who need persistent names of the device interfaces of their KVM guests!

VMware WS – bridging of Linux bridges and security implications

Virtual bridges must be treated with care when security aspects get important. I stumbled into an unexpected kind of potential security topic when I experimented with a combination of KVM and VMware Workstation on one and the same Linux host. I admit that the studied scenario was a very special and academic one, but it really gave me an idea about the threat that some programs may change important and security relevant bridge parameters on a Linux system in the background – and you as an admin may only become aware of the change and its security consequences indirectly.

The scenario starts with a virtual Linux bridge “br0” (created with “brctl”). This Linux bridge gets an IP-address and uses an assigned physical NIC for direct bridging to a physical LAN. You may want to read my previous blog article
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
for more information about this type of direct bridging.

In our scenario the Linux brigde itself then gets enslaved as an ethernet capable device by a VMware bridge. See also: Opensuse/Linux – KVM, VMware WS – 3 virtuelle Brücken zwischen den Welten (see the detailed description of “Lösungsansatz 2”).

Under which circumstances may such a complicated arrangement be interesting or necessary?

Direct attachment of virtualization guests to physical networks

A Linux host-system for virtualization may contain KVM guests as well as VMware Workstation [WS] guests. A simple way to attach a virtual guest to some physical LAN of the Linux host (without routing) is to directly “bridge” a physical device of the host – as e.g. “enp8s0” – and then attach the guest to the virtual bridge. Related methods are available both for KVM and VMware. (We do not look at routing models for the communication of virtual guests with physical networks in this article).

However, on a host, on which you started working with KVM before you began using VMware, you may already have bridged the physical device with a standard Linux bridge “br0” before you began/begin with the implementation of VMware guests.

Opensuse, e.g., automatically sets up Linux bridges for all physical NICs when you configure the host for virtualization with YaST2. Or you yourself may have configured the Linux bridge and attached both the physical device and virtual “tap”-devices via the “brctl” and the “tunctl” commands. Setting up KVM guests via virt-manager also may have resulted in the attachment of further virtual NIC (tap-) devices to the bridge.

The following sketch gives you an idea about a corresponding scenario:

kvm_vmware_b2b_bridge_4

Ignore for a moment the upper parts and the displayed private virtual networks there. In the lower part you recognize our Linux host’s “direct bridge to LAN” in grey color with a red stitched border line. I have indicated some ports in order to visualize the association with assigned (named) virtual and physical network devices.

The bridge “br0” plays a double role: On one side it provides all the logic for packet forwarding to its target ports; on the other side it delivers the packets meant for the host itself to the Linux-kernel as if the bridge were a normal ethernet device. This is not done via an additional tap device of the host but directly. To indicate the difference “br0” is not sketched as a port.

The virtual and
physical devices are also visible e.g. in the output of CLI commands like “ifconfig”, “ip” or “wicked” with listing options as soon as the guest systems are started. The command “brctl show br0” would in addition inform you what devices are enslaved (via virtual ports) by the bridge “br0”.

Note that the physical device has to operate in the so called “promiscuous mode” in this scenario and gets no IP-address.

Bridging the Linux bridge by VMware

Under such conditions VMware still offers you an option to bridge the physical device “enp8s0” – but after some tests you will find out that you are not able to transmit anything across the NIC “enp8s0” – because it is already enslaved by your Linux bridge … Now, you may think : Let us create an additional Linux TAP-device on the host, add it to bridge “br0” and then set up a VMware network bridged to the new tap device. However, I never succeeded with bridging from VMware directly to a Linux tap device. (If you know how to do this, send me an email …).

There are 2 other possibilities for directly connecting your VMware guests (without routing) to the LAN. One is to “bridge the Linux bridge br0” – by administrative means of VMware WS. The other requires a direct connection of the VMware switch via its Host Interface to the Linux-bridge with the spanning tree protocol enabled. We only look at the solution based on “bridging the bridge” in this article.

We achieve a cascaded bridge configuration in which the VMware switch enslaves a Linux bridge via VMwares “Virtual network editor”:

vmware_vne_8

Such a solution is working quite well. The KVM guests can communicate with the physical LAN as well as with the VMware guests as long as all guests NICs are configured to be part of the same network segment. And the VMware guests reach the physical LAN as well as the KVM guests.

The resulting scenario is displayed in the following sketch:

kvm_vmware_b2b_bridge_5

In the lower part you recognize the “cascaded bridging”: The ethernet device corresponding to the bridge “br0” is enslaved by the VMware bridge “vmnet3” in the example. The drawing is only schematic – I do not really know how the “bridging of the bridge” is realized internally.

Interestingly enough the command “brctl” on the Linux side does NOT allow for a similar type of cascaded “bridging of a Linux bridge”. You cannot attach a Linux bridge to a Linux bridge port there.

We shall see that there is a good reason for this (maybe besides additional kernel module aspects and recursive stack handling).

Basic KVM guest “isolation” on a Linux bridge ?

A physical IEEE 802.1D bridge/switch may learn what MAC-addresses are reachable through which port, keep this information in an internal table and forward packets directly between ports without flooding packets to all ports. Is there something similar for virtual Linux bridges? The Linux bridge code implements a subset of the ANSI/IEEE 802.1d standard – see e.g. http://www.linuxfoundation.org/ collaborate/ workgroups/ networking/ bridge#What_does_ a_bridge_do.3F.
So, yes, in a way: There is a so called “ageing” parameter of the bridge. If you set the ageing time to “0” by

brctl setageingtime br0 0

this setting brings the bridge into a “hub” like mode – all accepted packets are sent to all virtual ports – and a privileged user of a KVM guest
may read all packets destined for all guests as well as for the LAN/WAN as soon as he switches the guest’s ethernet device into the “promiscuous mode”.

However, if you set the ageing parameter to a reasonable value like “30” or “40” then the bridge works in a kind of “switch” mode which isolates e.g. KVM guests attached to it against each other. The bridge then keeps track of the MAC adresses attached to its virtual ports and forwards packets accordingly and exclusively (see the man pages of “brctl”). Later on in this article we shall prove this by the means of a packet sniffer. (We assume normal operation here – it is clear that also a virtual bridge can be attacked by methods of ARP-spoofing and/or ARP-flooding).

Now, let us assume a situation with

brctl setageingtime br0 40

and let our Linux bridge be bridged by VMware. If I now asked you whether a KVM guest could listen to the data traffic of a VMware guest to the Internet, what would you answer?

What does “wireshark” tell us about KVM guest isolation without VMware started?

Let us first look at a situation where you have 2 KVM guests and VMware deactivated by

/etc/init.d/vmware stop

KVM guest 1 [kali2] may have an address of 192.168.0.20 in our test scenario, guest 2 [kali3] gets an address of 192.168.0.21. Both guests are attached to “br0” and can communicate with each other:

kvm_ne_12

kvm_ne_13

We first set explicitly

brctl setageing br0 30

on the host. Does KVM guest 1 see the network traffic of KVM guest 2 with Internet servers?

To answer this question we start “wireshark” on guest “kali3”, filter for packets of guest “kali2” and first look at ping traffic directly sent to “kali3”:
kvm_ne_14

Ok, as expected. Now, if we keep up packet tracking on kali3 and open a web page with “iceweasel” on kali2 we will not see any new packets in the wireshark window. This is the expected result. (Though it can not be displayed as it is difficult to visualize a non-appearance – you have to test it yourself). The Linux virtual bridge works more or less like a switch and directs the internet traffic of kali2 directly and exclusively to the attached “enp8s0”-port for the real ethernet NIC of the host. And incoming packets for kali2 are forwarded directly and exclusively from enp8s0 to the port for the vnet-device used by guest kali2. Thus, no traffic between guest “kali2” and a web server on the Tnternet can be seen on guest “kali3”.

But now let us change the ageing-parameter:

brctl setageing br0 0

and reload our web page on kali2 again:

kvm_ne_15

Then we, indeed, see a full reaction of wireshark on guest kali3:
kvm_ne_16

All packets to and from the server are visible! Note that we have not discussed any attack vectors for packet sniffing here. We just discussed effects of special setting for the Linux bridge.

Intermediate result: Setting the ageing-parameter
on a linux bridge helps to isolate the KVM guests against each other.

Can we see an Internet communication of a VMware guest on a KVM guest?

We now reset the ageing parameter of the bridge and start the daemons for VMware WS on our Opensuse host:

 
mytux:~ # brctl setageing br0 30 
mytux:~ # /etc/init.d/vmware start
Starting VMware services:                                                                 
   Virtual machine monitor                                             done               
   Virtual machine communication interface                             done               
   VM communication interface socket family                            done               
   Blocking file system                                                done               
   Virtual ethernet                                                    done               
   VMware Authentication Daemon                                        done               
   Shared Memory Available                                             done               
mytux:~ #               

 
Then we start a VMware guest with a reasonably configured IP address of 192.168.0.41 within our LAN segment:
kvm_ne_18
Then we load a web page on the VMware guest and have a parallel view at a reasonably filtered wireshark output on KVM guest “kali3”:

kvm_ne_19

Wireshark:
kvm_ne_20

Hey, we can see – almost – everything! A closer look reveals that we only capture ACK and data packets from the Internet server (and other sources, which is not visible in our picture) but not packages from the VMware guest to the Internet server or other target servers.

Still and remarkably, we can capture all packets directed towards our VMware windows guest on a KVM guest. Despite an ageing parameter > 0 on the bridge “br0”!

Guest isolation in our scenario is obviously broken! To be able to follow TCP-packets and thereby be able to decode the respective data streams fetched from a server to a distinct virtualization guest from other virtualization guests is not something any admin wants to see on a virtualization host! This at least indicates a potential for resulting security problems!

So, how did this unexpected “sniffing” become possible?

Bridges and the promiscuous mode of an attached physical device

What does a virtual layer 2 Linux bridge with an attached (physical) device to a LAN do? It uses this special device to send packets from virtualization guests to the LAN and further into the Internet – and vice versa it receives packets from the Internet/LAN sent to the multiple attached guests or the host. Destination IP addresses are resolved to MAC-addresses via the ARP-protocol. A received packet is then transferred to the specific target guest attached at the bridge’s virtual ports. If the ageing parameter is set > 0 the bridge remembers the MAC-address/port association and works like a switch – and thus realizes the basic guest isolation discussed above.

Let us have a look at the Linux bridge of our host :

 
mytux:/proc/net # brctl show br0 
bridge name     bridge id               STP enabled     interfaces
br0             8000.1c6f653dfd1e       no              enp8s0
                
                                        vnet0
                                                        vnet4

 
The physical device “enp8s0” is attached. The additional network interfaces “vnet0”, “venet4” devices are tun-devices assigned to our 2 virtual KVM guests “kali2” and “kali3”.

There is a very basic requirement for the bridge to be able to distribute packets coming from the LAN to their guest targets: The special physical device – here “enp8s0” – must be put into the “promiscuous mode”. This is required for the device to be able to receive and handle packets for multiple and different MAC- and associated IP-addresses.

How can we see that the “enp8s0”-device on my test KVM host really is in a promiscuous state? Good question: Actually and as far as I know, this is a bit more difficult than you may expect. Most standard tools you may want to use –
ifconfig, ip, “netstat -i” – fail to show the change if done in the background by bridge tools. However, a clear indication in my opinion is delivered by

mytux:/proc/net # cat /sys/class/net/enp8s0/flags 
0x1303

Watch the 3rd position! If I understand the settings corrrectly, I would assume that anything bigger than 1 there indicates that the IFF_PROMISC flag of a structure describing NIC properties is set – and this means promiscuous mode. It is interesting to see what happens if you remove the physical interface from the bridge

 
mytux:/proc/net # brctl delif br0 enp8s0
mytux:/proc/net # cat /sys/class/net/enp8s0/flags 
0x1003
mytux:/proc/net # brctl addif br0 enp8s0
mytux:/proc/net # cat /sys/class/net/enp8s0/flags 
0x1303
mytux:/proc/net # cat /sys/class/net/enp9s0/flags 
0x1003
mytux:/proc/net # cat /sys/class/net/vnet0/flags 
0x1303
mytux:/proc/net # cat /sys/class/net/vnet4/flags 
0x1303
mytux:/proc/net # 

 
The promiscuous mode is obviously switched on by the “brctl addif”-action. As a comparison see the setting for the physical ethernet device “enp9s0” not connected to the bridge. (By the way: all interfaces attached to the bridge are in the same promiscuous mode as “enp8s0”. That does not help much for sniffing if the bridge works in a switch-like mode).

Another way of monitoring the promiscuous state of a physical ethernet device in virtual bridge scenarios is to follow the and analyze the output of systemd’s “journalctl”:

mytux:~ # brctl delif br0 enp8s0
mytux:~ # brctl addif br0 enp8s0

The parallel output of “journalctl -f” is:

 
...
.Jan 12 15:21:59 rux kernel: device enp8s0 left promiscuous mode
Jan 12 15:21:59 rux kernel: br0: port 1(enp8s0) entered disabled state
....
....
Jan 12 15:22:10 mytux kernel: IPv4: martian source 192.168.0.255 from 192.168.0.200, on dev enp8s0
....
....
Jan 12 15:22:13 mytux kernel: device enp8s0 entered promiscuous mode
Jan 12 15:22:13 mytux kernel: br0: port 1(enp8s0) entered forwarding state
Jan 12 15:22:13 mytux kernel: br0: port 1(enp8s0) entered forwarding state
...

 

Promiscuous or non promiscuous state of the Linux bride itself?

An interesting question is: In which state is our bridge – better the ethernet device it also represents (besides its port forwarding logic)? With stopped vmware-services? Let us see :

mytux:~ # /etc/init.d/vmware stop
....
mytux:~ # cat /sys/class/net/br0/flags 
0x1003

 
Obviously not in promiscuous mode. However, the bridge itself can work with ethernet packets addressed to it. In our configuration the bridge itself got an IP-address – associated with the
host:

mytux:~ # wicked show  br0 enp8s0 vnet0 vnet4
enp8s0          enslaved
      link:     #2, state up, mtu 1500, master br0
      type:     ethernet, hwaddr 1c:6f:65:3d:fd:1e
      config:   compat:/etc/sysconfig/network/ifcfg-enp8s0

br0             up
      link:     #5, state up, mtu 1500
      type:     bridge
      config:   compat:/etc/sysconfig/network/ifcfg-br0
      addr:     ipv4 192.168.0.19/24
      route:    ipv4 default via 192.168.0.200

vnet4           device-unconfigured
      link:     #14, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:27:4e:0a

vnet0           device-unconfigured
      link:     #18, state up, mtu 1500, master br0
      type:     tap, hwaddr fe:54:00:85:20:d1
mytux:~ # 

 
This means that the bridge “br0” also acts like a normal non promiscuous NIC for packets addressed to the host. As the bridge itself is not in promiscuous mode it will NOT handle packets not addressed to any of its attached ports (and associated MAC-addresses) and just throw them away. The attached ports – and even the host itself (br0) – thus would not see any packets not addressed to them. Note: That the virtual bridge can separate the traffic between its promiscuous ports and thereby isolate them with “ageing > 0” is a reasonable but additional internal feature.

What impact has VMware’s “bridging the bridge” on br0 ?

However, “br0” becomes a part of a VMware bridge in our scenario – just like “enp8s0” became a part of the linux bridge “br0”. This happens in our case as soon as we start a virtual VMware machine inside the user interface of VMware WS. Thinking a bit makes it clear that the VMware bridge – independent of how it is realized internally – must put the device “br0″ (receiving external data form the LAN) into the promiscuous mode”. And really:

mytux:/sys/class/net # cat /sys/class/net/br0/flags 
0x1103
mytux:/sys/class/net # 

 
This means that the bridge now also accepts packets sent from the Internet/LAN to the VMware guests attached to the VMware bridge realized by a device “vmnet3”, which can be found under the “/dev”-directory. These packets arriving over “enp8s0” first pass the bridge “br0” before they are by some VMware magic picked up sat the output side of the Linux bridge and transmitted/forwarded to the VMware bridge.

But, obviously the Linux program responsible for the handling of packets reaching the bridge “br0” via “enp8s0” and the further internal distribution of such packets kicks in first (or in parallel) and gets a problem as it now receives packets which cannot be directed to any of its known ports.

Now, we speculate a bit: What does a standard physical 802.1D switch typically do when it gets packets addressed to it – but cannot identify the port to which it should transfer the packet? It just distributes or floods it to all of its ports!

And hey – here we have found a very plausible reason for our the fact that we can read incoming traffic to our VMware guest from all KVM guests!

Addendum 29.01.2016:
Since Kernel 3.1 options can be set for controlling and stopping the flooding of packets for unknown target MACs to specific ports of a Linux bridge. See e.g.:
http://events.linuxfoundation.org/ sites/ events/ files/ slides/ LinuxConJapan2014 _makita_0.pdf
The respective command would be :

echo 0 > /sys/class/net//brport/unicast_flooding

It would have to be used on all tap ports (for the KVM guests) on the Linux bridge. Such a procedure may
deliver a solution to the problem described above. I have tested it, yet.

Conclusion

Although our scenario is a bit special we have learned some interesting things:

  1. Bridging a Linux bridge as if it were a normal ethernet device from other virtualization environments is a dangerous game and should be avoided on productive virtualization hosts!
  2. A Linux bridge may be set into promiscuous mode by background programs – and you may have to follow and analyze flag entries in special files for a network device under “/sys/class/net/” or “journalctl” entries” to get notice of the change! Actually, on a productive system one should monitor these sources for status changes of network devices.
  3. A Linux bridge in promiscuous mode may react like a 802.1D device and flood its ports with packets for which it has not learned MAC adresses yet – this obviously has security and performance implications – especially when the flooding becomes a permanent action as in our scenario.
  4. Due to points 2 and 3 the status of a Linux bridge to a physical ethernet device of a host must be monitored with care.

Regarding VMware and KVM/Linux-Bridges – what are possible alternatives for “linking” the virtual bridges of both sides to each other and enable communication between all attached guests?

One simple answer is routing (via the virtualization host). But are there also solutions without routing?

From what we have learned a scenario in which the virtual VMware switch is directly attached to a Linux bridge port seems to be preferable in comparison to “bridging the bridge”. Port specific MAC addresses for the traffic could then be learned by the Linux bridge – and we would get a basic guest isolation. Such a solution would be a variation of what I have described as “Lösung 3” in a previous article about “bridges between KVM and VMware”:
KVM, VMware WS – 3 virtuelle Brücken zwischen den Welten
However, in contrast to “Lösung 3” described there we would require a Linux bridge with activated STP protocol – because 2 ethernet devices would be enslaved by the Linux bridge. Whether such a scenario is really more secure, we may study in another article of this blog.

Links

See especially pages 301 – 304

http://www.linuxfoundation.org/ collaborate/ workgroups/ networking/ bridge# What_does_a_ bridge_do.3F
See especially the paragraph “Why is it worse than a switch?”

Promiscuous mode analysis
https://www.kernel.org/ doc/ Documentation/ ABI/ testing/ sysfs-class-net
http://grokbase.com/ t/ centos/ centos/ 1023xtt5fd/ how-to-find-out-promiscuous-mode
https://lists.centos.org/ pipermail/ centos/ 2010-February/ 090269.html
Wrong info via “netstat -i ”
http://serverfault.com/ questions/ 453213/ why-is-my-ethernet-interface-in-promiscuous-mode

Opensuse/Linux – KVM, VMware WS – virtuelle Brücken zwischen den Welten

Bei der Wahl einer Virtualisierungsumgebung auf einem Linux-Host spielen viele Faktoren eine Rolle. Kosten, Anzahl der Gäste, Ressourcenbelastung des Hosts, Verfügbarkeit von Monitoring- und Admin-Umgebungen – aber auch die Kompatibilität mit den zu implementierenden Gastsystemen. Ich betrachte in diesem Artikel ausschließlich Linux-Arbeitsplatzsysteme als Virtualisierungshosts – also Desktops oder Laptops. Auf solchen Systemen verfolge ich dabei andere Interessen als auf Hosts für Server-Gast-Systeme:

In meinem Desktop-Umfeld spielen Windows-Gastsysteme für Entwicklungs- und für Penetrationstetsts neben Mehrzweck-Linux-Systemen wie Debian oder Kali2 eine Rolle. Ich benötige nur wenige und rudimentär ausgestattete Gäste. Die durchschnittliche Belastung des Virtualisierungs-Hosts ist somit relativ gering. Die Kosten sollen möglichst klein sein, die administrativen Möglichkeiten ausreichend, d.h. ohne Luxus. Für alle Gastsysteme gilt aber, dass die emulierte grafische Oberfläche hinreichend flüssig bedienbar sein muss.

Für die Windows-Desktops verwende ich deshalb eine VMware-Workstation-Umgebung, da mir diese Virtualisierungs-Plattform erfahrungsgemäß gut handhabbare Windows-Gastsysteme inkl. einer schnellen grafischen Oberfläche liefert. Zur Not auch mit 3D-Grafik-Unterstützung. Linux-Gäste fahre ich dagegen unter der KVM/Qemu-Virtualisierungsinfrastruktur – durch virtio-Treiber, Spice und Video XQL ist dort die grafische Nutzeroberfläche (inzwischen) hinreichend schnell (wenn z.Z. auch noch ohne 3D-Support). Libvirt/virt-manager liefern für KVM ausreichende und bequeme Möglichkeiten der Administration.

Mixt man – wie ich – zwei unterschiedliche Virtualisierungsumgebungen auf ein und demselben Host, so entstehen neue Begehrlichkeiten. Das gilt im Besonderen für die Konfiguration eines Pentest-Labors und die schnelle Abänderung der Netzwerk-Konfiguration. Für Penetrationstests isoliert man die Gäste i.d.R. in privaten Netzwerken. Diese werden jeweils durch eine virtuelle Bridge repräsentiert – mit oder ggf. auch mal ohne Host-NIC, um auch den Host an das jeweilige Netzwerk anbinden zu können. Ein direktes Bridging zu physikalischen Devices des Hosts bringt dagegen je nach Testsituation zu viele Gefahren für den Hosts selbst und dessen LAN-Umgebung mit sich.

“Host-Only-Netzwerke” [HON] kann man sowohl unter VMware WS als auch unter KVM realisieren. Jedes Host-Only-Netzwerk definiert ein bestimmtes Netzwerksegment, dem die jeweiligen Gastsysteme und ggf. der Host über spezielle virtuelle NICs zugeordnet werden.

In vorliegenden Artikel will ich auf die Frage eingehen, ob und wie man

  • Gäste einer “KVM”-Umgebung an eine Host-Only-Bridge Bridge einer VMware WS-Umgebung
  • und/oder umgekehrt Gäste einer “VMware WS”-Umgebung an eine Host-Only-Bridge Bridge einer KVM-Umgebung

anbinden kann. Natürlich könnte man immer über den Host zwischen den Netzwerksegmenten der Virtualisierungsumgebungen routen. Das ist sicher die schnellste Art der Kopplung. Mich interessieren in diesem Artikel aber Möglichkeiten, die ohne Routing auskommen. Ich meine also Szenarien, in denen die Gastsysteme der beiden Virtualisierungsumgebungen Teil eines logischen Netzwerk-Segmentes (auf Layer 3) werden.

Es geht also um eine Art “Cross”-Bridging” für bestimmte Gäste der unterschiedlichen Virtualisierungsumgebungen. Ich stelle hierfür nachfolgend drei verschiedene Lösungen vor. Eine vierte Lösung – eine elegante Spielart der dritten Lösung – wird im kommenden Artikel Fun with veth devices, Linux virtual bridges, KVM, VMware – attach the host and connect bridges via veth
vorgestellt.

Wozu braucht man die Möglichkeit einer gegenseitigen direkten Anbindung von Gästen einer “KVM”- oder “VMware WS”-Umgebung an ein Host-Only-Netzwerk der jeweils anderen Virtualisierungs-Infrastruktur?

Stattet man die Gastsysteme mit nur einer NIC aus, welche über die Verwaltungstools der jeweiligen Virtualisierungsinfrastruktur an die dort beheimatete virtuelle Bridge angebunden wird, so können die Gastsysteme des Segments der einen Virtualisierungsumgebung mit den Gastsystemen des Segments der jeweils anderen Virtualisierungsumgebung nur dann kommunizieren, wenn Routing über den Host und dessen Paketfilter zugelassen wird. Damit sind unter Penetrationstest-Bedingungen aber erneut Sicherheitsrisiken (“packet leackage” ins physikalische Netz) verbunden.

Umgekehrt gilt: Auch ohne Paketfilter erzwingen die Standardeinstellungen der Router/Forwarding-Funktionalität eines Linux-Systems Einschränkungen – u.a. werden IP-gespoofte Pakete, mit Ursprungsadressen im jeweils anderen Segment, nicht vom Linux-Kernel des Hosts weitergeleitet. Unter diesen Bedingungen kann im Rahmen von Penetrationstests dann z.B. ein NMAP Idle-Scan von einem Linux-System im KVM-Segment über ein “idle” Windows-System zu einem Windows-Target im VMware-Segment nicht durchgespielt werden.

Grundsätzlich ist meine Anforderung die, dass man auf die Vorteile der jeweiligen Virtualisierungsumgebung für die Gast-Einrichtung nicht verzichten will, unter bestimmten Bedingungen aber eine direkte – d.h. routing-freie – Anbindung eines Gastsystems an das Host-Only-Netzwerksegment der jeweils anderen Virtualisierungsumgebung nutzen möchte – über eine entsprechende konfigurierte (ggf. zusätzliche) virtuelle NIC.

Ich wollte das schon länger mal aus Prinzip ausprobieren – einfach um zu sehen, ob und wie das ggf. geht. Durch Aktivierung/Deaktivierung von NICs und zugehörigen Profilen will ich für ein Gastsystem schnell zwischen Szenarien mit Routing und ggf. zwischengeschalteten Paketfiltern einerseits und direkter Segment-Anbindung andererseits umschalten können.

Das Ganze ist aber auch unter dem Aspekt interessant, dass ein direktes “cascaded” oder “nested” Bridging von Linux-Bridges nicht möglich ist. So stellt sich u.a. die Frage: Wie bekommt man eine VMware Bridge an eine Linux-Bridge angeschlossen?

Netzwerk-Editoren

In der “VMware WS”-Umgebung nutzt man zur Konfiguration virtueller Netzwerke/virtueller Bridges als User root den “Virtual Network Editor” (s. Abbdg.), unter KVM den “Virtual Machine Manager” (virt-manager > “Edit” >> “Connection Details” >> “Virtual Networks”) und/oder eine Kombination aus “brctl”- und “ip” bzw. “tunctl”-Kommandos.

VMware Virtual Network Editor [VNE]
vmware_vne_1

KVM Virtual Machine Manager => Virtual Networks
kvm_ne_1

CLI-Kommandos: brctl/tunctl/ip
Wir kommen auf die praktische Anwendung dieser Kommandos weiter unten zurück. (Siehe aber auch: Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts.) Festzuhalten bleibt zunächst, dass ein mittels “tunctl” erzeugtes virtuelles “tap”-Device (Layer 2 Ethernet Device) nur genau einer Linux-Bridge zugeordnet werden kann. Ein Kaskadieren von Bridges (= direktes Einhängen einer Linux-Bridge in eine andere) ist wie gesagt auch nicht möglich.

Ein paar Anmerkungen zum VMware VNE:
nVermutlich reichen die Konfigurationsmöglichkeiten der VMware WS nicht an die der großen VMware Vitualisierungsumgebungen heran – hinreichend sind sie aber. So kann man u.a. Host-Only-Netzwerke (Bridges) mit oder ohne NAT erzeugen. Bei der Erzeugung eines VMware Host-Only-Segments wird automatisch eine Host-NIC für die erzeugte Bridge mit erzeugt. Diese Host-NIC für das VMware Host-Only-Netz erscheint dann – obwohl virtuell – als Ethernet-Device des Hostes.

vmnet1          device-unconfigured
      link:     #11, state up, mtu 1500
      type:     ethernet, hwaddr 00:50:56:c0:00:01
      addr:     ipv4 192.168.48.1/24

vmnet2          device-unconfigured
      link:     #12, state up, mtu 1500
      type:     ethernet, hwaddr 00:50:56:c0:00:02
      addr:     ipv4 192.168.3.1/24

 
Das ist insofern bemerkenswert, als es in einem entscheidenden Punkt anders aussieht als bei den “tap”- und Bridge-Devices, die man (z.B. mit virt-manager) zu virtuellen “vibr”-Bridges in der KVM-Welt erzeugt:

Der Typ des virtuellen VMware Devices ist “ethernet”! Nicht “bridge” oder “tap”.

Die VMware-Umgebung bietet auch ein direktes Bridging zu physikalischen Host-NICs an. Erzeugt man virtuelle Devices unter Linux und verfolgt, was sich dann auf der VMware-Seite tut, so sieht man: Die VMware Umgebung erkennt neue Netzwerk-Devices (wie erzeugte virtuelle Linux-Bridges und Linux Tap-Devices) und bietet (zumindest theoretisch) ein direktes Bridging auch zu diesen virtuellen Devices an.

Wie bringt man nun mit diesem Rüstzeug die unterschiedlichen Virtualisierungs-Welten bzw. ihre Gastsysteme auf einem Linux-Desktop-System zusammen? Welche Möglichkeiten gibt es?

Lösungsansatz 1: Anbindung von KVM-Gästen an das virtuelle VMware-Netz mittels “macvtap”-Devices

Im ersten Lösungsansatz möchte ich versuchen, ausgewählte KVM-Gäste der Linux-Bridge direkt an das virtuelle VMware-Netz anzubinden. Das soll vom Effekt her ungefähr so aussehen:

kvm_to_vmware

Im Zentrum des Interesses steht “Guest0” im KVM-Bereich. Das eine Interface (vnet1) dieses Gastes ist regulär an die Linux-Bridge “virbr0” angebunden. Von dort könnte über den Host ins VMware-Netz geroutet werden. Das wollen wir aber vermeiden und statt dessen über ein weiteres Interface die nahtlose Anbindung von Guest0 an das virtuelle Netz “vmnet1” auf der VMware-Seite erreichen – unter Umgehung irgendwelcher Paketfilter des Hostes. Siehe die gestrichelte Linie. Auch wenn wir das nicht direkt in der dargestellten Form verwirklichen können, so soll doch zumindest der effektive Datentransport der Logik dieses Bildes entsprechen.

Der VMware WS VNE bietet eine direkte Anbindung virtueller “tap”-Devices von VMware Gästen an die vmnet1-Bridge leider nicht an. Das einzige Element von “vmnet1”, das auf der Linux-Seite für evtl. Manipulationen zur Verfügung steht, ist die zugehörige virtuelle Host-NIC gleichen Namens. Was können wir damit aus Sicht eines KVM-Gastes tun?

macvtap-Devices?

KVM unterstützt seit einigen Jahren die Nutzung physikalischer Ethernet Devices durch Gastsysteme in Form sog. virtueller “macvtap”-Bridge-Devices. Zur Einführung siehe
http://virt.kernelnewbies.org/MacVTap

Ein weiterer sehr lesenswerter Artikel hat mein Verständnis beträchtlich erhöht:
https://seravo.fi/2012/virtualized-bridged-networking-with-macvtap

Der folgende
Artikel zeigt, wie man macvtap Devices in einer mit “libvirt” verwalteten Umgebung manuell über entsprechende Einträge in XML-Konfigurationsdateien einbindet.

 
Weitere interessante Artikel zu macvtap-Devices sind:
https://wiki.math.cmu.edu/iki/wiki/tips/20140303-kvm-macvtap.html
http://www.furorteutonicus.eu/2013/08/04/enabling-host-guest-networking-with-kvm-macvlan-and-macvtap/

Man beachte, dass alle genannten Artikel auf physikalische Devices als sog. “lower devices” einer “macvtap”-Bridge Bezug nehmen. Kann man evtl. auch virtuelle VMware-Devices in einer macvtap-Bridge nutzen?

Nutzung von VMware Host-NICs in einer MacVtap-Bridge?

Die Vermutung liegt nahe, dass aus Sicht des Linux-Kernels lediglich relevant ist, dass ein entsprechender “device node” mit zugeordneten Treibern und einem unterstützten Ethernet Stack vorhanden ist. VMware hat hier ganze Arbeit geleistet:

Die virtuellen Host-NICs für die virtuellen VMware Host-Only-Netze erscheinen unter “ifconfig” als vollwertige Ethernet Devices (s.o.). Unter dem Verzeichnis “/dev/” findet man zudem zugehörige Dateien für zeichenorientierte Geräte.

Also: Warum nicht mal ein virtuelles VMware “Ethernet”-Devices in KVM über MacVtap bridgen? Man könnte dann einem KVM-(Linux)-Gast-System eine virtuelle NIC zuordnen, die das Ethernet-Device “vmnet1” über den “macvtap”-Mechanismus nutzt. Wird das virtuelle Device im KVM-Gastsystem danach mit einer IP-Adresse versehen, die zum VMware-Netzwerk-Segment “vmnet1” passt, sollte der KVM-Gast direkt im VMware-Netz erreichbar sein bzw. direkt mit Gästen des VMware-Netzes kommunizieren können.

Konfiguration eines MacVtap-Devices auf einem KVM-Gast mit virt-manager

Um meinem Kali2-Gastsystem in der KVM-Umgebung testweise ein macvtap-Device zuzuordnen benutze ich “virt-manager”. (Ich gehe dabei davon aus, dass der libvirtd-Dämon gestartet wurde, nachdem das virtuelle VMware-Netz angelegt wurde, so dass die “vmnet”-Netzwerk-Devices der KVM-Umgebung mit Sicherheit bekannt sind.)

kvm_ne_3

Mit dem Ergebnis:

kvm_ne_2_800

Nun ggf. das Gastsystem nochmal rebooten. Dann die neue virtuelle NIC im Gastsystem konfigurieren – hier in einem Kali2-System:

kvm_ne_4_800

Danach unter VMware WS ein Gastsystem starten. Hier z.B. ein altes XP-System mit einer IP 192.168.48.3. Dann auf beiden Seiten pingen:

kvm_ne_5

vmware_vne_2

Sieht doch gut aus! Nun auch noch testweise einen nmap-Scan durchführen:

kvm_ne_6

Bestens – Lösungsansatz 1 funktioniert offenbar! Wenn intern auch nicht exakt so, wie im einleitenden Bild dargestellt.

Lösungansatz 2: Der umgekehrte Weg über eine virtuelle Linux-Bridge

Nun wollen wir uns um den umgekehrten Ansatz kümmern. Ein ausgewählter VMware WS-Gast soll irgendwie an eine Linux-Bridge, die von KVM-Gästen bereits über tap-Devices genutzt wird, angebunden werden. Die nachfolgende Abbildung zeigt, wie man sich das in etwa vorstellen kann.

vmware_to_kvm

Im Gegensatz zum entsprechenden Bild für den Lösungsansatz 1 haben wir jetzt die Möglichkeit des Routings über den Host ganz ausgeblendet. Zu beachten ist auch, dass das 192.168.48.0/24-Netz sich jetzt auf der rechten Seite befindet. Der Host selbst erhält in unserem Szenario nicht mal eine eigene, and die Bridge angeschlossene virtuelle NIC.

Die entscheidende Frage ist, ob und wie VMware WS dazu zu bekommen ist, eine Linux-Bridge zu nutzen, die mit “brctl addbr” erzeugt wird. Wer einen genauen Blick auf die ursprüngliche VMware-Netz-Konfiguration meines Testsystems weiter oben wirft, stellt fest, dass VMware offenbar virtuelle Netzwerke mit Zugriff auf eine Linux-Bridge (im Beispiel br0) einrichten kann. Das “br0”-Device ist auf meinem Testsystem eine direkte Linux-Bridge zu einem physikalischen Device. Was aber nichts daran ändert, dass es sich dabei um eine kernel-unterstützte Bridge handelt. Deshalb liegt es nahe, das ganze Spiel mal mit einer Linux-Bridge für ein rein virtuelles Host-Only-Netzwerk zu probieren.

Um Verwerfungen zu vermeiden, fahren wir dazu unseren KVM-Gast runter, entfernen dann das oben eingerichtete MacVtap-Device aus diesem Gast mittels “virt-manager”. Dann wechseln wir als root in den VMware Network Editor und löschen das Netzwerk “vmnet1”. Die entsprechende Host-NIC ist ist danach auch nicht mehr als Character-Device unter Linux vorhanden. In der Konfiguration der VMware-Gäste entfernen wir zur Sicherheit entsprechende Netzwerk-Karten vollständig.

Nun richten wir manuell eine Linux-Bridge namens “virbr_vmw” auf unserem Linux-Host ein:

mytux:/etc/init.d # brctl addbr virbr_vmw
mytux:/etc/init.d # ifconfig virbr_vmw 192.168.48.1 netmask 255.255.255.0 broadcast 192.168.48.255 
maytux:/etc/init.d # wicked show all
...
...
virbr_vmw       device-unconfigured
      link:     #47, state up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.48.1/24
...

 
Wir wechseln dann erneut zu VMwares “Virtual Network Editor”. Hier fügen wir ein neues Netzwerk hinzu. Und zwar im Modus “Bridged” – hierdurch wird für die VMware-Gäste eine Bridge zum Sharen eines Linux-Devices erzeugt. Wir verwenden für das neue virtuelle Netzwerk den inzwischen wieder freigewordenen Namen “vmnet1”:

vmware_vne_3

Durch eine VMware-Automatik wird das Netzwerk “vmnet1” dabei vorerst der nächsten freien physikalischen Netzwerkschnittstelle – soweit vorhanden – zugeordnet. In meinem Fall ist das das Device “enp9s0”:

vmware_vne_4

Wir können das aber ändern und wählen über die Combobox vielmehr die neu angelegte Linux-Bridge “virbr_vmw” aus :

vmware_vne_5

[Sollte die Linux-Bridge noch nicht in der Auswahl angezeigt werden, müssen die VMware-Dienste ggf. runter gefahren und dann über die entsprechen Skripte (/etc/init.d/vmware restart) neu gestartet werden.]

In den Konfigurationseinstellungen eines geeigneten VMware-Gastes legen wir nun ein Netzwerk-Interface zum virtuellen Netz “vmnet1” an. Wir fahren den Gast (in unserem Testfall ein XP-System) anschließend hoch und konfigurieren dort das neue Device mittels der Konfigurationswerkzeuge des VMware-Gastes (hier eines Windows-Systems). Soweit, so gut. Unsere Bridge kann natürlich von unserem Linux-Host aus angepingt werden. Ist unser VMware-Gast aber auch vom Host aus erreichbar?

kvm_ne_7

Offenbar ja! Aber wir haben insgesamt noch keine permanente, statische Konfiguration sondern nur eine temporäre erzeugt, die beim nächsten Systemboot verloren gehen würde.

Nun machen wir die ganze Sache auf unserem Opensuse-System permanent. Dazu legen als root unter dem Verzeichnis “/etc/sysconfig/network” eine Datei “ifcfg-virbr_vmw” an – mit folgendem Inhalt:

BOOTPROTO='static'
BRIDGE='yes'
BRIDGE_FORWARDDELAY='0'
BRIDGE_PORTS=''
BRIDGE_STP='off'
BROADCAST=''
DHCLIENT_SET_DEFAULT_ROUTE='yes'
ETHTOOL_OPTIONS=''
IPADDR='192.168.48.1/24'
MTU=''
NETWORK=''
PREFIXLEN='24'
REMOTE_IPADDR=''
STARTMODE='auto'
NAME=''

 
Im Gegensatz zu einer evtl. schon vorhandenen Bridge “br0” ordnen wir dem Bridge-Device aber keine physikalische NIC des Hostes (wie etwa “enp9s0”) zu!

Hinweis: Debian- oder Red Hat-Anhänger nutzen entsprechende Konfigurationsdateien ihres Systems, die sich teils in anderen Verzeichnissen befinden, einen anderen Namen haben und eine im Detail abweichende Syntax erfordern.

Nun fahren wir alle evtl. noch laufenden virtuellen Maschinen runter und re-booten danach unseren Host. Nach dem Booten sollte man nach Eingabe des Kommandos “ifconfig” in ein Host-Terminal u.a. Folgendes sehen:

virbr_vmw Link encap:Ethernet  HWaddr EA:93:44:6E:45:55  
          inet addr:192.168.48.1  Bcast:192.168.48.255  Mask:255.255.255.0
          inet6 addr: fe80::e893:44ff:fe6e:4555/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:132 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:21008 (20.5 Kb)

 
Wir fahren unseren VMware-Gast wieder hoch und prüfen, ob ein An-Pingen des Hostes möglich ist.

vmware_vne_6

Wenn nicht, so ist noch etwas an der Konfigurationsdatei falsch. Ggf. mit YaST über die dortigen Module zur Netzwerk-Konfiguration prüfen. Zur Sicherheit auch nochmal die Routen auf dem Host und im VMware-Gast kontrollieren.

Nun wenden wir uns unserem KVM-Gast-System zu. Über die Verwaltungsoberfläche von virt-manager fügen wir der Hardware des Gastes ein neues Device hinzu:

kvm_ne_8

Wir starten danach den KVM-Gast und konfigurieren dort das neue Ethernet-Device mit den Mitteln des Gastsystems (hier mit Kali2/Debian-Einstellungswerkzeugen):

kvm_ne_9

Abschließend testen wir wieder Pings in Richtung Host, VMware-Gast und zurück:

kvm_ne_11

vmware_vne_7

So liebe ich Virtualisierung! Weitere KVM-Gäste benötigen lediglich ein Netzwerk-Interface zur Bridge “virbr_vmw” – und dann stehen auch sie schon in Verbindung zu entsprechend konfigurierten Gästen des VMware-Systems.

Lösungsansatz 3: Einbinden des VMware-Ethernet-Devices in die Linux-Bridge

Lösungsansatz 1 bot eine Möglichkeit zur direkten Anbindung einzelner KVM-Gäste an das VMware Host-Only-Netzwerk. Es stellt sich die Frage: Geht das nicht auch kollektiv?

Dazu müsste man das von VMware bereitgestellte Ethernet-Device in die Linux-Bridge einbinden. Sprich das Host-Interface des VMware-Switches muss Teil der Linux-Bridge werden. Das entspräche einem Kaskadieren von Bridges. Funktioniert das? Bildlich sähe das dann etwa wie folgt aus:

vmw-to_kvm_1

Die erste Frage, die sich einem beim Betrachten stellt, ist: Wie vermeidet man Kollisionen in der Addresszuweisung? DHCP-seitig sollte nach dem Einhängen der des VMware-Devices nur noch genau ein DHCP-Dienst laufen – z.B. der auf der Linux-Seite. Dazu muss beim Einrichten des VMware-Netzwerkes die angebotene DHCP-Funktionalität abgewählt werden.

Der andere Punkt ist der, dass man zumindest mit dem “Virtual Machine Manager” ein Problem bekommt, wenn man ein virtuelles Host-Only-Netzwerk anlegen will, dass das gleiche Segment betrifft, aus dessen Adressbereich anderen virtuellen oder physikalischen Devices bereits IP-Adressen zugewiesen wurden. Also legt man als Erstes am besten die Bridge auf der rechten Seite der obigen Abbildung an. Wir benutzen dazu KVM’s virt-manager:

vmw-to_kvm_2

Das Netzwerk taucht in meinem Fall in Form der virtuellen Bridge “virbr2” auf dem Host auf (mit “brtcl show” prüfbar). Nun ordnen wir einem KVM-Gast noch ein entsprechendes virtuelles Device zu:

vmw-to_kvm_3

Wir können diesen Gast schon mal starten und die Zuordnung zur Bridge prüfen:

mytux:~ # brctl show virbr2
bridge name     bridge id               STP enabled     interfaces
virbr2          8000.005056c00008       yes             virbr2-nic
                                                        vnet4
mytux:~ # 

 
OK, jetzt aber zur VMware-Seite. Ein neues virtuelles Netzwerk ist auch im VNE schnell angelegt:

vmw-to_kvm_4

r
(Hinweis: Das ist nicht mehr das standardmäßige vmnet8 Host-Only-Network, das VMware nach einer anfänglichen Installation automatisch konfiguriert, sondern mein eigenes. Ihr könnt zum Testen natürlich auch das vorkonfigurierte verwenden – oder statt der “8” eine andere Nummer wählen).

Leider ist VMware beim Generieren des gleichnamigen Host-Interfaces gnadenlos und überprüft im Gegensatz zu KVM nicht, ob der Adressraum Überschneidungen aufweist:

mytux:~ # ip address show       
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
...
18: virbr2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 52:54:00:6b:ab:44 brd ff:ff:ff:ff:ff:ff
    inet 192.168.6.1/24 brd 192.168.6.255 scope global virbr2
       valid_lft forever preferred_lft forever
19: virbr2-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr2 state DOWN group default qlen 500
    link/ether 52:54:00:6b:ab:44 brd ff:ff:ff:ff:ff:ff
20: vmnet8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether 00:50:56:c0:00:08 brd ff:ff:ff:ff:ff:ff
    inet 192.168.6.1/24 brd 192.168.6.255 scope global vmnet8
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fec0:8/64 scope link 
       valid_lft forever preferred_lft forever
mytux:~ # 

 
So gehts natürlich nicht – wir entfernen die IP-Adresse manuell vom VMware-Interface:

mytux:~ # ip addr del 192.168.6.1/24 broadcast 192.168.6.255 dev vmnet8

 

Nachtrag, 30.01.2016:
Interessanterweise wurde nach einem heutigen Kernelupdate und einer Neukompilation der VMware-Programme der VMware-Dienst “Virtual Ethernet” zur Aktivierung der virtuellen VMware-Netze nicht mehr erfolgreich gestartet, wenn dadurch Überlapps in der IP-Adresse mit irgendeiner gestarteten KVM-Bridge produziert wurden. In einem solchen Fall muss man die VMware Standard-DHCP-Einstellungen für das betroffene virtuelle Netzwerk in der Datei “/etc/vmware/netzwerkname/dhcpd/dhcpd.conf” für das erzeugte Ethernet-Device der Bridge manuell abändern.

Nun kommt ein wichtiger Punkt:

Welche IP ordnen wir dem Interface “vmnet8” statt dessen zu? Erinnern wir uns an Linux-Bridges zu physikalischen Ethernet-Devices (s. etwa: Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts):
Dem Ethernet Device (in seiner Funktion als Uplink-Device) darf dort keine IP-Adresse zugeordnet sein! Die richtige Antwort ist auch in diesem Fall: Keine IP-Adresse für das vmnet-Device!

Da wir schon schon auf der Kommandozeile sind, ordnen wir jetzt unserer Bridge “virbr2” das Device “vmnet8” manuell zu:

mytux:~ # brctl addif virbr2 vmnet8
mytux:~ # brctl show virbr2
bridge name     bridge id               STP enabled     interfaces
virbr2          8000.005056c00008       yes             virbr2-nic
                                                        vmnet8
                                                        vnet4
mytux:~ # 

 
Nun statten wir unseren exemplarischen VMware-Gast noch mit einem passenden Interface aus:

vmw-to_kvm_5

Dann fahren wir den VMware-Gast hoch, konfigurieren unsere neue
Netzwerkschnittstelle dort mit den vorhandenen Tools des Gast-Betriebssystems (hier Windows) manuell und probieren, ob wir den Host erreichen können:

vmw-to_kvm_6

Bestens! Nun zu unserem Linux-KVM-Gast:

vmw-to_kvm_7

Alles klar! Lösung 3 funktioniert. Der Leser, der das nachbaut, kann ja selbst weitere Tests durchführen.

Lösung 3 erscheint mir persönlich unter den drei hier besprochenen Ansätzen die strukturell klarste zu sein. Sie hat allerdings den Nachteil, dass man sich selbst darum kümmern muss, dass dem VMware Interface (hier “vmnet8”) auch beim nächsten Systemstart die IP-Adresse entzogen wird. Sobald der VMware-Dienst startet, konfiguriert er “vmnet8” ansonsten nämlich wieder mit einer Adresse. Ich habe bislang keine Möglichkeit gefunden, das über die VMware-Netzwerk-Konfigurations-Tools und zugehörige Konfigurationsdateien unter “/etc/vmware” abzustellen.

Auch die Zuordnung des VMware Devices zur Linux-Bridge sollte man am besten selbst vornehmen, um von der Reihenfolge des Starts der diversen Virtualisierungsdienste etwas unabhängiger zu werden. Für die Abarbeitung entsprechender Kommandos erstellt man sich ein kleines Script, dass man mit ein bisschen Geschick auch als eigenen systemd-Service in den systemd-Startup-Vorgang einbinden kann.

Alternativer Lösungsansatz 4: Nutzung von veth-Device-Paaren für die Bridge-Kopplung

Ein anderer Weg zur Realisierung einer Bridge-Kopplung im Geiste von Lösung 3 besteht darin, die VMware Bridge von vornherein als Bridge zu einem im Linux-System angelegten “veth”-Device zu kreieren. Das zweite Interface des veth-Paars wird dann zur Anbindung an die Linux-Bridge eingesetzt. Ehrlich gesagt, ist das dann die Lösung, die ich für die wirklich beste halte. Ich gehe auf den Einsatz von veth-device-Paaren für ein strukturiertes Bridge-Linking ausführlich in einem kommenden Artikel ein (Fun with veth devices, Linux virtual bridges, KVM, VMware – attach the host and connect bridges via veth).

Performance der vorgestellten Lösungen?

Ein gute Frage! Ohne gezielte Messungen traue ich mich nicht, hierzu Aussagen zu machen. MacVtap bietet z.B. eine leichtgewichtige und auch performante Methode, ein Ethernet-Interface zu nutzen. Die Frage ist allerdings, wie gut das Character-Device, das VMware auf dem Host für ein virtuelles Netz bereitstellt, mit mehreren Kommunikationsströmen zwischen KVM-Gästen und VMware-Gästen, die in Lösung 1 und Lösung 3 gleichzeitig über dieses eine (!) Interface transferiert werden müssen, klarkommt. In Lösung 2 hängt dagegen alles an der unter Linux definierten (und vom Kernel unterstützten) virtuellen Bridge, die gleichzeitig von Gästen aus beiden Virtualisierungs-Umgebungen geteilt wird.

Schwer einzuschätzen …… Bin für Hinweise dankbar. In meinem Mini-Labor für Penetrationstests konnte ich bislang keine signifikanten Unterschiede ausmachen. Werde die Sache aber im Auge behalten …

Security

Standard-Bridges haben aus Security-Sicht einen grundlegenden Nachteil: Sie müssen mindestens über ein angeschlossenes Netzwerk-Interface Pakete für verschiedene IP-Adressen entgegennehmen können. Was bedeutet das eigentlich im Fall unserer zweiten und dritten Lösung für die Sicherheit? Eine sehr gute Frage, die man dann aber auch gleich für die Standardbridge “br0” stellen muss. Hier muss ich den Leser vertrösten. Ich werde mich in kommenden Artikeln mit diesem
Thema etwas intensiver auseinandersetzen. Siehe u.a.:
VMware WS – bridging of Linux bridges and security implications

Für den Augenblick möchte ich allerdings folgende explizite Warnung aussprechen:

Warnung : Das Bridgen einer Bridge – wie in Lösung 2 – ist aus Security-Gesichtspunkten ein gefährliches Spiel. In Pentest-Labors mag so etwas nützlich sein – in Produktivumgebungen sollten beim Admin alle roten Warnleuchten angehen, da die Bridge dann selbst – und nicht nur ein spezielles Uplink-Interface – in den “promiscuous mode” versetzt wird.
 
Eine Abschottung des Datenverkehrs bestimmter Gastsysteme von und mit externen Systemen gegen ein Mithören durch andere Gäste ist dann nicht mehr garantiert – selbst wenn die Linux-Bridge das im Normalfall leisten würde.  
 
Man muss dann sehr genau wissen, was man tut, und ggf. explizit untersuchen, wie die Linux-Bridge auf die Modifikationen durch erneutes Bridging reagiert.

Fazit

Was haben wir erreicht? Die von einer “VMware WS” bereitgestellten virtuellen Devices des Hosts erscheinen unter Linux als vollwertige (zeichen-orientierte) Ethernet-Devices. Sie können von einem KVM-Gast z.B. über die Einrichtung eines individuellen MacVtap-Devices genutzt werden. Nach Konfiguration mit IP-Adressen und passenden Routen wird der KVM-Gast dann für alle praktischen Anwendungen Mitglied des VMware-Netzes.

Umgekehrt und alternativ lässt sich eine virtuelle Linux-Bridge, die mit einer IP-Adresse ausgestattet wurde, unter VMware als direkt gebridgtes Ethernet-Device von mehreren VMware-Gästen nutzen. KVM-Gäste werden an die Linux-Bridge mit Standard-Verfahren angebunden. VMware-Gäste, die über das Bridging der Linux-Bridge an selbige Linux-Bridge angeschlossen wurden, kommunizieren direkt mit allen KVM-Gastsystemen.

Beiden Lösungen 1 und 2 ist gemeinsam, dass sie ein bereits virtualisiertes Devices auf einer Metaebene nochmals in virtualisierter Form nutzen. Das allein macht diese Lösungen – wie ich finde – spannend.

Am elegantesten erscheint im Rückblick jedoch Lösung 3 (bzw. Lösung 4) zu sein. Der VMware-Switch wird dabei einfach und ohne IP-Adresse in die Linux-Bridge integriert. Das Vmware-Hostinterface funktioniert dann wie ein Uplink-Device zum VMware-Switch.

Alle beschriebenen Methoden können im Einzelfall jedoch erhebliche Sicherheitsimplikationen nach sich ziehen. Damit werde ich mich in kommenden Artikeln befassen.

Immerhin:
Durch Aktivierung oder Deaktivierung von entsprechend konfigurierten Ethernet-Devices auf KVM- und VMware-Gästen kann man in allen drei dargestellten Fällen schnell zwischen

  • einer Konfiguration mit einer über den Host gerouteten Verbindung zum VMware-Netz
  • oder einer Konfiguration mit einer direkten Ein-/An-Bindung der Gastsysteme in das jeweils andere Netz ohne Routing

hin- und herschalten.

Das erhöht u.a. die Flexibilität für das Durchspielen verschiedener Penetrationstest-Szenarien in einem virtuellen Test-Labor eines Linux-Hostes beträchtlich. Man kann sich natürlich aber auch etliche Alltags-Szenarien vorstellen, in denen diese Art des Brückenschlags zwischen den Welten nützlich ist. Solange es keine speziellen Sicherheitsanforderungen gibt ….

Viel Spaß nun mit dem Einsatz von Brücken zwischen den Virtualisierungswelten von VMware WS und KVM unter Linux!

KVM – Anlegen eines privaten, isolierten Netzwerks mit virt-manager

Im vorhergehenden Artikel dieses Blogs
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
hatte ich 2 Arten der Anbindung eines KVM-Gastsystems an die physikalische Umwelt des KVM-Hostes diskutiert. Ich hatte angemerkt, dass es unter KVM/libvirt neben einem direkten Bridging zu einer physikalischen Host-NIC natürlich auch die Möglichkeit gibt, KVM-Gäste an ein sog. “Host-Only-Network” [HON] anzubinden. Intern wird dieses Netzwerk durch eine virtuelle Bridge repräsentiert. Soll man aus dem HON heraus mit der physikalischen Umwelt (LAN) kommunizieren, muss man auf dem KVM-Host Routing zwischen einer bereitgestellten virtuellen Host-NIC der Bridge zu einer physikalischen NIC des Hostes ermöglichen. Letztere leitet die Pakete dann ins LAN.

Unterlässt man das Routing (und/oder filtert man Pakete aus dem HON) auf dem Host, so befinden sich Gastsystem und Host in einem isolierten virtuellen Netz, aus dem nach außen ohne weitere Vorkehrungen nicht kommuniziert werden kann. Dem isolierten Netz können natürlich weitere Gäste beitreten.

Wegen der Nachfrage einer Leserin, zeige ich nachfolgend kurz die Anlage eines virtuellen Host-Only-Netzwerks unter KVM mittels “virt-manager”. Ich setze voraus, dass der “libvirtd”-Daemon läuft.

Man ruft als root “virt-manager” auf und geht im Übersichtsfenster auf “Edit >> Connection Details” und dort auf den Reiter “Virtual Networks”. Dort findet man unter der Übersichtsliste zu bereits vorhandenen Netzwerken, einen Button mit einem “+” Symbol zum Anlegen eines neuen Netzwerks. Ich zeige nachfolgend die Dialogsequenz:

new_network_1

new_network_2

new_network_3

new_network_4

new_network_5

Die Bridge und eine zugehörige Host-NIC tauchen dann auch in der Liste der vorhandenen Netzwerk-Devices auf. Unter Opensuse zeigt das Komamndo “wicked show all” dann etwa ein virbr-Device (virtual bridge – im Beispiel ein “virbr2”):

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24

 
Das neue Netzwerk findet sich dann auch in Form einer XML-Netzwerk-Konfigurations-Datei unter “/etc/libvirt/qemu/networks/host2.xml” wieder:

<!--
WARNING: THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
OVERWRITTEN AND LOST. Changes to this xml configuration should be made using:
  virsh net-edit host2
or other application using the libvirt API.
-->

<network>
  <name>host2&
lt;/name>
  <uuid>f47c2d04-b1d6-48bf-a6dc-a643d28b38d3</uuid>
  <bridge name='virbr2' stp='on' delay='0'/>
  <mac address='52:54:00:c9:bd:24'/>
  <domain name='host2'/>
  <ip address='192.168.120.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.120.128' end='192.168.120.254'/>
    </dhcp>
  </ip>
</network>

 
Will man eine solche Host-Only-Bridge von einem KVM-Gast aus nutzen, so muss man für diesen Gast ein entsprechendes “Netzwerk”-Device (NIC) anlegen, das der Bridge zugeordnet wird. “virt-manager” bietet auch hierfür entsprechende grafische Dialoge an. Ich gehe davon aus, dass eine virtuelle Maschine (z.B. namens “kali2”) bereits existiert. Man öffnet deren Konfigurations-Oberfläche durch Doppelklick auf den entsprechenden Eintrag in der Liste aller KVM-Instanzen unter “virt-manager”. Im sich öffnenden Fenster klickt man weiter auf den Button mit dem “i”-Symbol:

new_network_6

Danach taucht das Device auch in der XML-Konfigurationsdatei für den Gast auf – in meinem Beispiel etwa für einen Kali-Gast mit der Datei “/etc/libvirt/qemu/kali.xml” – ich zeige nur den relevanten Ausschnitt:

    <interface type='network'>
      <mac address='52:54:00:0d:3c:8b'/>
      <source network='host2'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/>
    </interface>

 
Danach muss man den Gast neu starten und in ihm natürlich das neu aufgetauchte Netzwerk-Interface manuell konfigurieren – falls man nicht auf DHCP setzt und/oder besondere Einstellungen benötigt. Man merke sich hierzu die MAC-Adresse, um bei mehreren NICs nicht den Überblick zu verlieren! Man achte auch auf das Default-Gateway, wenn Routing über den Host gewünscht ist.

Achtung: Das neue virtuelle Netzwerk des Gastes ist mit “ifconfig”, “ip” oder “wicked” Kommandos erst als ein “vnetN-Device sichtbar, wenn der Gast gestartet und aktiv ist. Das “N” steht dabei für eine fortlaufende Nummer, die vom System (libvirt) vergeben wird. Z.B. taucht unter “wicked show all” dann ggf. ein Device “vnet3” auf:

virbr2          device-unconfigured
      link:     #73, state device-up, mtu 1500
      type:     bridge
      addr:     ipv4 192.168.120.1/24

virbr2-nic      device-unconfigured
      link:     #74, state down, mtu 1500, master virbr2
      type:     tap, hwaddr 52:54:00:c9:bd:24
...
...
vnet3           device-unconfigured
      link:     #17, state up, mtu 1500, master virbr2
      type:     tap, hwaddr fe:54:00:0d:3c:8b

 
Man beachte, dass sowohl die Host-Nic, als auch das Device des Gastes “tap”-Devices sind. Allg. Infos zu tap-devices finden sich hier:
https://www.kernel.org/doc/Documentation/networking/tuntap.txt
https://de.wikipedia.org/wiki/TUN/TAP

Hinweise für Leser, die die Konfiguration lieber manuell und über die Kommandozeile durchführen mögen

Wie man eine virtuelle Bridge auf dem KVM-Host mittels des “brctl”-Kommandos einrichtet, benennt und wie man ihr “tap”-Devices zuordnet, habe ich im Prinzip bereits früher am Beispiel einer direkten Bridge zu einem physikalischen Device beschrieben:
Opensuse – manuelles Anlegen von Bridge to LAN Devices (br0, br1, …) für KVM Hosts
Siehe aber auch hier:
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking – Abschnitt (Private Network)

“tap”-Devices kann man manuell und temporär über das Kommando “tunctl” auf dem Host erzeugen.
Siehe etwa :
http://unix.stackexchange.com/questions/86720/can-i-create-a-virtual-ethernet-interface-named-eth0
http://serverfault.com/questions/347895/creating-tun-tap-devices-on-linux
http://www.naturalborncoder.com/virtualization/2014/10/17/understanding-tun-tap-interfaces/
http://blog.elastocloud.org/2015/07/qemukvm-bridged-network-with-tap.html

Die Zuordnung von “tap”-Devices zu einer virtuellen Linux-Bridge erfolgt über das “brctl addif”-Kommando. Nun fehlt also nur noch eine Methode, um einmal erzeugte “tap”-Devices in die Konfiguration eines Gastes einzubinden.

Ich kenne einige Leute, die starten ihre virtuellen Maschinen lieber eigenhändig und über Scripts statt über virt-manager. Dann kann die Netzwerk-Konfiguration des Gastsystems in Form passender Optionsparameter des Kommandos “qemu-system-x86_64” (mit weiteren Optionen für KVM-Unterstützung; s.u.) oder des Kommandos “qemu-kvm” zum Starten eines KVM-Gastes geschehen. Eine Übersicht über diese Möglichkeit findet man hier:
http://qemu-buch.de/de/index.php?title=QEMU-KVM-Buch/_Netzwerkoptionen/_Virtuelle_Netzwerke_konfigurieren
In abgekürzter Form auch hier :
https://bbs.archlinux.org/viewtopic.php?pid=1148335#p1148335
https://bbs.archlinux.org/viewtopic.php?pid=1424044#p1424044

Eine weitere Alternative ist hier beschrieben (s. den Abschnitt zu “Private Networking”):
http://www.linux-kvm.org/page/Networking#Configuring_Guest_Networking

Übrigens: “qemu-kvm” ist auf aktuellen Linux-Systemen meist nur ein kleines Shell-Script-Kommando, dass “qemu-system-x86_64” mit KVM-Hardware-Unterstützungsoptionen aufruft! Siehe zum Unterschied zw. “qemu-system-x86_64” und “qemu-kvm” etwa
http://www.linux-kvm.com/content/qemu-kvm-or-qemu-system-x8664%EF%BC%9F).
Die Optionen des “qemu-kvm”-Kommandos sind z.B. hier beschrieben:
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_gen_opts.html
Eine Zusammenfassung zu tap-Devices und verschiedenen Bridging-Varianten gibt auch
https://www.suse.com/documentation/sles11/book_kvm/data/cha_qemu_running_networking.html

Will man die Tools von “libvirt/virt-manager” zum Starten der virtuellen Maschine benutzen, dann kann die “manuelle” Definition von virtuellen NICs für einen KVM-Gastes aber auch mittels des “virsh edit”-Kommandos zur Manipulation des XML-Files für die
Gastkonfiguration durchgeführt werden.
Siehe:
http://serverfault.com/questions/665440/set-up-network-interfaces-in-ubuntu-for-kvm-virtual-machine

In einigen Situationen kann es auch erforderlich sein, den Gast im laufenden Betrieb um ein Netzwerkinterface zu einem neu definierten virtuellen Netz zu erweitern. Informationen hierzu findet man hier:
http://www.linuxwave.info/2014/12/hot-attach-and-hot-detach-network.html
https://kashyapc.fedorapeople.org/virt/add-network-card-in-guest.txt