Skip to content

Split iptables and port publishing into separate pages #23217

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 9 additions & 45 deletions content/manuals/engine/network/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,54 +129,18 @@ $ docker run --rm -it --network container:redis example/redis-cli -h 127.0.0.1

## Published ports

By default, when you create or run a container using `docker create` or `docker run`,
containers on bridge networks don't expose any ports to the outside world.
Use the `--publish` or `-p` flag to make a port available to services
outside the bridge network.
This creates a firewall rule in the host,
mapping a container port to a port on the Docker host to the outside world.
Here are some examples:

| Flag value | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-p 8080:80` | Map port `8080` on the Docker host to TCP port `80` in the container. |
| `-p 192.168.1.100:8080:80` | Map port `8080` on the Docker host IP `192.168.1.100` to TCP port `80` in the container. |
| `-p 8080:80/udp` | Map port `8080` on the Docker host to UDP port `80` in the container. |
| `-p 8080:80/tcp -p 8080:80/udp` | Map TCP port `8080` on the Docker host to TCP port `80` in the container, and map UDP port `8080` on the Docker host to UDP port `80` in the container. |

> [!IMPORTANT]
>
> Publishing container ports is insecure by default. Meaning, when you publish
> a container's ports it becomes available not only to the Docker host, but to
> the outside world as well.
>
> If you include the localhost IP address (`127.0.0.1`, or `::1`) with the
> publish flag, only the Docker host and its containers can access the
> published container port.
>
> ```console
> $ docker run -p 127.0.0.1:8080:80 -p '[::1]:8080:80' nginx
> ```
>
> > [!WARNING]
> >
> > In releases older than 28.0.0, hosts within the same L2 segment (for example,
> > hosts connected to the same network switch) can reach ports published to localhost.
> > For more information, see
> > [moby/moby#45610](https://github.com/moby/moby/issues/45610)

If you want to make a container accessible to other containers,
it isn't necessary to publish the container's ports.
You can enable inter-container communication by connecting the containers to the
same network, usually a [bridge network](./drivers/bridge.md).

Ports on the host's IPv6 addresses will map to the container's IPv4 address
if no host IP is given in a port mapping, the bridge network is IPv4-only,
and `--userland-proxy=true` (default).
When you create or run a container using `docker create` or `docker run`, all
ports of containers on bridge networks are accessible from the Docker host and
other containers connected to the same network. Ports are not accessible from
outside the host or, with the default configuration, from containers in other
networks.

Use the `--publish` or `-p` flag to make a port available outside the host,
and to containers in other bridge networks.

For more information about port mapping, including how to disable it and use
direct routing to containers, see
[packet filtering and firewalls](./packet-filtering-firewalls.md).
[port publishing](./port-publishing.md).

## IP address and hostname

Expand Down
131 changes: 131 additions & 0 deletions content/manuals/engine/network/firewall-iptables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: Docker with iptables
weight: 10
description: How Docker works with iptables
keywords: network, iptables, firewall
---

Docker creates nftables rules in the host's network namespace for bridge
networks. For bridge and other network types, nftables rules for DNS are
also created in the container's network namespace.

Creation of iptables rules can be disabled using daemon options `iptables`
and `ip6tables`, see [Prevent Docker from manipulating firewall rules](packet-filtering-firewalls.md#prevent-docker-from-manipulating-firewall-rules).
But, this option is not appropriate for most users, it is likely to break
container networking.

### Docker and iptables chains

To support bridge and overlay networks, Docker creates the following custom
`iptables` chains:

* `DOCKER-USER`
* A placeholder for user-defined rules that will be processed before rules
in the `DOCKER-FORWARD` and `DOCKER` chains.
* `DOCKER-FORWARD`
* The first stage of processing for Docker's networks. Rules that pass packets
that are not related to established connections to the other Docker chains,
as well as rules to accept packets that are part of established connections.
* `DOCKER`, `DOCKER-BRIDGE`, `DOCKER-INTERNAL`
* Rules that determine whether a packet that is not part of an established
connection should be accepted, based on the port forwarding configuration
of running containers.
* `DOCKER-CT`
* Per-bridge connection tracking rules.
* `DOCKER-INGRESS`
* Rules related to Swarm networking.

In the `FORWARD` chain, Docker adds rules that unconditionally jump to the
`DOCKER-USER`, `DOCKER-FORWARD` and `DOCKER-INGRESS` chains.

In the `nat` table, Docker creates chain `DOCKER` and adds rules to implement
masquerading and port-mapping.

Docker requires IP Forwarding to be enabled on the host for its default
bridge network configuration. If it enables IP Forwarding, it also sets the
default policy of the iptables `FORWARD` chain in the `filter` table to `DROP`.

### Add iptables policies before Docker's rules

Packets that get accepted or rejected by rules in these custom chains will not
be seen by user-defined rules appended to the `FORWARD` chain. So, to add
additional rules to filter these packets, use the `DOCKER-USER` chain.

Rules appended to the `FORWARD` chain will be processed after Docker's rules.

### Match the original IP and ports for requests

When packets arrive to the `DOCKER-USER` chain, they have already passed through
a Destination Network Address Translation (DNAT) filter. That means that the
`iptables` flags you use can only match internal IP addresses and ports of
containers.

If you want to match traffic based on the original IP and port in the network
request, you must use the
[`conntrack` iptables extension](https://ipset.netfilter.org/iptables-extensions.man.html#lbAO).
For example:

```console
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctorigdst 198.51.100.2 --ctorigdstport 80 -j ACCEPT
```

> [!IMPORTANT]
>
> Using the `conntrack` extension may result in degraded performance.

### Allow forwarding between host interfaces

Check warning on line 77 in content/manuals/engine/network/firewall-iptables.md

View workflow job for this annotation

GitHub Actions / validate (vale)

[vale] reported by reviewdog 🐶 [Docker.RecommendedWords] Consider using 'let' instead of 'Allow' Raw Output: {"message": "[Docker.RecommendedWords] Consider using 'let' instead of 'Allow'", "location": {"path": "content/manuals/engine/network/firewall-iptables.md", "range": {"start": {"line": 77, "column": 5}}}, "severity": "INFO"}

If Docker has set the default policy of the `FORWARD` chain in the `filter`
table to `DROP`, a rule in `DOCKER-USER` can be used to allow forwarding

Check warning on line 80 in content/manuals/engine/network/firewall-iptables.md

View workflow job for this annotation

GitHub Actions / validate (vale)

[vale] reported by reviewdog 🐶 [Docker.RecommendedWords] Consider using 'let' instead of 'allow' Raw Output: {"message": "[Docker.RecommendedWords] Consider using 'let' instead of 'allow'", "location": {"path": "content/manuals/engine/network/firewall-iptables.md", "range": {"start": {"line": 80, "column": 57}}}, "severity": "INFO"}
between host interfaces. For example:

```console
$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT
```

### Restrict external connections to containers

By default, all external source IPs are allowed to connect to ports that have
been published to the Docker host's addresses.

To allow only a specific IP or network to access the containers, insert a

Check warning on line 92 in content/manuals/engine/network/firewall-iptables.md

View workflow job for this annotation

GitHub Actions / validate (vale)

[vale] reported by reviewdog 🐶 [Docker.RecommendedWords] Consider using 'let' instead of 'allow' Raw Output: {"message": "[Docker.RecommendedWords] Consider using 'let' instead of 'allow'", "location": {"path": "content/manuals/engine/network/firewall-iptables.md", "range": {"start": {"line": 92, "column": 4}}}, "severity": "INFO"}
negated rule at the top of the `DOCKER-USER` filter chain. For example, the
following rule drops packets from all IP addresses except `192.0.2.2`:

```console
$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.2 -j DROP
```

You will need to change `ext_if` to correspond with your
host's actual external interface. You could instead allow connections from a

Check warning on line 101 in content/manuals/engine/network/firewall-iptables.md

View workflow job for this annotation

GitHub Actions / validate (vale)

[vale] reported by reviewdog 🐶 [Docker.RecommendedWords] Consider using 'let' instead of 'allow' Raw Output: {"message": "[Docker.RecommendedWords] Consider using 'let' instead of 'allow'", "location": {"path": "content/manuals/engine/network/firewall-iptables.md", "range": {"start": {"line": 101, "column": 53}}}, "severity": "INFO"}
source subnet. The following rule only allows access from the subnet `192.0.2.0/24`:

```console
$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.0/24 -j DROP
```

Finally, you can specify a range of IP addresses to accept using `--src-range`
(Remember to also add `-m iprange` when using `--src-range` or `--dst-range`):

```console
$ iptables -I DOCKER-USER -m iprange -i ext_if ! --src-range 192.0.2.1-192.0.2.3 -j DROP
```

You can combine `-s` or `--src-range` with `-d` or `--dst-range` to control both
the source and destination. For instance, if the Docker host has addresses
`2001:db8:1111::2` and `2001:db8:2222::2`, you can make rules specific to
`2001:db8:1111::2` and leave `2001:db8:2222::2` open.

You may need to allow responses from servers outside the permitted external address

Check warning on line 120 in content/manuals/engine/network/firewall-iptables.md

View workflow job for this annotation

GitHub Actions / validate (vale)

[vale] reported by reviewdog 🐶 [Docker.RecommendedWords] Consider using 'let' instead of 'allow' Raw Output: {"message": "[Docker.RecommendedWords] Consider using 'let' instead of 'allow'", "location": {"path": "content/manuals/engine/network/firewall-iptables.md", "range": {"start": {"line": 120, "column": 17}}}, "severity": "INFO"}
ranges. For example, containers may send DNS or HTTP requests to hosts that are
not allowed to access the container's services. The following rule accepts any
incoming or outgoing packet belonging to a flow that has already been accepted
by other rules. It must be placed before `DROP` rules that restrict access from
external address ranges.

```console
$ iptables -I DOCKER-USER -m state --state RELATED,ESTABLISHED -j ACCEPT
```

`iptables` is complicated. There is a lot more information at [Netfilter.org HOWTO](https://www.netfilter.org/documentation/HOWTO/NAT-HOWTO.html).
Loading
Loading