This patch adds the support for the firewalld zones in the virtual
bridges managed by libvirt allowing to configure the rules as for
all the other interfaces.
After assigning a fwzone to the bridge it is then possible to
configure the firewall rules through the firwalld daemon, e.g.
enabling or disabling connections to certain services located on
the host.
The firewalld events (e.g. restart, reload) are managed with the
preexisting infrastructure that is already in place to reinstate
the general iptables rules.
Signed-off-by: Federico Simoncelli <fsimonce(a)redhat.com>
---
docs/formatnetwork.html.in | 5 +-
docs/schemas/network.rng | 6 +++
src/conf/network_conf.c | 8 +++-
src/conf/network_conf.h | 1 +
src/network/bridge_driver.c | 114 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 131 insertions(+), 3 deletions(-)
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 4dd0415..e6d7d9c 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -69,7 +69,7 @@
<pre>
...
- <bridge name="virbr0" stp="on"
delay="5"/>
+ <bridge name="virbr0" fwzone="internal"
stp="on" delay="5"/>
<domain name="example.com"/>
<forward mode="nat" dev="eth0"/>
...</pre>
@@ -91,6 +91,9 @@
is 'on' or 'off' (default is
'on'). Attribute <code>delay</code> sets the bridge's
forward
delay value in seconds (default is 0).
+ Attribute <code>fwzone</code> specifies the firewalld zone to use
+ for the bridge device (the default is no zone and firewalld will
+ configure the device with its default zone).
<span class="since">Since 0.3.0</span>
</dd>
<dt><code>domain</code></dt>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 6c3fae2..2f92cfc 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -66,6 +66,12 @@
</attribute>
</optional>
+ <optional>
+ <attribute name="fwzone">
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
+
</element>
</optional>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 968cf11..cf163e0 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1749,6 +1749,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt);
stp = virXPathString("string(./bridge[1]/@stp)", ctxt);
def->stp = (stp && STREQ(stp, "off")) ? 0 : 1;
+ def->fwzone = virXPathString("string(./bridge[1]/@fwzone)", ctxt);
if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay)
< 0)
def->delay = 0;
@@ -1880,9 +1881,10 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
}
/* fall through to next case */
case VIR_NETWORK_FORWARD_BRIDGE:
- if (def->delay || stp) {
+ if (def->delay || stp || def->fwzone) {
virReportError(VIR_ERR_XML_ERROR,
- _("bridge delay/stp options only allowed in route, nat,
and isolated mode, not in %s (network '%s')"),
+ _("bridge delay/stp/fwzone options only allowed in
"
+ "route, nat, and isolated mode, not in %s (network
'%s')"),
virNetworkForwardTypeToString(def->forward.type),
def->name);
goto error;
@@ -2370,6 +2372,8 @@ virNetworkDefFormatInternal(virBufferPtr buf,
virBufferAddLit(buf, "<bridge");
if (def->bridge)
virBufferEscapeString(buf, " name='%s'", def->bridge);
+ if (def->fwzone)
+ virBufferEscapeString(buf, " fwzone='%s'",
def->fwzone);
virBufferAsprintf(buf, " stp='%s' delay='%ld'
/>\n",
def->stp ? "on" : "off",
def->delay);
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 6ce9a00..80ee75f 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -194,6 +194,7 @@ struct _virNetworkDef {
char *bridge; /* Name of bridge device */
char *domain;
+ char *fwzone; /* The firewalld zone */
unsigned long delay; /* Bridge forward delay (ms) */
unsigned int stp :1; /* Spanning tree protocol */
virMacAddr mac; /* mac address of bridge device */
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index e8b314a..4875bbc 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -76,6 +76,29 @@
#define VIR_FROM_THIS VIR_FROM_NETWORK
+#if HAVE_FIREWALLD
+static char *firewall_cmd_path = NULL;
+
+static int
+networkBridgeOnceInit(void)
+{
+ firewall_cmd_path = virFindFileInPath("firewall-cmd");
+ if (!firewall_cmd_path) {
+ VIR_INFO("firewall-cmd not found on system. "
+ "firewalld support disabled for firewall zones.");
+ /* no need to verify the firewalld state as the service might
+ * be available now but not later on when it is actually used to
+ * configure the zones, or the opposite: it might be available
+ * now but not when needed.
+ */
+ }
+ return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(networkBridge)
+
+#endif
+
/* Main driver state */
struct network_driver {
virMutex lock;
@@ -1893,6 +1916,75 @@ networkRemoveGeneralIp6tablesRules(struct network_driver *driver,
iptablesRemoveForwardRejectOut(driver->iptables, AF_INET6,
network->def->bridge);
}
+#if HAVE_FIREWALLD
+static int
+networkAddInterfaceZone(virNetworkObjPtr network)
+{
+ int status;
+
+ if ((network->def->bridge == NULL) || (network->def->fwzone == NULL))
+ return 0;
+
+ networkBridgeInitialize();
+
+ if (firewall_cmd_path == NULL) {
+ VIR_WARN("firewall-cmd is missing cannot add interface '%s' to
"
+ "firewall zone '%s'", network->def->bridge,
+ network->def->fwzone);
+ return -1;
+ }
+
+ virCommandPtr cmd = virCommandNew(firewall_cmd_path);
+ /* the --change-zone command is used (instead of --add-interface) to avoid
+ * errors if the device was already assigned to a different zone.
+ */
+ virCommandAddArgList(cmd, "--zone", network->def->fwzone,
+ "--change-zone", network->def->bridge, NULL);
+ if (virCommandRun(cmd, &status) < 0 || status != 0) {
+ VIR_ERROR("failed to add interface '%s' to firewall zone
'%s'",
+ network->def->bridge, network->def->fwzone);
+ } else {
+ VIR_DEBUG("interface '%s' has been added to firewall zone
'%s'",
+ network->def->bridge, network->def->fwzone);
+ }
+
+ virCommandFree(cmd);
+
+ return 0;
+}
+
+static void
+networkRemoveInterfaceZone(virNetworkObjPtr network)
+{
+ int status;
+
+ if (network->def->bridge == NULL)
+ return;
+
+ networkBridgeInitialize();
+
+ if (firewall_cmd_path == NULL) {
+ VIR_WARN("firewall-cmd is missing cannot remove interface '%s'
"
+ "from its firewall zone", network->def->bridge);
+ return;
+ }
+
+ virCommandPtr cmd = virCommandNew(firewall_cmd_path);
+ /* not specifying a zone removes the interface from any zone */
+ virCommandAddArgList(cmd, "--remove-interface",
+ network->def->bridge, NULL);
+ if (virCommandRun(cmd, &status) < 0 || status != 0) {
+ VIR_ERROR("failed to remove interface '%s' from its firewall
zone",
+ network->def->bridge);
+ } else {
+ VIR_DEBUG("interface '%s' has been removed from its firewall
zone",
+ network->def->bridge);
+ }
+
+ virCommandFree(cmd);
+}
+#endif
+
static int
networkAddGeneralIptablesRules(struct network_driver *driver,
virNetworkObjPtr network)
@@ -2000,9 +2092,27 @@ networkAddGeneralIptablesRules(struct network_driver *driver,
goto err9;
}
+#if HAVE_FIREWALLD
+ if (networkAddInterfaceZone(network) < 0) {
+ virReportError(VIR_ERR_SYSTEM_ERROR,
+ _("failed to add interface '%s' to the firewall zone
'%s'"),
+ network->def->bridge, network->def->fwzone);
+ goto err10;
+ }
+#else
+ if (network->def->fwzone != NULL)
+ VIR_WARN("firewalld support not compiled into this binary, interface "
+ "'%s' not added to firewall zone '%s'",
network->def->bridge,
+ network->def->fwzone);
+#endif
+
return 0;
/* unwind in reverse order from the point of failure */
+#if HAVE_FIREWALLD
+err10:
+ networkRemoveGeneralIp6tablesRules(driver, network);
+#endif
err9:
iptablesRemoveForwardAllowCross(driver->iptables, AF_INET,
network->def->bridge);
err8:
@@ -2032,6 +2142,10 @@ networkRemoveGeneralIptablesRules(struct network_driver *driver,
int ii;
virNetworkIpDefPtr ipv4def;
+#if HAVE_FIREWALLD
+ networkRemoveInterfaceZone(network);
+#endif
+
networkRemoveGeneralIp6tablesRules(driver, network);
for (ii = 0;
--
1.8.1.4