|
| 1 | +--- |
| 2 | +title: Docker with nftables |
| 3 | +weight: 10 |
| 4 | +description: How Docker works with nftables |
| 5 | +keywords: network, nftables, firewall |
| 6 | +--- |
| 7 | + |
| 8 | +> [!WARNING] |
| 9 | +> |
| 10 | +> Support for nftables is experimental, configuration options, behavior and |
| 11 | +> implementation may all change in future releases. |
| 12 | +> The rules for overlay networks have not yet been migrated from iptables. |
| 13 | +> So, nftables cannot be enabled when the daemon has Swarm enabled. |
| 14 | +
|
| 15 | +To use nftables instead of iptables, use Docker Engine option |
| 16 | +`--firewall-backend=nftables` on its command line, or `"firewall-backend": true` |
| 17 | +in its configuration file. You may also need to modify IP forwarding configuration |
| 18 | +on the host, and migrate rules from the iptables `DOCKER-USER` chain, see |
| 19 | +[migrating from iptables to nftables](#migrating-from-iptables-to-nftables). |
| 20 | + |
| 21 | +For bridge networks, Docker creates nftables rules in the host's network |
| 22 | +namespace. For bridge and other network types, nftables rules for DNS are |
| 23 | +also created in the container's network namespace. |
| 24 | + |
| 25 | +Creation of nftables rules can be disabled using daemon options `iptables` |
| 26 | +and `ip6tables`. _These options apply to both iptables and nftables._ |
| 27 | +See [Prevent Docker from manipulating firewall rules](packet-filtering-firewalls.md#prevent-docker-from-manipulating-firewall-rules). |
| 28 | +But, this is not appropriate for most users, it is likely to break |
| 29 | +container networking. |
| 30 | + |
| 31 | +## Docker's nftables tables |
| 32 | + |
| 33 | +For bridge networks, Docker creates two tables, `ip docker-bridges` and |
| 34 | +`ip6 docker-bridges`. |
| 35 | + |
| 36 | +Each table contains a number of [base chains](https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_base_chains), |
| 37 | +and further chains are added for each bridge network. The moby project |
| 38 | +has some [internal documentation](https://github.com/moby/moby/blob/master/integration/network/bridge/nftablesdoc/index.md) |
| 39 | +describing its nftables, and how they depend on network and container |
| 40 | +configuration. But, the tables and their rules are likely to change between |
| 41 | +Docker Engine releases. |
| 42 | + |
| 43 | +Do not modify Docker's tables directly as the modifications are likely to |
| 44 | +be lost, Docker expects to have full ownership of its tables. |
| 45 | + |
| 46 | +> [!NOTE] |
| 47 | +> |
| 48 | +> Because iptables has a fixed set of chains, equivalent to nftables base |
| 49 | +> chains, all rules are included in those chains. The `DOCKER-USER` chain |
| 50 | +> is supplied as a way to insert rules into the `filter` table's `FORWARD` |
| 51 | +> chain, to run before Docker's rules. |
| 52 | +> In Docker's nftables implementation, there is no `DOCKER-USER` chain. |
| 53 | +> Instead, rules can be added in separate tables, with base chains that |
| 54 | +> have the same types and hook points as Docker's base chains. If necessary, |
| 55 | +> [base chain priority](https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_priority) |
| 56 | +> can be used to tell nftables which order to call the chains in. |
| 57 | +> Docker uses well known [priority values](https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook) for each of its base chains. |
| 58 | +
|
| 59 | +## Migrating from iptables to nftables |
| 60 | + |
| 61 | +If the Docker daemon has been running with the iptables firewall backend, |
| 62 | +restarting it with the nftables backend will delete most of Docker's iptables |
| 63 | +chains and rules, and create nftables rules instead. |
| 64 | + |
| 65 | +If IP forwarding is not enabled, Docker will report an error when creating |
| 66 | +a bridge network that needs it. Because of the default bridge, if IPv4 |
| 67 | +forwarding is disabled, the error will be reported during daemon startup. |
| 68 | +See [IP forwarding](#ip-forwarding). |
| 69 | + |
| 70 | +If you have rules in the `DOCKER-USER` chain, see [Migrating |
| 71 | +`DOCKER-USER`](#migrating-docker-user). |
| 72 | + |
| 73 | +You may need to manually update the iptables `FORWARD` policy if it has |
| 74 | +been set to `DROP` by Docker with iptables, or as part of your host's |
| 75 | +firewall configuration. See [FORWARD policy in iptables](#forward-policy-in-iptables). |
| 76 | + |
| 77 | +### IP forwarding |
| 78 | + |
| 79 | +IP forwarding on the Docker host enables Docker functionality including port |
| 80 | +publishing, communication between bridge networks, and direct routing from |
| 81 | +outside the host to containers in bridge networks. |
| 82 | + |
| 83 | +When running with iptables, depending on network and daemon configuration, |
| 84 | +Docker may enable IPv4 and IPv6 forwarding on the host. |
| 85 | + |
| 86 | +With its nftables firewall backend enabled, Docker will not enable IP forwarding |
| 87 | +itself. It will report an error if forwarding is needed, but not already enabled. |
| 88 | +To disable Docker's check for IP forwarding, allowing it to start and |
| 89 | +create networks when it reports that forwarding is disabled, use Daemon option |
| 90 | +`--ip-forward=false`, or `"ip-forward": false` in its configuration file. |
| 91 | + |
| 92 | +> [!WARNING] |
| 93 | +> |
| 94 | +> When enabling IP forwarding, make sure you have firewall rules to block |
| 95 | +> unwanted forwarding between non-Docker interfaces. |
| 96 | +
|
| 97 | +> [!NOTE] |
| 98 | +> |
| 99 | +> If you stop Docker to migrate to nftables, Docker may already have enabled |
| 100 | +> forwarding. If nothing else enables it when you reboot, Docker will |
| 101 | +> probably not start up. |
| 102 | +
|
| 103 | +If Docker is in a VM that has a single network interface and no other |
| 104 | +software running, there is probably no unwanted forwarding to block. |
| 105 | +But, on a physical host with multiple network interfaces, forwarding |
| 106 | +between those interfaces should probably be blocked unless the host |
| 107 | +is acting as a router. |
| 108 | + |
| 109 | +To enable IP forwarding on the host, set the following sysctls: |
| 110 | + |
| 111 | +- `net.ipv4.ip_forward=1` |
| 112 | +- `net.ipv6.conf.all.forwarding=1` |
| 113 | + |
| 114 | +If your host uses `systemd`, you may be able to use `systemd-sysctl`. For |
| 115 | +example, by editing `/etc/sysctl.d/99-sysctl.conf`. |
| 116 | + |
| 117 | +If the host is running `firewalld`, you may be able to use it to block |
| 118 | +unwanted forwarding. Docker's bridges are in a firewalld zone called |
| 119 | +`docker`, it creates a forwarding policy called `docker-forwarding` that |
| 120 | +allows forwarding from `ANY` zone to the `docker` zone. |
| 121 | + |
| 122 | +As an example, to use nftables to block forwarding between interfaces `eth0` |
| 123 | +and `eth1`, you could use: |
| 124 | + |
| 125 | +```console |
| 126 | +table inet no-ext-forwarding { |
| 127 | + chain no-ext-forwarding { |
| 128 | + type filter hook forward priority filter; policy accept; |
| 129 | + iifname "eth0" oifname "eth1" drop |
| 130 | + iifname "eth1" oifname "eth0" drop |
| 131 | + } |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +### FORWARD policy in iptables |
| 136 | + |
| 137 | +An iptables chain with `FORWARD` policy `DROP` will drop packets that have |
| 138 | +been accepted by Docker's nftables rules, because the packet will be processed |
| 139 | +by the iptables chains as well as Docker's nftables chains. |
| 140 | + |
| 141 | +Some features, including port publishing, will not work unless the `DROP` |
| 142 | +policy is removed, or additional iptables rules are added to the iptables |
| 143 | +`FORWARD` chain to accept Docker-related traffic. |
| 144 | + |
| 145 | +When Docker is using iptables, and it enables IP forwarding on the host, |
| 146 | +it sets the default policy of the iptables `FORWARD` chain to `DROP`. So, |
| 147 | +if you stop Docker to migrate to nftables, it may have set a `DROP` that |
| 148 | +you need to remove. It will be removed anyway on reboot. |
| 149 | + |
| 150 | +If you want to keep using rules in `DOCKER-USER` that rely on the chain |
| 151 | +having policy `DROP`, you will need to add explicit `ACCEPT` rules for |
| 152 | +Docker related traffic. |
| 153 | + |
| 154 | +To check the current iptables `FORWARD` policy, use: |
| 155 | + |
| 156 | +```console |
| 157 | +$ iptables -L FORWARD |
| 158 | +Chain FORWARD (policy DROP) |
| 159 | +target prot opt source destination |
| 160 | +$ ip6tables -L FORWARD |
| 161 | +Chain FORWARD (policy ACCEPT) |
| 162 | +target prot opt source destination |
| 163 | +``` |
| 164 | + |
| 165 | +To set the iptables policies to `ACCEPT` for IPv4 and IPv6: |
| 166 | + |
| 167 | +```console |
| 168 | +$ iptables -P FORWARD ACCEPT |
| 169 | +$ ip6tables -P FORWARD ACCEPT |
| 170 | +``` |
| 171 | + |
| 172 | +### Migrating `DOCKER-USER` |
| 173 | + |
| 174 | +With firewall backend "iptables", rules added to the iptables `DOCKER-USER` |
| 175 | +are processed before Docker's rules in the filter table's `FORWARD` chain. |
| 176 | + |
| 177 | +When starting the daemon with nftables after running with iptables, Docker |
| 178 | +will not remove the jump from the `FORWARD` chain to `DOCKER-USER`. So, |
| 179 | +rules created in `DOCKER-USER` will continue to run until the jump is |
| 180 | +removed or the host is rebooted. |
| 181 | + |
| 182 | +When starting with nftables, the daemon will not add the jump. So, unless |
| 183 | +there is an existing jump, rules in `DOCKER-USER` will be ignored. |
| 184 | + |
| 185 | +#### Migrating ACCEPT rules |
| 186 | + |
| 187 | +Some rules in the `DOCKER-USER` chain will continue to work. For example, if a |
| 188 | +packet is dropped, it will be dropped before or after the nftables rules in |
| 189 | +Docker's `filter-FORWARD` chain. But other rules, particularly `ACCEPT` rules |
| 190 | +to override Docker's `DROP` rules, will not work. |
| 191 | + |
| 192 | +In nftables, an "accept" rule is not final. It terminates processing |
| 193 | +for its base chain, but the accepted packet will still be processed by |
| 194 | +other base chains, which may drop it. |
| 195 | + |
| 196 | +To override Docker's `drop` rule, you must use a firewall mark. Select a |
| 197 | +mark not already in use on your host, and use Docker Engine option |
| 198 | +`--bridge-accept-fwmark`. |
| 199 | + |
| 200 | +For example, `--bridge-accept-fwmark=1` tells the daemon to accept any |
| 201 | +packet with an `fwmark` value of `1`. Optionally, you can supply a mask |
| 202 | +to match specific bits in the mark, `--bridge-accept-fwmark=0x1/0x3`. |
| 203 | + |
| 204 | +Then, instead of accepting the packet in `DOCKER-USER`, add the firewall |
| 205 | +mark you have chosen and Docker will not drop it. |
| 206 | + |
| 207 | +The firewall mark must be added before Docker's rules run. So if the mark |
| 208 | +is added in a chain with type `filter` and hook `forward`, it must have |
| 209 | +priority `filter - 1` or lower. |
| 210 | + |
| 211 | +#### Replacing `DOCKER-USER` with an nftables table |
| 212 | + |
| 213 | +Because nftables doesn't have pre-defined chains, to replace the `DOCKER-USER` |
| 214 | +chain you can create your own table and add chains and rules to it. |
| 215 | + |
| 216 | +The `DOCKER-USER` chain has type `filter` and hook `forward`, so it can |
| 217 | +only have rules in the filter forward chain. The base chains in your |
| 218 | +table can have any `type` or `hook`. If your rules need to run before |
| 219 | +Docker's rules, give the base chains a lower `priority` number than |
| 220 | +Docker's chain. Or, a higher priority to make sure they run after Docker's |
| 221 | +rules. |
| 222 | + |
| 223 | +Docker's base chains use the priority values defined at |
| 224 | +[priority values](https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook) |
| 225 | + |
| 226 | +#### Example: restricting external connections to containers |
| 227 | + |
| 228 | +By default, any remote host can connect to ports published to the Docker |
| 229 | +host's external addresses. |
| 230 | + |
| 231 | +To allow only a specific IP or network to access the containers, create a |
| 232 | +table with a base chain that has a drop rule. For example, the |
| 233 | +following table drops packets from all IP addresses except `192.0.2.2`: |
| 234 | + |
| 235 | +```console |
| 236 | +table ip my-table { |
| 237 | + chain my-filter-forward { |
| 238 | + type filter hook forward priority filter; policy accept; |
| 239 | + iifname "ext_if" ip saddr != 192.0.2.2 counter drop |
| 240 | + } |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +You will need to change `ext_if` to your host's external interface name. |
| 245 | + |
| 246 | +You could instead allow connections from a source subnet. The following |
| 247 | +table only allows access from the subnet `192.0.2.0/24`: |
| 248 | + |
| 249 | +```console |
| 250 | +table ip my-table { |
| 251 | + chain my-filter-forward { |
| 252 | + type filter hook forward priority filter; policy accept; |
| 253 | + iifname "ext_if" ip saddr != 192.0.2.2/24 counter drop |
| 254 | + } |
| 255 | +} |
| 256 | +``` |
| 257 | + |
| 258 | +If you are running other services on the host that use IP forwarding |
| 259 | +and need to be accessed by different external hosts, you will need more |
| 260 | +specific filters. For example, to match the default prefix `br-` of |
| 261 | +bridge devices belonging to Docker's user-defined bridge networks: |
| 262 | + |
| 263 | +```console |
| 264 | +table ip my-table { |
| 265 | + chain my-filter-forward { |
| 266 | + type filter hook forward priority filter; policy accept; |
| 267 | + iifname "ext_if" oifname "br-*" ip saddr != 192.0.2.2/24 counter drop |
| 268 | + } |
| 269 | +} |
| 270 | +``` |
| 271 | + |
| 272 | +`nftables` is complicated. There is a lot more information at the |
| 273 | +[nftables wiki](https://wiki.nftables.org/wiki-nftables/index.php/Main_Page). |
0 commit comments