Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docker interfaces to firewalld docker zone #2548

Merged
merged 1 commit into from
May 21, 2020

Conversation

arkodg
Copy link
Contributor

@arkodg arkodg commented May 4, 2020

If firewalld is running, create a new docker zone and
add the docker interfaces to the docker zone to allow
container networking for distros with Firewalld enabled

Fixes: #2496
Addresses docker/for-linux#957

Debug output

[vagrant@centos8 ~]$ uname -a
Linux centos8 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

[vagrant@centos8 ~]$ sudo firewall-cmd --info-zone docker
docker (active)
  target: ACCEPT
  icmp-block-inversion: no
  interfaces: docker0
  sources: 
  services: 
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

[vagrant@centos8 ~]$ sudo docker run -it alpine nslookup www.google.com
  Server:		10.0.2.3
Address:	10.0.2.3:53

Non-authoritative answer:
Name:	www.google.com
Address: 2607:f8b0:4005:80a::2004

Non-authoritative answer:
Name:	www.google.com
Address: 172.217.164.100

Signed-off-by: Arko Dasgupta [email protected]

@arkodg
Copy link
Contributor Author

arkodg commented May 4, 2020

PTAL @yrro @cpuguy83 @thaJeztah

@yrro
Copy link

yrro commented May 5, 2020

IMO, Docker should follow libvirt's example and create its own docker zone, rather than default to putting its interfaces into trusted.

I think you're modifying firewalld's runtime configuration only, so interfaces will be moved back to the default zone when firewalld is reloaded or restarted. libvirt handles this by listening to firewalld's Reloaded signal, and the normal D-Bus NameOwnerChanged signal (to handle firewalld restarts), and re-doing its runtime configuration changes when signalled.

@yrro
Copy link

yrro commented May 5, 2020

It occurs to me that a user might want different interfaces to be created in different zones. A docker zone would be a good default but it would be nice to have a --zone option to docker network create...

@arkodg arkodg changed the title Add docker interfaces to firewalld trusted zone Add docker interfaces to firewalld docker zone May 5, 2020
@arkodg
Copy link
Contributor Author

arkodg commented May 5, 2020

@yrro thanks for taking a look !

  1. attempted to create a new docker zone but hitting some issues, following it up with firewalld
  2. libnetwork does handle the reload case -
    func OnReloaded(callback func()) {
  3. the notion of zones or just security policy is definitely something that would be nice to have but not sure what the interface would look like yet, and hoping to keep that out of this PR

@arkodg arkodg force-pushed the add-intf-firewalld-zone branch 2 times, most recently from ed91f60 to d0d4036 Compare May 6, 2020 23:57
@arkodg
Copy link
Contributor Author

arkodg commented May 7, 2020

updated the PR to add docker interfaces to a new docker zone
PTAL @yrro @cpuguy83 @thaJeztah

Copy link

@erig0 erig0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, thanks for adding firewalld integration. This will please users. In general this looks functional. I have various cleanup type comments. But those are non-blocking.

I realize that masquerading and port forwarding is still done via iptables. In the future I think firewalld could be used for those as well. Upstream is currently working on support for forward/output filtering.

iptables/firewalld.go Outdated Show resolved Hide resolved
iptables/firewalld.go Outdated Show resolved Hide resolved

settings := getDockerZoneSettings()
// Permanent
if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, settings).Err; err != nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note that the addZone() method has been deprecated. The next firewalld feature release introduces addZone2() which is dictionary based and doesn't require all fields be filled. That being said, docker has to support older versions of firewalld so this is the correct one to call. In the future support for addZone2() should be considered.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, libvirt ships a static zone definition as part of the package. Therefore they avoid the addZone() and reload(). They also allow other things; dhcp, dns, icmp, ipv6-icmp. But they're also filtering (denying) other packets destined to the host.

Comment on lines +258 to +273
// Check if interface is already added to the zone
if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil {
return err
}
// Return if interface is already part of the zone
if contains(intfs, intf) {
logrus.Infof("Firewalld: interface %s already part of %s zone, returning", intf, dockerZone)
return nil
}

logrus.Debugf("Firewalld: adding %s interface to %s zone", intf, dockerZone)
// Runtime
if err := connection.sysObj.Call(dbusInterface+".zone.addInterface", 0, dockerZone, intf).Err; err != nil {
return err
}
return nil
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably aware, but this is racey. It's possible the interface gets deleted/added by another entity/user between the getInterfaces() and addInterface() calls. It may be simpler to always call addInterface() and handle the error codes appropriately. e.g. ALREADY_ENABLED.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep it this way, since AFAIK I would need to compare the error string to handle the above case, which might change in the future

