Skip to content

Commit

Permalink
Add handling for firewalld's StrictForwardPorts setting
Browse files Browse the repository at this point in the history
This mostly amounts to throwing a sensible error in cases where
the setting is enabled and the user has requested ports be
forwarded.

There are no tests at present because firewalld 2.3.x is
currently restricted to Rawhide, and is required for effective
testing.

A manpage has also been added with details on how to work with
the StrictForwardPorts setting (as well as some other tidbits on
firewalld + Podman integration).

Signed-off-by: Matt Heon <[email protected]>
  • Loading branch information
mheon committed Jan 22, 2025
1 parent 56a0d3f commit 33d2a4a
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 1 deletion.
7 changes: 6 additions & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ MANDIR ?= $(DATADIR)/man
GO ?= go
GOMD2MAN ?= go-md2man

docs: $(patsubst %.md,%,$(wildcard *.1.md))
docs: $(patsubst %.md,%,$(wildcard *.[0-9].md))

%.1: %.1.md
$(GOMD2MAN) -in $^ -out $@

%.5: %.5.md
$(GOMD2MAN) -in $^ -out $@

.PHONY: .install.md2man
.install.md2man:
$(GO) install github.com/cpuguy83/go-md2man/v2@latest
Expand All @@ -17,6 +20,8 @@ docs: $(patsubst %.md,%,$(wildcard *.1.md))
install:
install -d ${DESTDIR}/${MANDIR}/man1
install -m 0644 *.1 ${DESTDIR}/${MANDIR}/man1
install -d ${DESTDIR}/${MANDIR}/man5
install -m 0644 *.5 ${DESTDIR}/${MANDIR}/man5

.PHONY: clean
clean:
Expand Down
85 changes: 85 additions & 0 deletions docs/netavark-firewalld-forwarding.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
% NETAVARK-FIREWALLD 5 Netavark Firewalld Interactions Man Page
% Matthew Heon
% January 2025

## Name

netavark-firewalld - description of the interaction of Netavark and firewalld

## Description

Netavark can be used on systems with firewalld enabled without issue.
When using the default `nftables` or `iptables` firewall drivers, on systems where firewalld is running, firewalld will automatically be configured to allow connectivity to Podman containers.
All subnets of Podman-managed networks will be automatically added to the `trusted` zone to allow this access.

### Firewalld Driver

There is also a dedicated firewalld driver in Netavark.
This driver uses the firewalld DBus API to natively interact with firewalld.
It can be enabled by setting `firewall_driver` to `firewalld` in `containers.conf`.
The native firewalld driver offers better integration with firewalld, but presently suffers from several limitations.
It does not support isolation (the `--opt isolate=` option to `podman network create`.
It also does not currently support connections to a forwarded port of a container on the same host via public IP, only via the IPv4 localhost IP (127.0.0.1).

### Strict Port Forwarding

Since firewalld version 2.3.0, a new setting, `StrictForwardPorts`, has been added.
The setting is located in `/etc/firewalld/firewalld.conf` and defaults to `no` (disabled).
When disabled, port forwarding with Podman works as normal.
When it is enabled (set to `yes`), port forwarding with Podman will become nonfunctional.
Attempting to start a container or pod with the `-p` or `-P` options will return errors.
When StrictForwardPorts is enabled, all port forwarding must be done through firewalld using the firewall-cmd tool.
This ensures that containers cannot allow traffic through the firewall without administrator intervention.

Instead, containers should be started without forwarded ports specified, and preferably with static IPs.

To forward a port externally, the following command should be run, substituting the desired host and container port numbers, protocol, and the container's IP.
```
# firewall-cmd --permanent --zone public --add-forward-port=port={HOST_PORT_NUMBER}:proto={PROTOCOL}:toport={CONTAINER_PORT_NUMBER}:toaddr={CONTAINER_IP}
```

If the container does not have a static IP, it can be found with `podman inspect`:
```
# podman inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {CONTAINER_NAME_OR_ID}
```

Once the container is stopped or removed, the rule must be manually removed with the following command:
```
# firewall-cmd --permanent --zone public --remove-forward-port=port={HOST_PORT_NUMBER}:proto={PROTOCOL}:toport={CONTAINER_PORT_NUMBER}:toaddr={CONTAINER_IP}
```

To also allow forwarding via IPv4 localhost (`127.0.0.1`), a firewalld policy must be added, as well as a rich rule for each port requiring localhost forwarding.
Forwarding via IPv6 localhost is not possible due to kernel limitations.

To add the policies required for IPv4 localhost forwarding, the following commands must be run.
This is only necessary once per system.
```
# firewall-cmd --permanent --new-policy localhostForward
# firewall-cmd --permanent --policy localhostForward --add-ingress-zone HOST
# firewall-cmd --permanent --policy localhostForward --add-egress-zone ANY
```

A further rich rule for each container is required:
```
# firewall-cmd --permanent --policy localhostForward --add-rich-rule='rule family=ipv4 destination address=127.0.0.0/8 forward-port port={HOST_PORT_NUMBER} protocol={PROTOCOL} to-port={CONTAINER_PORT_NUMBER} to-addr={CONTAINER_IP}'
```

These rules must be manually removed when the container is stopped or removed with the following command:
```
# firewall-cmd --permanent --policy localhostForward --remove-rich-rule='rule family=ipv4 destination address=127.0.0.0/8 forward-port port={HOST_PORT_NUMBER} protocol={PROTOCOL} to-port={CONTAINER_PORT_NUMBER} to-addr={CONTAINER_IP}'
```

The associated `localhostForward` policy does not need to be removed.

Please also note that, at present, it is not possible to access forwarded ports of a container on the same host via the host's public IP; the rich rule above must be applied instead and access must be via 127.0.0.1, not the public IP of the host.

Please note that the firewalld driver presently bypasses this protection, and will still allow traffic through the firewall when `StrictForwardPorts` is enabled without manual forwarding through `firewall-cmd`.
This may be changed in a future release.

## SEE ALSO

firewalld(1), firewall-cmd(1), firewalld.conf(5), podman(1), containers.conf(5)

## Authors

Matthew Heon <[email protected]>
51 changes: 51 additions & 0 deletions src/firewall/firewalld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,3 +706,54 @@ pub fn rm_firewalld_if_possible(net: &ipnet::IpNet) {
),
};
}

