diff --git a/content/manuals/engine/network/_index.md b/content/manuals/engine/network/_index.md index 97583dfe8af..369f4c869ef 100644 --- a/content/manuals/engine/network/_index.md +++ b/content/manuals/engine/network/_index.md @@ -18,55 +18,85 @@ aliases: --- Container networking refers to the ability for containers to connect to and -communicate with each other, or to non-Docker workloads. +communicate with each other, and with non-Docker network services. Containers have networking enabled by default, and they can make outgoing connections. A container has no information about what kind of network it's -attached to, or whether their peers are also Docker workloads or not. A +attached to, or whether its network peers are also Docker containers. A container only sees a network interface with an IP address, a gateway, a -routing table, DNS services, and other networking details. That is, unless the -container uses the `none` network driver. +routing table, DNS services, and other networking details. This page describes networking from the point of view of the container, and the concepts around container networking. -This page doesn't describe OS-specific details about how Docker networks work. -For information about how Docker manipulates `iptables` rules on Linux, -see [Packet filtering and firewalls](packet-filtering-firewalls.md). + +When Docker Engine on Linux starts for the first time, it has a single +built-in network called the "default bridge" network. When you run a +container with no `--network` option, it is connected to the default bridge. + +Containers attached to the default bridge have access to network services +outside the Docker host. They use "masquerading" which means, if the +Docker host has Internet access, no additional configuration is needed +for the container to have Internet access. + +For example, to run a container on the default bridge network, and have +it ping an Internet host: + +```console +$ docker run --rm -ti busybox ping -c1 docker.com +PING docker.com (23.185.0.4): 56 data bytes +64 bytes from 23.185.0.4: seq=0 ttl=62 time=6.564 ms + +--- docker.com ping statistics --- +1 packets transmitted, 1 packets received, 0% packet loss +round-trip min/avg/max = 6.564/6.564/6.564 ms +``` ## User-defined networks -You can create custom, user-defined networks, and connect multiple containers -to the same network. Once connected to a user-defined network, containers can -communicate with each other using container IP addresses or container names. +With the default configuration, containers attached to the default +bridge network have unrestricted network access to each other using +container IP addresses. They cannot refer to each other by name. + +It can be useful to separate groups of containers that should have full +access to each other, but restricted access to containers in other groups. + +You can create custom, user-defined networks, and connect groups of containers +to the same network. Once connected to a user-defined network, containers +can communicate with each other using container IP addresses or container names. The following example creates a network using the `bridge` network driver and -running a container in the created network: +runs a container in that network: ```console $ docker network create -d bridge my-net -$ docker run --network=my-net -itd --name=container3 busybox +$ docker run --network=my-net -it --name=container3 busybox ``` ### Drivers -The following network drivers are available by default, and provide core -networking functionality: +Docker Engine has a number of network drivers, as well as the default "bridge". +On Linux, the following built-in network drivers are available: -| Driver | Description | -| :-------- | :----------------------------------------------------------------------- | -| `bridge` | The default network driver. | -| `host` | Remove network isolation between the container and the Docker host. | -| `none` | Completely isolate a container from the host and other containers. | -| `overlay` | Overlay networks connect multiple Docker daemons together. | -| `ipvlan` | IPvlan networks provide full control over both IPv4 and IPv6 addressing. | -| `macvlan` | Assign a MAC address to a container. | +| Driver | Description | +|:--------------------------------|:--------------------------------------------------------------------| +| [bridge](./drivers/bridge.md) | The default network driver. | +| [host](./drivers/host.md) | Remove network isolation between the container and the Docker host. | +| [none](./drivers/none.md) | Completely isolate a container from the host and other containers. | +| [overlay](./drivers/overlay.md) | Swarm Overlay networks connect multiple Docker daemons together. | +| [ipvlan](./drivers/ipvlan.md) | Connect containers to external VLANs. | +| [macvlan](./drivers/macvlan.md) | Containers appear as devices on the host's network. | -For more information about the different drivers, see [Network drivers -overview](./drivers/_index.md). +More information can be found in the network driver specific pages, including +their configuration options and details about their functionality. + +Native Windows containers have a different set of drivers, see +[Windows container network drivers](https://learn.microsoft.com/en-us/virtualization/windowscontainers/container-networking/network-drivers-topologies). ### Connecting to multiple networks -A container can be connected to multiple networks. +Connecting a container to a network can be compared to connecting an Ethernet +cable to a physical host. Just as a host can be connected to multiple Ethernet +networks, a container can be connected to multiple Docker networks. For example, a frontend container may be connected to a bridge network with external access, and a @@ -78,6 +108,8 @@ A container may also be connected to different types of network. For example, an `ipvlan` network to provide internet access, and a `bridge` network for access to local services. +Containers can also share networking stacks, see [Container networks](#container-networks). + When sending packets, if the destination is an address in a directly connected network, packets are sent to that network. Otherwise, packets are sent to a default gateway for routing to their destination. In the example above, @@ -99,84 +131,20 @@ $ docker run --network name=gwnet,gw-priority=1 --network anet1 --name myctr myi $ docker network connect anet2 myctr ``` -## Container networks - -In addition to user-defined networks, you can attach a container to another -container's networking stack directly, using the `--network -container:` flag format. - -The following flags aren't supported for containers using the `container:` -networking mode: - -- `--add-host` -- `--hostname` -- `--dns` -- `--dns-search` -- `--dns-option` -- `--mac-address` -- `--publish` -- `--publish-all` -- `--expose` - -The following example runs a Redis container, with Redis binding to -`localhost`, then running the `redis-cli` command and connecting to the Redis -server over the `localhost` interface. - -```console -$ docker run -d --name redis example/redis --bind 127.0.0.1 -$ 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 @@ -237,7 +205,30 @@ containers. To pass additional hosts into a container, refer to [add entries to container hosts file](/reference/cli/docker/container/run.md#add-host) in the `docker run` reference documentation. -## Proxy server +## Container networks -If your container needs to use a proxy server, see -[Use a proxy server](/manuals/engine/daemon/proxy.md). +In addition to user-defined networks, you can attach a container to another +container's networking stack directly, using the `--network +container:` flag format. + +The following flags aren't supported for containers using the `container:` +networking mode: + +- `--add-host` +- `--hostname` +- `--dns` +- `--dns-search` +- `--dns-option` +- `--mac-address` +- `--publish` +- `--publish-all` +- `--expose` + +The following example runs a Redis container, with Redis binding to +`localhost`, then running the `redis-cli` command and connecting to the Redis +server over the `localhost` interface. + +```console +$ docker run -d --name redis example/redis --bind 127.0.0.1 +$ docker run --rm -it --network container:redis example/redis-cli -h 127.0.0.1 +``` diff --git a/content/manuals/engine/network/drivers/bridge.md b/content/manuals/engine/network/drivers/bridge.md index 4f0b3268d36..03128432bfe 100644 --- a/content/manuals/engine/network/drivers/bridge.md +++ b/content/manuals/engine/network/drivers/bridge.md @@ -11,16 +11,29 @@ aliases: - /network/drivers/bridge/ --- +A Docker bridge network has an IPv4 subnet and, optionally, an IPv6 subnet. +Each container connected to the bridge network has a network interface with +addresses in the network's subnets. By default, it: +- Allows unrestricted network access to containers in the network from + the host, and from other containers connected to the same bridge network. +- Blocks access from containers in other networks and from outside the + Docker host. +- Uses masquerading to give containers external network access. Devices on + the host's external networks only see the IP address of the Docker host. +- Supports port publishing, where network traffic is forwarded between + container ports and ports on host IP addresses. The published ports + can be accessed from outside the Docker host, on its IP addresses. + In terms of networking, a bridge network is a Link Layer device which forwards traffic between network segments. A bridge can be a hardware device or a software device running within a host machine's kernel. In terms of Docker, a bridge network uses a software bridge which lets containers connected to the same bridge network communicate, while providing -isolation from containers that aren't connected to that bridge network. The -Docker bridge driver automatically installs rules in the host machine so that -containers on different bridge networks can't communicate directly with each -other. +isolation from containers that aren't connected to that bridge network. By +default, the Docker bridge driver automatically installs rules in the host +machine so that containers connected to different bridge networks can only +communicate with each other using published ports. Bridge networks apply to containers running on the same Docker daemon host. For communication among containers running on different Docker daemon hosts, you diff --git a/content/manuals/engine/network/firewall-iptables.md b/content/manuals/engine/network/firewall-iptables.md new file mode 100644 index 00000000000..35f3e70a0e5 --- /dev/null +++ b/content/manuals/engine/network/firewall-iptables.md @@ -0,0 +1,132 @@ +--- +title: Docker with iptables +weight: 10 +description: How Docker works with iptables +keywords: network, iptables, firewall +--- + +Docker creates iptables rules in the host's network namespace for bridge +networks. For bridge and other network types, iptables 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). +However, this is not recommended for most users as it will likely 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 + +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 +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 +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 +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 +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 +``` + +For more detailed information about iptables configuration and advanced usage, +refer to the [Netfilter.org HOWTO](https://www.netfilter.org/documentation/HOWTO/NAT-HOWTO.html). diff --git a/content/manuals/engine/network/firewall-nftables.md b/content/manuals/engine/network/firewall-nftables.md new file mode 100644 index 00000000000..8ed34ce45cb --- /dev/null +++ b/content/manuals/engine/network/firewall-nftables.md @@ -0,0 +1,272 @@ +--- +title: Docker with nftables +weight: 10 +description: How Docker works with nftables +keywords: network, nftables, firewall +--- + +> [!WARNING] +> +> Support for nftables is experimental, configuration options, behavior and +> implementation may all change in future releases. +> The rules for overlay networks have not yet been migrated from iptables. +> So, nftables cannot be enabled when the daemon has Swarm enabled. + +To use nftables instead of iptables, use Docker Engine option +`--firewall-backend=nftables` on its command line, or `"firewall-backend": "nftables"` +in its configuration file. You may also need to modify IP forwarding configuration +on the host, and migrate rules from the iptables `DOCKER-USER` chain, see +[migrating from iptables to nftables](#migrating-from-iptables-to-nftables). + +For bridge networks, Docker creates nftables rules in the host's network +namespace. For bridge and other network types, nftables rules for DNS are +also created in the container's network namespace. + +Creation of nftables rules can be disabled using daemon options `iptables` +and `ip6tables`. _These options apply to both iptables and nftables._ +See [Prevent Docker from manipulating firewall rules](packet-filtering-firewalls.md#prevent-docker-from-manipulating-firewall-rules). +However, this is not recommended for most users as it will likely break +container networking. + +## Docker's nftables tables + +For bridge networks, Docker creates two tables, `ip docker-bridges` and +`ip6 docker-bridges`. + +Each table contains a number of [base chains](https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Adding_base_chains), +and further chains are added for each bridge network. The moby project +has some [internal documentation](https://github.com/moby/moby/blob/master/integration/network/bridge/nftablesdoc/index.md) +describing its nftables, and how they depend on network and container +configuration. But, the tables and their rules are likely to change between +Docker Engine releases. + +Do not modify Docker's tables directly as the modifications are likely to +be lost, Docker expects to have full ownership of its tables. + +> [!NOTE] +> +> Because iptables has a fixed set of chains, equivalent to nftables base +> chains, all rules are included in those chains. The `DOCKER-USER` chain +> is supplied as a way to insert rules into the `filter` table's `FORWARD` +> chain, to run before Docker's rules. +> In Docker's nftables implementation, there is no `DOCKER-USER` chain. +> Instead, rules can be added in separate tables, with base chains that +> have the same types and hook points as Docker's base chains. If necessary, +> [base chain priority](https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_priority) +> can be used to tell nftables which order to call the chains in. +> 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. + +## Migrating from iptables to nftables + +If the Docker daemon has been running with the iptables firewall backend, +restarting it with the nftables backend will delete most of Docker's iptables +chains and rules, and create nftables rules instead. + +If IP forwarding is not enabled, Docker will report an error when creating +a bridge network that needs it. Because of the default bridge, if IPv4 +forwarding is disabled, the error will be reported during daemon startup. +See [IP forwarding](#ip-forwarding). + +If you have rules in the `DOCKER-USER` chain, see [Migrating +`DOCKER-USER`](#migrating-docker-user). + +You may need to manually update the iptables `FORWARD` policy if it has +been set to `DROP` by Docker with iptables, or as part of your host's +firewall configuration. See [FORWARD policy in iptables](#forward-policy-in-iptables). + +### IP forwarding + +IP forwarding on the Docker host enables Docker functionality including port +publishing, communication between bridge networks, and direct routing from +outside the host to containers in bridge networks. + +When running with iptables, depending on network and daemon configuration, +Docker may enable IPv4 and IPv6 forwarding on the host. + +With its nftables firewall backend enabled, Docker will not enable IP forwarding +itself. It will report an error if forwarding is needed, but not already enabled. +To disable Docker's check for IP forwarding, letting it start and create networks +when it determines that forwarding is disabled, use Daemon option `--ip-forward=false`, +or `"ip-forward": false` in its configuration file. + +> [!WARNING] +> +> When enabling IP forwarding, make sure you have firewall rules to block +> unwanted forwarding between non-Docker interfaces. + +> [!NOTE] +> +> If you stop Docker to migrate to nftables, Docker may have already enabled +> IP forwarding on your system. After a reboot, if no other service re-enables +> forwarding, Docker will fail to start. + +If Docker is in a VM that has a single network interface and no other +software running, there is probably no unwanted forwarding to block. +But, on a physical host with multiple network interfaces, forwarding +between those interfaces should probably be blocked unless the host +is acting as a router. + +To enable IP forwarding on the host, set the following sysctls: + +- `net.ipv4.ip_forward=1` +- `net.ipv6.conf.all.forwarding=1` + +If your host uses `systemd`, you may be able to use `systemd-sysctl`. For +example, by editing `/etc/sysctl.d/99-sysctl.conf`. + +If the host is running `firewalld`, you may be able to use it to block +unwanted forwarding. Docker's bridges are in a firewalld zone called +`docker`, it creates a forwarding policy called `docker-forwarding` that +accepts forwarding from `ANY` zone to the `docker` zone. + +As an example, to use nftables to block forwarding between interfaces `eth0` +and `eth1`, you could use: + +```console +table inet no-ext-forwarding { + chain no-ext-forwarding { + type filter hook forward priority filter; policy accept; + iifname "eth0" oifname "eth1" drop + iifname "eth1" oifname "eth0" drop + } +} +``` + +### FORWARD policy in iptables + +An iptables chain with `FORWARD` policy `DROP` will drop packets that have +been accepted by Docker's nftables rules, because the packet will be processed +by the iptables chains as well as Docker's nftables chains. + +Some features, including port publishing, will not work unless the `DROP` +policy is removed, or additional iptables rules are added to the iptables +`FORWARD` chain to accept Docker-related traffic. + +When Docker is using iptables, and it enables IP forwarding on the host, +it sets the default policy of the iptables `FORWARD` chain to `DROP`. So, +if you stop Docker to migrate to nftables, it may have set a `DROP` that +you need to remove. It will be removed anyway on reboot. + +To keep using rules in `DOCKER-USER` that rely on the chain having policy +`DROP`, you must add explicit `ACCEPT` rules for Docker related traffic. + +To check the current iptables `FORWARD` policy, use: + +```console +$ iptables -L FORWARD +Chain FORWARD (policy DROP) +target prot opt source destination +$ ip6tables -L FORWARD +Chain FORWARD (policy ACCEPT) +target prot opt source destination +``` + +To set the iptables policies to `ACCEPT` for IPv4 and IPv6: + +```console +$ iptables -P FORWARD ACCEPT +$ ip6tables -P FORWARD ACCEPT +``` + +### Migrating `DOCKER-USER` + +With firewall backend "iptables", rules added to the iptables `DOCKER-USER` +are processed before Docker's rules in the filter table's `FORWARD` chain. + +When starting the daemon with nftables after running with iptables, Docker +will not remove the jump from the `FORWARD` chain to `DOCKER-USER`. So, +rules created in `DOCKER-USER` will continue to run until the jump is +removed or the host is rebooted. + +When starting with nftables, the daemon will not add the jump. So, unless +there is an existing jump, rules in `DOCKER-USER` will be ignored. + +#### Migrating ACCEPT rules + +Some rules in the `DOCKER-USER` chain will continue to work. For example, if a +packet is dropped, it will be dropped before or after the nftables rules in +Docker's `filter-FORWARD` chain. But other rules, particularly `ACCEPT` rules +to override Docker's `DROP` rules, will not work. + +In nftables, an "accept" rule is not final. It terminates processing +for its base chain, but the accepted packet will still be processed by +other base chains, which may drop it. + +To override Docker's `drop` rule, you must use a firewall mark. Select a +mark not already in use on your host, and use Docker Engine option +`--bridge-accept-fwmark`. + +For example, `--bridge-accept-fwmark=1` tells the daemon to accept any +packet with an `fwmark` value of `1`. Optionally, you can supply a mask +to match specific bits in the mark, `--bridge-accept-fwmark=0x1/0x3`. + +Then, instead of accepting the packet in `DOCKER-USER`, add the firewall +mark you have chosen and Docker will not drop it. + +The firewall mark must be added before Docker's rules run. So if the mark +is added in a chain with type `filter` and hook `forward`, it must have +priority `filter - 1` or lower. + +#### Replacing `DOCKER-USER` with an nftables table + +Because nftables doesn't have pre-defined chains, to replace the `DOCKER-USER` +chain you can create your own table and add chains and rules to it. + +The `DOCKER-USER` chain has type `filter` and hook `forward`, so it can +only have rules in the filter forward chain. The base chains in your +table can have any `type` or `hook`. If your rules need to run before +Docker's rules, give the base chains a lower `priority` number than +Docker's chain. Or, a higher priority to make sure they run after Docker's +rules. + +Docker's base chains use the priority values defined at +[priority values](https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks#Priority_within_hook) + +#### Example: restricting external connections to containers + +By default, any remote host can connect to ports published to the Docker +host's external addresses. + +To allow only a specific IP or network to access the containers, create a +table with a base chain that has a drop rule. For example, the +following table drops packets from all IP addresses except `192.0.2.2`: + +```console +table ip my-table { + chain my-filter-forward { + type filter hook forward priority filter; policy accept; + iifname "ext_if" ip saddr != 192.0.2.2 counter drop + } +} +``` + +You will need to change `ext_if` to your host's external interface name. + +You could instead accept connections from a source subnet. The following +table only accepts access from the subnet `192.0.2.0/24`: + +```console +table ip my-table { + chain my-filter-forward { + type filter hook forward priority filter; policy accept; + iifname "ext_if" ip saddr != 192.0.2.0/24 counter drop + } +} +``` + +If you are running other services on the host that use IP forwarding +and need to be accessed by different external hosts, you will need more +specific filters. For example, to match the default prefix `br-` of +bridge devices belonging to Docker's user-defined bridge networks: + +```console +table ip my-table { + chain my-filter-forward { + type filter hook forward priority filter; policy accept; + iifname "ext_if" oifname "br-*" ip saddr != 192.0.2.0/24 counter drop + } +} +``` + +For more detailed information about nftables configuration and advanced usage, +refer to the [nftables wiki](https://wiki.nftables.org/wiki-nftables/index.php/Main_Page). diff --git a/content/manuals/engine/network/packet-filtering-firewalls.md b/content/manuals/engine/network/packet-filtering-firewalls.md index 72fa993b2e0..ec7ae893add 100644 --- a/content/manuals/engine/network/packet-filtering-firewalls.md +++ b/content/manuals/engine/network/packet-filtering-firewalls.md @@ -8,405 +8,81 @@ aliases: - /network/packet-filtering-firewalls/ --- -On Linux, Docker creates `iptables` and `ip6tables` rules to implement network -isolation, port publishing and filtering. +On Linux, Docker creates firewall rules to implement network +isolation, [port publishing](./port-publishing.md) and filtering. Because these rules are required for the correct functioning of Docker bridge networks, you should not modify the rules created by Docker. -But, if you are running Docker on a host exposed to the internet, you will -probably want to add iptables policies that prevent unauthorized access to -containers or other services running on your host. This page describes how -to achieve that, and the caveats you need to be aware of. +This page describes options that control Docker's firewall rules to +implement functionality including port publishing, and NAT/masquerading. > [!NOTE] > -> Docker creates `iptables` rules for bridge networks. +> Docker creates firewall rules for bridge networks. > -> No `iptables` rules are created for `ipvlan`, `macvlan` or `host` networking. +> No rules are created for `ipvlan`, `macvlan` or `host` networking. -## Docker and iptables chains +## Firewall backend -In the `filter` table, Docker sets the default policy to `DROP`, and creates the -following custom `iptables` chains: +By default, Docker Engine creates its firewall rules using iptables, +see [Docker with iptables](./firewall-iptables.md). It also has +support for nftables, see [Docker with nftables](./firewall-nftables.md). -* `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` - * 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-ISOLATION-STAGE-1` and `DOCKER-ISOLATION-STAGE-2` - * Rules to isolate Docker networks from each other. -* `DOCKER-INGRESS` - * Rules related to Swarm networking. +For bridge networks, iptables and nftables have the same functionality. -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. - -### 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. - -## Port publishing and mapping - -By default, for both IPv4 and IPv6, the daemon blocks access to ports that have not -been published. Published container ports are mapped to host IP addresses. -To do this, it uses iptables to perform Network Address Translation (NAT), -Port Address Translation (PAT), and masquerading. - -For example, `docker run -p 8080:80 [...]` creates a mapping -between port 8080 on any address on the Docker host, and the container's -port 80. Outgoing connections from the container will masquerade, using -the Docker host's IP address. - -### 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 -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 -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 -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). - -### Direct routing - -Port mapping ensures that published ports are accessible on the host's -network addresses, which are likely to be routable for any external -clients. No routes are normally set up in the host's network for container -addresses that exist within a host. - -But, particularly with IPv6 you may prefer to avoid using NAT and instead -arrange for external routing to container addresses ("direct routing"). - -To access containers on a bridge network from outside the Docker host, -you must first set up routing to the bridge network via an address on the -Docker host. This can be achieved using static routes, Border Gateway Protocol (BGP), -or any other means appropriate for your network. For example, within -a local layer 2 network, remote hosts can set up static routes to a container -network via the Docker daemon host's address on the local network. - -#### Direct routing to containers in bridge networks - -By default, remote hosts are not allowed direct access to container IP -addresses in Docker's Linux bridge networks. They can only access ports -published to host IP addresses. - -To allow direct access to any published port, on any container, in any -Linux bridge network, use daemon option `"allow-direct-routing": true` -in `/etc/docker/daemon.json` or the equivalent `--allow-direct-routing`. - -To allow direct routing from anywhere to containers in a specific bridge -network, see [Gateway modes](#gateway-modes). - -Or, to allow direct routing via specific host interfaces, to a specific -bridge network, use the following option when creating the network: -- `com.docker.network.bridge.trusted_host_interfaces` - -#### Example - -Create a network where published ports on container IP addresses can be -accessed directly from interfaces `vxlan.1` and `eth3`: - -```console -$ docker network create --subnet 192.0.2.0/24 --ip-range 192.0.2.0/29 -o com.docker.network.bridge.trusted_host_interfaces="vxlan.1:eth3" mynet -``` - -Run a container in that network, publishing its port 80 to port 8080 on -the host's loopback interface: - -```console -$ docker run -d --ip 192.0.2.100 -p 127.0.0.1:8080:80 nginx -``` - -The web server running on the container's port 80 can now be accessed -from the Docker host at `http://127.0.0.1:8080`, or directly at -`http://192.0.2.100:80`. If remote hosts on networks connected to -interfaces `vxlan.1` and `eth3` have a route to the `192.0.2.0/24` -network inside the Docker host, they can also access the web server -via `http://192.0.2.100:80`. - -#### Gateway modes - -The bridge network driver has the following options: -- `com.docker.network.bridge.gateway_mode_ipv6` -- `com.docker.network.bridge.gateway_mode_ipv4` - -Each of these can be set to one of the gateway modes: -- `nat` -- `nat-unprotected` -- `routed` -- `isolated` - -The default is `nat`, NAT and masquerading rules are set up for each -published container port. Packets leaving the host will use a host address. - -With mode `routed`, no NAT or masquerading rules are set up, but `iptables` -are still set up so that only published container ports are accessible. -Outgoing packets from the container will use the container's address, -not a host address. - -In `nat` mode, when a port is published to a specific host address, that -port is only accessible via the host interface with that address. So, -for example, publishing a port to an address on the loopback interface -means remote hosts cannot access it. - -However, using direct routing, published container ports are always -accessible from remote hosts, unless the Docker host's firewall has -additional restrictions. Hosts on the local layer-2 network can set up -direct routing without needing any additional network configuration. -Hosts outside the local network can only use direct routing to the -container if the network's routers are configured to enable it. - -In `nat-unprotected` mode, unpublished container ports are also -accessible using direct routing, no port filtering rules are set up. -This mode is included for compatibility with legacy default behaviour. - -The gateway mode also affects communication between containers that -are connected to different Docker networks on the same host. -- In `nat` and `nat-unprotected` modes, containers in other bridge - networks can only access published ports via the host addresses they - are published to. Direct routing from other networks is not allowed. -- In `routed` mode containers in other networks can use direct - routing to access ports, without going via a host address. - -In `routed` mode, a host port in a `-p` or `--publish` port mapping is -not used, and the host address is only used to decide whether to apply -the mapping to IPv4 or IPv6. So, when a mapping only applies to `routed` -mode, only addresses `0.0.0.0` or `::` should be used, and a host port -should not be given. If a specific address or port is given, it will -have no effect on the published port and a warning message will be -logged. - -Mode `isolated` can only be used when the network is also created with -CLI flag `--internal`, or equivalent. An address is normally assigned to the -bridge device in an `internal` network. So, processes on the docker host can -access the network, and containers in the network can access host services -listening on that bridge address (including services listening on "any" host -address, `0.0.0.0` or `::`). No address is assigned to the bridge when the -network is created with gateway mode `isolated`. - -#### Example - -Create a network suitable for direct routing for IPv6, with NAT enabled -for IPv4: -```console -$ docker network create --ipv6 --subnet 2001:db8::/64 -o com.docker.network.bridge.gateway_mode_ipv6=routed mynet -``` - -Create a container with a published port: -```console -$ docker run --network=mynet -p 8080:80 myimage -``` - -Then: -- Only container port 80 will be open, for IPv4 and IPv6. -- For IPv6, using `routed` mode, port 80 will be open on the container's IP - address. Port 8080 will not be opened on the host's IP addresses, and - outgoing packets will use the container's IP address. -- For IPv4, using the default `nat` mode, the container's port 80 will be - accessible via port 8080 on the host's IP addresses, as well as directly - from within the Docker host. But, container port 80 cannot be accessed - directly from outside the host. - Connections originating from the container will masquerade, using the - host's IP address. - -In `docker inspect`, this port mapping will be shown as follows. Note that -there is no `HostPort` for IPv6, because it is using `routed` mode: -```console -$ docker container inspect --format "{{json .NetworkSettings.Ports}}" -{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":""}]} -``` - -Alternatively, to make the mapping IPv6-only, disabling IPv4 access to the -container's port 80, use the unspecified IPv6 address `[::]` and do not -include a host port number: -```console -$ docker run --network mynet -p '[::]::80' -``` - -### Setting the default bind address for containers - -By default, when a container's ports are mapped without any specific host -address, the Docker daemon binds published container ports to all host -addresses (`0.0.0.0` and `[::]`). - -For example, the following command publishes port 8080 to all network -interfaces on the host, on both IPv4 and IPv6 addresses, potentially -making them available to the outside world. - -```console -docker run -p 8080:80 nginx -``` - -You can change the default binding address for published container ports so that -they're only accessible to the Docker host by default. To do that, you can -configure the daemon to use the loopback address (`127.0.0.1`) instead. - -> [!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) - -To configure this setting for user-defined bridge networks, use -the `com.docker.network.bridge.host_binding_ipv4` -[driver option](./drivers/bridge.md#options) when you create the network. - -```console -$ docker network create mybridge \ - -o "com.docker.network.bridge.host_binding_ipv4=127.0.0.1" -``` - -> [!NOTE] -> -> - Setting the default binding address to `::` means port bindings with no host -> address specified will work for any IPv6 address on the host. But, `0.0.0.0` -> means any IPv4 or IPv6 address. -> - Changing the default bind address doesn't have any effect on Swarm services. -> Swarm services are always exposed on the `0.0.0.0` network interface. - -#### Default bridge - -To set the default binding for the default bridge network, configure the `"ip"` -key in the `daemon.json` configuration file: - -```json -{ - "ip": "127.0.0.1" -} -``` - -This changes the default binding address to `127.0.0.1` for published container -ports on the default bridge network. -Restart the daemon for this change to take effect. -Alternatively, you can use the `dockerd --ip` flag when starting the daemon. +Docker Engine option `firewall-backend` can be used to select whether +iptables or nftables is used. See +[daemon configuration](https://docs.docker.com/reference/cli/dockerd/). ## Docker on a router On Linux, Docker needs "IP Forwarding" enabled on the host. So, it enables the `sysctl` settings `net.ipv4.ip_forward` and `net.ipv6.conf.all.forwarding` it they are not already enabled when it starts. When it does that, it also -sets the policy of the iptables `FORWARD` chain to `DROP`. +configures the firewall to drop forwarded packets unless they are explicitly +accepted. -If Docker sets the policy for the `FORWARD` chain to `DROP`. This will prevent -your Docker host from acting as a router, it is the recommended setting when -IP Forwarding is enabled. +When Docker sets the default forwarding policy to "drop", it will prevent +your Docker host from acting as a router. This is the recommended setting when +IP Forwarding is enabled, unless router functionality is required. -To stop Docker from setting the `FORWARD` chain's policy to `DROP`, include +To stop Docker from setting the forwarding policy to "drop", include `"ip-forward-no-drop": true` in `/etc/docker/daemon.json`, or add option `--ip-forward-no-drop` to the `dockerd` command line. -Alternatively, you may add `ACCEPT` rules to the `DOCKER-USER` chain for the -packets you want to forward. For example: - -```console -$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT -``` - -> [!WARNING] -> -> In releases older than 28.0.0, Docker always set the default policy of the -> IPv6 `FORWARD` chain to `DROP`. In release 28.0.0 and newer, it will only -> set that policy if it enables IPv6 forwarding itself. This has always been -> the behaviour for IPv4 forwarding. +> [!NOTE] > -> If IPv6 forwarding is enabled on your host before Docker starts, check your -> host's configuration to make sure it is still secure. +> With the experimental nftables backend, Docker does not enable IP forwarding +> itself, and it will not create a default "drop" nftables policy. See +> [Migrating from iptables to nftables](./firewall-nftables.md#migrating-from-iptables-to-nftables). -## Prevent Docker from manipulating iptables +## Prevent Docker from manipulating firewall rules -It is possible to set the `iptables` or `ip6tables` keys to `false` in -[daemon configuration](https://docs.docker.com/reference/cli/dockerd/), but -this option is not appropriate for most users. It is likely to break +Setting the `iptables` or `ip6tables` keys to `false` in +[daemon configuration](https://docs.docker.com/reference/cli/dockerd/), will +prevent Docker from creating most of its `iptables` or `nftables` rules. But, +this option is not appropriate for most users, it is likely to break container networking for the Docker Engine. -All ports of all containers will be accessible from the network, and none -will be mapped from Docker host IP addresses. +For example, with Docker's firewalling disabled and no replacement +rules, containers in bridge networks will not be able to access +internet hosts by masquerading, but all of their ports will be accessible +to hosts on the local network. -It is not possible to completely prevent Docker from creating `iptables` +It is not possible to completely prevent Docker from creating firewall rules, and creating rules after-the-fact is extremely involved and beyond the scope of these instructions. ## Integration with firewalld -If you are running Docker with the `iptables` option set to `true`, and -[firewalld](https://firewalld.org) is enabled on your system, Docker -automatically creates a `firewalld` zone called `docker`, with target `ACCEPT`. +If you are running Docker with the `iptables` or `ip6tables` options set to +`true`, and [firewalld](https://firewalld.org) is enabled on your system, in +addition to its usual iptables or nftables rules, Docker creates a `firewalld` +zone called `docker`, with target `ACCEPT`. -All network interfaces created by Docker (for example, `docker0`) are inserted -into the `docker` zone. +All bridge network interfaces created by Docker (for example, `docker0`) are +inserted into the `docker` zone. Docker also creates a forwarding policy called `docker-forwarding` that allows forwarding from `ANY` zone to the `docker` zone. @@ -415,8 +91,8 @@ forwarding from `ANY` zone to the `docker` zone. [Uncomplicated Firewall](https://launchpad.net/ufw) (ufw) is a frontend that ships with Debian and Ubuntu, -and it lets you manage firewall rules. Docker and ufw use iptables in ways -that make them incompatible with each other. +and it lets you manage firewall rules. Docker and ufw use firewall rules in +ways that make them incompatible with each other. When you publish a container's ports using Docker, traffic to and from that container gets diverted before it goes through the ufw firewall settings. diff --git a/content/manuals/engine/network/port-publishing.md b/content/manuals/engine/network/port-publishing.md new file mode 100644 index 00000000000..b0721b15750 --- /dev/null +++ b/content/manuals/engine/network/port-publishing.md @@ -0,0 +1,293 @@ +--- +title: Port publishing and mapping +weight: 10 +description: Accessing container ports +keywords: network, iptables, firewall +--- + +By default, for both IPv4 and IPv6, the daemon blocks access to ports that have not +been published. Published container ports are mapped to host IP addresses. +To do this, it uses firewall rules to perform Network Address Translation (NAT), +Port Address Translation (PAT), and masquerading. + +For example, `docker run -p 8080:80 [...]` creates a mapping +between port 8080 on any address on the Docker host, and the container's +port 80. Outgoing connections from the container will masquerade, using +the Docker host's IP address. + +## Publishing ports + +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. + +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) + +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). + +## Direct routing + +Port mapping ensures that published ports are accessible on the host's +network addresses, which are likely to be routable for any external +clients. No routes are normally set up in the host's network for container +addresses that exist within a host. + +But, particularly with IPv6 you may prefer to avoid using NAT and instead +arrange for external routing to container addresses ("direct routing"). + +To access containers on a bridge network from outside the Docker host, +you must first set up routing to the bridge network via an address on the +Docker host. This can be achieved using static routes, Border Gateway Protocol (BGP), +or any other means appropriate for your network. For example, within +a local layer 2 network, remote hosts can set up static routes to a container +network via the Docker daemon host's address on the local network. + +### Direct routing to containers in bridge networks + +By default, remote hosts are not allowed direct access to container IP +addresses in Docker's Linux bridge networks. They can only access ports +published to host IP addresses. + +To allow direct access to any published port, on any container, in any +Linux bridge network, use daemon option `"allow-direct-routing": true` +in `/etc/docker/daemon.json` or the equivalent `--allow-direct-routing`. + +To allow direct routing from anywhere to containers in a specific bridge +network, see [Gateway modes](#gateway-modes). + +Or, to allow direct routing via specific host interfaces, to a specific +bridge network, use the following option when creating the network: +- `com.docker.network.bridge.trusted_host_interfaces` + +#### Example + +Create a network where published ports on container IP addresses can be +accessed directly from interfaces `vxlan.1` and `eth3`: + +```console +$ docker network create --subnet 192.0.2.0/24 --ip-range 192.0.2.0/29 -o com.docker.network.bridge.trusted_host_interfaces="vxlan.1:eth3" mynet +``` + +Run a container in that network, publishing its port 80 to port 8080 on +the host's loopback interface: + +```console +$ docker run -d --ip 192.0.2.100 -p 127.0.0.1:8080:80 nginx +``` + +The web server running on the container's port 80 can now be accessed +from the Docker host at `http://127.0.0.1:8080`, or directly at +`http://192.0.2.100:80`. If remote hosts on networks connected to +interfaces `vxlan.1` and `eth3` have a route to the `192.0.2.0/24` +network inside the Docker host, they can also access the web server +via `http://192.0.2.100:80`. + +## Gateway modes + +The bridge network driver has the following options: +- `com.docker.network.bridge.gateway_mode_ipv6` +- `com.docker.network.bridge.gateway_mode_ipv4` + +Each of these can be set to one of the gateway modes: +- `nat` +- `nat-unprotected` +- `routed` +- `isolated` + +The default is `nat`, NAT and masquerading rules are set up for each +published container port. Packets leaving the host will use a host address. + +With mode `routed`, no NAT or masquerading rules are set up, but firewall +rules are still set up so that only published container ports are accessible. +Outgoing packets from the container will use the container's address, +not a host address. + +In `nat` mode, when a port is published to a specific host address, that +port is only accessible via the host interface with that address. So, +for example, publishing a port to an address on the loopback interface +means remote hosts cannot access it. + +However, using direct routing, published container ports are always +accessible from remote hosts, unless the Docker host's firewall has +additional restrictions. Hosts on the local layer-2 network can set up +direct routing without needing any additional network configuration. +Hosts outside the local network can only use direct routing to the +container if the network's routers are configured to enable it. + +In `nat-unprotected` mode, unpublished container ports are also +accessible using direct routing, no port filtering rules are set up. +This mode is included for compatibility with legacy default behaviour. + +The gateway mode also affects communication between containers that +are connected to different Docker networks on the same host. +- In `nat` and `nat-unprotected` modes, containers in other bridge + networks can only access published ports via the host addresses they + are published to. Direct routing from other networks is not allowed. +- In `routed` mode containers in other networks can use direct + routing to access ports, without going via a host address. + +In `routed` mode, a host port in a `-p` or `--publish` port mapping is +not used, and the host address is only used to decide whether to apply +the mapping to IPv4 or IPv6. So, when a mapping only applies to `routed` +mode, only addresses `0.0.0.0` or `::` should be used, and a host port +should not be given. If a specific address or port is given, it will +have no effect on the published port and a warning message will be +logged. + +Mode `isolated` can only be used when the network is also created with +CLI flag `--internal`, or equivalent. An address is normally assigned to the +bridge device in an `internal` network. So, processes on the Docker host can +access the network, and containers in the network can access host services +listening on that bridge address (including services listening on "any" host +address, `0.0.0.0` or `::`). No address is assigned to the bridge when the +network is created with gateway mode `isolated`. + +### Example + +Create a network suitable for direct routing for IPv6, with NAT enabled +for IPv4: +```console +$ docker network create --ipv6 --subnet 2001:db8::/64 -o com.docker.network.bridge.gateway_mode_ipv6=routed mynet +``` + +Create a container with a published port: +```console +$ docker run --network=mynet -p 8080:80 myimage +``` + +Then: +- Only container port 80 will be open, for IPv4 and IPv6. +- For IPv6, using `routed` mode, port 80 will be open on the container's IP + address. Port 8080 will not be opened on the host's IP addresses, and + outgoing packets will use the container's IP address. +- For IPv4, using the default `nat` mode, the container's port 80 will be + accessible via port 8080 on the host's IP addresses, as well as directly + from within the Docker host. But, container port 80 cannot be accessed + directly from outside the host. + Connections originating from the container will masquerade, using the + host's IP address. + +In `docker inspect`, this port mapping will be shown as follows. Note that +there is no `HostPort` for IPv6, because it is using `routed` mode: +```console +$ docker container inspect --format "{{json .NetworkSettings.Ports}}" +{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":""}]} +``` + +Alternatively, to make the mapping IPv6-only, disabling IPv4 access to the +container's port 80, use the unspecified IPv6 address `[::]` and do not +include a host port number: +```console +$ docker run --network mynet -p '[::]::80' +``` + +## Setting the default bind address for containers + +By default, when a container's ports are mapped without any specific host +address, the Docker daemon publishes ports to all host addresses +(`0.0.0.0` and `[::]`). + +For example, the following command publishes port 8080 to all network +interfaces on the host, on both IPv4 and IPv6 addresses, potentially +making them available to the outside world. + +```console +docker run -p 8080:80 nginx +``` + +You can change the default binding address for published container ports so that +they're only accessible to the Docker host by default. To do that, you can +configure the daemon to use the loopback address (`127.0.0.1`) instead. + +> [!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) + +To configure this setting for user-defined bridge networks, use +the `com.docker.network.bridge.host_binding_ipv4` +[driver option](./drivers/bridge.md#default-host-binding-address) when you +create the network. Despite the option name, it is possible to specify an +IPv6 address. + +```console +$ docker network create mybridge \ + -o "com.docker.network.bridge.host_binding_ipv4=127.0.0.1" +``` + +Or, to set the default binding address for containers in all user-defined +bridge networks, use daemon configuration option `default-network-opts`. +For example: + +```json +{ + "default-network-opts": { + "bridge": { + "com.docker.network.bridge.host_binding_ipv4": "127.0.0.1" + } + } +} +``` + +> [!NOTE] +> +> - Setting the default binding address to `::` means port bindings with no host + > address specified will work for any IPv6 address on the host. But, `0.0.0.0` + > means any IPv4 or IPv6 address. +> - Changing the default bind address doesn't have any effect on Swarm services. + > Swarm services are always exposed on the `0.0.0.0` network interface. + +### Default bridge + +To set the default binding for the default bridge network, configure the `"ip"` +key in the `daemon.json` configuration file: + +```json +{ + "ip": "127.0.0.1" +} +``` + +This changes the default binding address to `127.0.0.1` for published container +ports on the default bridge network. +Restart the daemon for this change to take effect. +Alternatively, you can use the `dockerd --ip` flag when starting the daemon.