[libvirt] [PATCH] network: allow DHCP/DNS/TFTP explicitly in OUTPUT rules

Daniel P. Berrangé berrange at redhat.com
Fri Sep 27 16:16:55 UTC 2019


From: Malina Salina <malina.salina at protonmail.com>

While the default iptables setup used by Fedora/RHEL distros
only restricts traffic on the INPUT and/or FORWARD rules,
some users might have custom firewalls that restrict the
OUTPUT rules too.

These can prevent DHCP/DNS/TFTP responses from dnsmasq
from reaching the guest VMs. We should thus whitelist
these protocols in the OUTPUT chain, as well as the
INPUT chain.

Signed-off-by: Malina Salina <malina.salina at protonmail.com>

Initial patch then modified to add unit tests and IPv6
support

Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
---
 src/libvirt_private.syms                      |  2 +
 src/network/bridge_driver_linux.c             | 29 ++++++++++---
 src/util/viriptables.c                        | 36 ++++++++++++++++
 src/util/viriptables.h                        |  8 ++++
 .../nat-default-linux.args                    | 21 ++++++++++
 .../nat-ipv6-linux.args                       | 42 +++++++++++++++++++
 .../nat-many-ips-linux.args                   | 21 ++++++++++
 .../nat-no-dhcp-linux.args                    | 42 +++++++++++++++++++
 .../nat-tftp-linux.args                       | 28 +++++++++++++
 .../route-default-linux.args                  | 21 ++++++++++
 10 files changed, 244 insertions(+), 6 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 7b681fac64..83b97af364 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2186,6 +2186,7 @@ iptablesAddForwardRejectIn;
 iptablesAddForwardRejectOut;
 iptablesAddOutputFixUdpChecksum;
 iptablesAddTcpInput;
+iptablesAddTcpOutput;
 iptablesAddUdpInput;
 iptablesAddUdpOutput;
 iptablesRemoveDontMasquerade;
@@ -2198,6 +2199,7 @@ iptablesRemoveForwardRejectIn;
 iptablesRemoveForwardRejectOut;
 iptablesRemoveOutputFixUdpChecksum;
 iptablesRemoveTcpInput;
+iptablesRemoveTcpOutput;
 iptablesRemoveUdpInput;
 iptablesRemoveUdpOutput;
 iptablesSetDeletePrivate;
diff --git a/src/network/bridge_driver_linux.c b/src/network/bridge_driver_linux.c
index 35459c10d1..0b6ff45b17 100644
--- a/src/network/bridge_driver_linux.c
+++ b/src/network/bridge_driver_linux.c
@@ -553,18 +553,23 @@ networkAddGeneralIPv4FirewallRules(virFirewallPtr fw,
             break;
     }
 
-    /* allow DHCP requests through to dnsmasq */
+    /* allow DHCP requests through to dnsmasq & back out */
     iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
     iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
+    iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
     iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
 
-    /* allow DNS requests through to dnsmasq */
+    /* allow DNS requests through to dnsmasq & back out */
     iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
     iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
+    iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
+    iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
 
-    /* allow TFTP requests through to dnsmasq if necessary */
-    if (ipv4def && ipv4def->tftproot)
+    /* allow TFTP requests through to dnsmasq if necessary & back out*/
+    if (ipv4def && ipv4def->tftproot) {
         iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
+        iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
+    }
 
     /* Catch all rules to block forwarding to/from bridges */
     iptablesAddForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
@@ -592,13 +597,18 @@ networkRemoveGeneralIPv4FirewallRules(virFirewallPtr fw,
     iptablesRemoveForwardRejectIn(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
     iptablesRemoveForwardRejectOut(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge);
 
-    if (ipv4def && ipv4def->tftproot)
+    if (ipv4def && ipv4def->tftproot) {
         iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
+        iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 69);
+    }
 
     iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
     iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
+    iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
+    iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 53);
 
     iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
+    iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 68);
     iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
     iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV4, def->bridge, 67);
 }
@@ -626,10 +636,14 @@ networkAddGeneralIPv6FirewallRules(virFirewallPtr fw,
     iptablesAddForwardAllowCross(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge);
 
     if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
-        /* allow DNS over IPv6 */
+        /* allow DNS over IPv6 & back out */
         iptablesAddTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
         iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
+        iptablesAddTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
+        iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
+        /* allow DHCPv6 & back out */
         iptablesAddUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
+        iptablesAddUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
     }
 }
 
@@ -643,7 +657,10 @@ networkRemoveGeneralIPv6FirewallRules(virFirewallPtr fw,
     }
 
     if (virNetworkDefGetIPByIndex(def, AF_INET6, 0)) {
+        iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 546);
         iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 547);
+        iptablesRemoveUdpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
+        iptablesRemoveTcpOutput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
         iptablesRemoveUdpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
         iptablesRemoveTcpInput(fw, VIR_FIREWALL_LAYER_IPV6, def->bridge, 53);
     }
diff --git a/src/util/viriptables.c b/src/util/viriptables.c
index 0e3c0ad73a..46d0c3df7a 100644
--- a/src/util/viriptables.c
+++ b/src/util/viriptables.c
@@ -303,6 +303,42 @@ iptablesRemoveUdpInput(virFirewallPtr fw,
     iptablesInput(fw, layer, deletePrivate, iface, port, REMOVE, 0);
 }
 