Comment on lines +280 to +292
if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil {
return err
}
// Remove interface if it exists
if !contains(intfs, intf) {
return fmt.Errorf("Firewalld: unable to find interface %s in %s zone", intf, dockerZone)
}

logrus.Debugf("Firewalld: removing %s interface from %s zone", intf, dockerZone)
// Runtime
if err := connection.sysObj.Call(dbusInterface+".zone.removeInterface", 0, dockerZone, intf).Err; err != nil {
return err
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment as above. Maybe always call removeInterface() and handle the errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

@arkodg arkodg force-pushed the add-intf-firewalld-zone branch 2 times, most recently from 57b7633 to 902a280 Compare May 7, 2020 22:55
If firewalld is running, create a new docker zone and
add the docker interfaces to the docker zone to allow
container networking for distros with firewalld enabled

Fixes: moby#2496

Signed-off-by: Arko Dasgupta <[email protected]>
@cpuguy83
Copy link
Member

cpuguy83 commented May 8, 2020

What happens here if someone already has a pre-configured dockerZone zone?
I see in the code we'll return early in certain places.

@arkodg
Copy link
Contributor Author

arkodg commented May 8, 2020

If a user manually created a docker zone, libnetwork would just reuse that and insert interfaces into that zone. However exact behavior of networking depends on the zone settings (which libnetwork will not try and override)

@thaJeztah
Copy link
Member

@arkodg discussing with @cpuguy83 and wondering if we should have an option (configurable on the daemon, and passed to libnetwork) to disable this; wdyt?

@arkodg
Copy link
Contributor Author

arkodg commented May 14, 2020

So I believe that should happen if the user sets iptables=false

Edit - tested this with iptables=false and no docker zone was created

@cpuguy83
Copy link
Member

I think the idea was to not have to set iptables=false but allow users to enable the specific firewalld enhancement (or rather disable it as needed), for example cases where they are already managing this.

I just know that firewall changes Docker does always ends up being problematic.

@arkodg
Copy link
Contributor Author

arkodg commented May 18, 2020

@cpuguy83 even w/o this change, we have been pushing iptables rules via firewalld
I'm not a fan of creating yet another daemon knob . Although iptables=false is less granular it should be the recommended approach if the user wants to manage their own iptables rules via iptables or firewalld

@cpuguy83
Copy link
Member

We've been pushing rules such that firewalld ensures they exist in iptables, but this is changing how firewalld is configured entirely.

@arkodg
Copy link
Contributor Author

arkodg commented May 19, 2020

@cpuguy83 do you prefer having a firewall-integration flag that can be used for firewalld and ufw, and would it be disabled/enabled by default .

Copy link
Member

@cpuguy83 cpuguy83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@arkodg arkodg merged commit 13a4da0 into moby:master May 21, 2020
@arkodg arkodg deleted the add-intf-firewalld-zone branch May 21, 2020 18:35
thaJeztah added a commit to thaJeztah/docker that referenced this pull request Jul 8, 2020
full diff: moby/libnetwork@2e24aed...9e99af2

- moby/libnetwork#2548 Add docker interfaces to firewalld docker zone
    - fixes docker/for-linux#957 DNS Not Resolving under Network [CentOS8]
    - fixes moby/libnetwork#2496 Port Forwarding does not work on RHEL 8 with Firewalld running with FirewallBackend=nftables
- store.getNetworksFromStore() remove unused error return
- moby/libnetwork#2554 Fix 'failed to get network during CreateEndpoint'
    - fixes/addresses docker/for-linux#888 failed to get network during CreateEndpoint
- moby/libnetwork#2558 [master] bridge: disable IPv6 router advertisements
- moby/libnetwork#2563 log error instead if disabling IPv6 router advertisement failed
    - fixes docker/for-linux#1033 Shouldn't be fatal: Unable to disable IPv6 router advertisement: open /proc/sys/net/ipv6/conf/docker0/accept_ra: read-only file system

Signed-off-by: Sebastiaan van Stijn <[email protected]>
docker-jenkins pushed a commit to docker-archive/docker-ce that referenced this pull request Jul 13, 2020
full diff: moby/libnetwork@2e24aed...9e99af2

- moby/libnetwork#2548 Add docker interfaces to firewalld docker zone
    - fixes docker/for-linux#957 DNS Not Resolving under Network [CentOS8]
    - fixes moby/libnetwork#2496 Port Forwarding does not work on RHEL 8 with Firewalld running with FirewallBackend=nftables
- store.getNetworksFromStore() remove unused error return
- moby/libnetwork#2554 Fix 'failed to get network during CreateEndpoint'
    - fixes/addresses docker/for-linux#888 failed to get network during CreateEndpoint
- moby/libnetwork#2558 [master] bridge: disable IPv6 router advertisements
- moby/libnetwork#2563 log error instead if disabling IPv6 router advertisement failed
    - fixes docker/for-linux#1033 Shouldn't be fatal: Unable to disable IPv6 router advertisement: open /proc/sys/net/ipv6/conf/docker0/accept_ra: read-only file system

Signed-off-by: Sebastiaan van Stijn <[email protected]>
Upstream-commit: 219e7e7ddcf5f0314578d2a517fc0832f03622c1
Component: engine
bmwiedemann pushed a commit to bmwiedemann/openSUSE that referenced this pull request Dec 9, 2020
https://build.opensuse.org/request/show/851816
by user mrostecki + dimstar_suse
- Add a patch which makes Docker compatible with firewalld with
  nftables backend. Backport of moby/libnetwork#2548
  (boo#1178801, SLE-16460)
  * boo1178801-0001-Add-docker-interfaces-to-firewalld-docker-zone.patch
@idc77
Copy link

idc77 commented May 13, 2024

Rocky 9

dnf info firewalld

Last metadata expiration check: 1:39:33 ago on Mon 13 May 2024 05:54:03 PM CEST.
Installed Packages
Name         : firewalld
Version      : 1.3.4
Release      : 1.el9
Architecture : noarch
Size         : 2.0 M
Source       : firewalld-1.3.4-1.el9.src.rpm
Repository   : @System
From repo    : baseos
Summary      : A firewall daemon with D-Bus interface providing a dynamic firewall
URL          : http://www.firewalld.org
License      : GPLv2+
Description  : firewalld is a firewall service daemon that provides a dynamic customizable
             : firewall with a D-Bus interface.
dnf info docker-ce
Last metadata expiration check: 1:40:29 ago on Mon 13 May 2024 05:54:03 PM CEST.
Installed Packages
Name         : docker-ce
Epoch        : 3
Version      : 26.1.2
Release      : 1.el9
Architecture : x86_64
Size         : 104 M
Source       : docker-ce-26.1.2-1.el9.src.rpm
Repository   : @System
From repo    : docker-ce-stable
Summary      : The open-source application container engine
URL          : https://www.docker.com
License      : ASL 2.0
Description  : Docker is a product for you to build, ship and run any application as a
             : lightweight container.
             : 
             : Docker containers are both hardware-agnostic and platform-agnostic. This means
             : they can run anywhere, from your laptop to the largest cloud compute instance
             : and everything in between - and they don't require you to use a particular
             : language, framework or packaging system. That makes them great building blocks
             : for deploying and scaling web apps, databases, and backend services without
             : depending on a particular stack or provider.
May 13 19:08:40 my.server.tld systemd[1]: Starting firewalld - dynamic firewall daemon...
May 13 19:08:40 my.server.tld systemd[1]: Started firewalld - dynamic firewall daemon.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -D PREROUTING -m addrtype --dst-type LOCAL -j DOCKER' failed: iptables v1.8.10 (nf_tables): Chain 'DOCKER' d>
                                           Try `iptables -h' or 'iptables --help' for more information.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -D OUTPUT -m addrtype --dst-type LOCAL ! --dst 127.0.0.0/8 -j DOCKER' failed: iptables v1.8.10 (nf_tables): >
                                           Try `iptables -h' or 'iptables --help' for more information.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -D OUTPUT -m addrtype --dst-type LOCAL -j DOCKER' failed: iptables v1.8.10 (nf_tables): Chain 'DOCKER' does >
                                           Try `iptables -h' or 'iptables --help' for more information.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -D PREROUTING' failed: iptables: Bad rule (does a matching rule exist in that chain?).
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -D OUTPUT' failed: iptables: Bad rule (does a matching rule exist in that chain?).
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -F DOCKER' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t nat -X DOCKER' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -F DOCKER' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -X DOCKER' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -F DOCKER-ISOLATION-STAGE-1' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -X DOCKER-ISOLATION-STAGE-1' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -F DOCKER-ISOLATION-STAGE-2' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -X DOCKER-ISOLATION-STAGE-2' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -F DOCKER-ISOLATION' failed: iptables: No chain/target/match by that name.
May 13 19:08:42 my.server.tld firewalld[727]: WARNING: COMMAND_FAILED: '/usr/sbin/iptables -w10 -t filter -X DOCKER-ISOLATION' failed: iptables: No chain/target/match by that name.

All docker containers listening on 0.0.0.0:PORT are accessible from the outside via servername:PORT

@erig0
Copy link

erig0 commented May 13, 2024

All docker containers listening on 0.0.0.0:PORT are accessible from the outside via servername:PORT

This is expected. This what docker requests.

I recently wrote a blog about this and how to do strict filtering of docker containers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Port Forwarding does not work on RHEL 8 with Firewalld running with FirewallBackend=nftables
7 participants