/// Check whether firewalld's StrictForwardPorts setting is enabled.
/// Returns false if firewalld is not installed, not running, or there is any
/// error with the process.
pub fn is_firewalld_strict_forward_enabled() -> bool {
let conn = match Connection::system() {
Ok(conn) => conn,
Err(_) => return false,
};
if !is_firewalld_running(&conn) {
return false;
}

// Fetch current running config
match conn.call_method(
Some("org.fedoraproject.FirewallD1"),
"/org/fedoraproject/FirewallD1/config",
Some("org.freedesktop.DBus.Properties"),
"Get",
&("org.fedoraproject.FirewallD1.config", "StrictForwardPorts"),
) {
Ok(b) => match b.body().deserialize() {
Ok(res) => return res,
Err(_) => return false,
},
Err(_) => {
// Assume any error is related to the property not existing
// (As it will not on older firewalld versions)
// Return false given that.
return false;
}
};
}

/// Check if firewalld's StrictForwardPorts setting is enabled and, if so,
/// whether the container has requested any ports be forwarded. If both are true
/// return a helpful error that port forwarding cannot be performed.
pub fn check_can_forward_ports(setup_portfw: &PortForwardConfig) -> NetavarkResult<()> {
if is_firewalld_strict_forward_enabled() {
let mut portfw_used = setup_portfw.dns_port != 53;
if let Some(ports) = setup_portfw.port_mappings {
portfw_used = portfw_used || !ports.is_empty();
}
if portfw_used {
return Err(NetavarkError::msg(
"Port forwarding not possible as firewalld StrictForwardPorts enabled",
));
}
}
return Ok(());
}
2 changes: 2 additions & 0 deletions src/firewall/iptables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ impl firewall::FirewallDriver for IptablesDriver {
}

fn setup_port_forward(&self, setup_portfw: PortForwardConfig) -> NetavarkResult<()> {
firewalld::check_can_forward_ports(&setup_portfw)?;

if let Some(v4) = setup_portfw.container_ip_v4 {
let subnet_v4 = match setup_portfw.subnet_v4 {
Some(s) => s,
Expand Down
2 changes: 2 additions & 0 deletions src/firewall/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ impl firewall::FirewallDriver for Nftables {
&self,
setup_portfw: internal_types::PortForwardConfig,
) -> NetavarkResult<()> {
firewalld::check_can_forward_ports(&setup_portfw)?;

let mut batch = Batch::new();

let existing_rules = get_netavark_rules()?;
Expand Down

0 comments on commit 33d2a4a

Please sign in to comment.