+/**
+ * iptablesAddTcpOutput:
+ * @ctx: pointer to the IP table context
+ * @iface: the interface name
+ * @port: the TCP port to add
+ *
+ * Add an output to the IP table allowing access to the given @port from
+ * the given @iface interface for TCP packets
+ */
+void
+iptablesAddTcpOutput(virFirewallPtr fw,
+                     virFirewallLayer layer,
+                     const char *iface,
+                     int port)
+{
+    iptablesOutput(fw, layer, true, iface, port, ADD, 1);
+}
+
+/**
+ * iptablesRemoveTcpOutput:
+ * @ctx: pointer to the IP table context
+ * @iface: the interface name
+ * @port: the UDP port to remove
+ *
+ * Removes an output from the IP table, hence forbidding access to the given
+ * @port from the given @iface interface for TCP packets
+ */
+void
+iptablesRemoveTcpOutput(virFirewallPtr fw,
+                        virFirewallLayer layer,
+                        const char *iface,
+                        int port)
+{
+    iptablesOutput(fw, layer, deletePrivate, iface, port, REMOVE, 1);
+}
+
 /**
  * iptablesAddUdpOutput:
  * @ctx: pointer to the IP table context
diff --git a/src/util/viriptables.h b/src/util/viriptables.h
index feea988acd..07b4851013 100644
--- a/src/util/viriptables.h
+++ b/src/util/viriptables.h
@@ -45,6 +45,14 @@ void             iptablesRemoveUdpInput          (virFirewallPtr fw,
                                                   const char *iface,
                                                   int port);
 
+void             iptablesAddTcpOutput            (virFirewallPtr fw,
+                                                  virFirewallLayer layer,
+                                                  const char *iface,
+                                                  int port);
+void             iptablesRemoveTcpOutput         (virFirewallPtr fw,
+                                                  virFirewallLayer layer,
+                                                  const char *iface,
+                                                  int port);
 void             iptablesAddUdpOutput            (virFirewallPtr fw,
                                                   virFirewallLayer layer,
                                                   const char *iface,
diff --git a/tests/networkxml2firewalldata/nat-default-linux.args b/tests/networkxml2firewalldata/nat-default-linux.args
index c9d523d043..ab18f30bd0 100644
--- a/tests/networkxml2firewalldata/nat-default-linux.args
+++ b/tests/networkxml2firewalldata/nat-default-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
diff --git a/tests/networkxml2firewalldata/nat-ipv6-linux.args b/tests/networkxml2firewalldata/nat-ipv6-linux.args
index a57b9266af..05d9ee33ca 100644
--- a/tests/networkxml2firewalldata/nat-ipv6-linux.args
+++ b/tests/networkxml2firewalldata/nat-ipv6-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
@@ -81,11 +102,32 @@ ip6tables \
 --jump ACCEPT
 ip6tables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+ip6tables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+ip6tables \
+--table filter \
 --insert LIBVIRT_INP \
 --in-interface virbr0 \
 --protocol udp \
 --destination-port 547 \
 --jump ACCEPT
+ip6tables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 546 \
+--jump ACCEPT
 iptables \
 --table filter \
 --insert LIBVIRT_FWO \
diff --git a/tests/networkxml2firewalldata/nat-many-ips-linux.args b/tests/networkxml2firewalldata/nat-many-ips-linux.args
index 1bdc43fd6a..82e1380f51 100644
--- a/tests/networkxml2firewalldata/nat-many-ips-linux.args
+++ b/tests/networkxml2firewalldata/nat-many-ips-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
diff --git a/tests/networkxml2firewalldata/nat-no-dhcp-linux.args b/tests/networkxml2firewalldata/nat-no-dhcp-linux.args
index 7d359f3824..8954cc5473 100644
--- a/tests/networkxml2firewalldata/nat-no-dhcp-linux.args
+++ b/tests/networkxml2firewalldata/nat-no-dhcp-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
@@ -81,11 +102,32 @@ ip6tables \
 --jump ACCEPT
 ip6tables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+ip6tables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+ip6tables \
+--table filter \
 --insert LIBVIRT_INP \
 --in-interface virbr0 \
 --protocol udp \
 --destination-port 547 \
 --jump ACCEPT
+ip6tables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 546 \
+--jump ACCEPT
 iptables \
 --table filter \
 --insert LIBVIRT_FWO \
diff --git a/tests/networkxml2firewalldata/nat-tftp-linux.args b/tests/networkxml2firewalldata/nat-tftp-linux.args
index b721801b70..88e9929b62 100644
--- a/tests/networkxml2firewalldata/nat-tftp-linux.args
+++ b/tests/networkxml2firewalldata/nat-tftp-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_INP \
 --in-interface virbr0 \
 --protocol udp \
@@ -42,6 +63,13 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 69 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
diff --git a/tests/networkxml2firewalldata/route-default-linux.args b/tests/networkxml2firewalldata/route-default-linux.args
index ed3c560f74..c427d9602d 100644
--- a/tests/networkxml2firewalldata/route-default-linux.args
+++ b/tests/networkxml2firewalldata/route-default-linux.args
@@ -16,6 +16,13 @@ iptables \
 --table filter \
 --insert LIBVIRT_OUT \
 --out-interface virbr0 \
+--protocol tcp \
+--destination-port 68 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
 --protocol udp \
 --destination-port 68 \
 --jump ACCEPT
@@ -35,6 +42,20 @@ iptables \
 --jump ACCEPT
 iptables \
 --table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol tcp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
+--insert LIBVIRT_OUT \
+--out-interface virbr0 \
+--protocol udp \
+--destination-port 53 \
+--jump ACCEPT
+iptables \
+--table filter \
 --insert LIBVIRT_FWO \
 --in-interface virbr0 \
 --jump REJECT
-- 
2.21.0




More information about the libvir-list mailing list