Commit Graph

1021 Commits

Author SHA1 Message Date
Albin Kerouanton
ec8a5b0f4c libnet/d/bridge: move iptRule to iptables pkg
This moves the iptRule struct from the bridge package to the iptables
pkg.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-12-18 11:01:27 +01:00
Albin Kerouanton
0d53725a7f Merge pull request #49107 from akerouanton/ipt-split-ProgramChain
libnet/iptables: split ProgramChain and move to bridge driver
2024-12-17 19:15:52 +01:00
Albin Kerouanton
820dea0d2b libnet/d/bridge: hardcode chain names
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-12-17 12:31:35 +01:00
Albin Kerouanton
bd30a51ea7 libnet/iptables: split ProgramChain and move to bridge driver
The `ProgramChain` method was called exclusively by the bridge driver
to insert totally unrelated ipt rules in two different table-chains.

Break down this method into two functions, and move them into the bridge
pkg.

The new function `addNATJumpRules` inserts rules that aren't related to
any specific network, and depends solely on the driver config. Call it
during driver configuration instead of during network setup.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-12-17 12:31:34 +01:00
Rob Murray
df3c78d061 Combine outgoing and ICC iptables rules
Rather than:

  ACCEPT -i <bridge> -o <bridge>   // icc=true
  ACCEPT -i <bridge> ! -o <bridge> // outgoing

Do:

  ACCEPT -i <bridge>  // icc=true and outgoing

For ICC=false, continue to do:

  DROP -i <bridge> -o <bridge>
  ACCEPT -i <bridge> ! -o <bridge>

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-17 10:38:09 +00:00
Rob Murray
0f259dd76d Rename setupIPTablesInternal -> setupNonInternalNetworkRules
setupIPTablesInternal is/was called from setupIPTables to set
up some rules for a network ... "internal" behaviour of the
outer function.

Then commit 7b64b1c2 added support for "--internal" bridge
networks, calling setupInternalNetworkRules from setupIPTables
instead of setupIPTablesInternal.

So, setupIPTablesInternal is the function that deals with
everything except "--internal" networks ... rename it.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-17 10:38:09 +00:00
Rob Murray
556b8eed16 Tidy setupIPTablesInternal
- Move variable declarations closer to where they're used.
- Put comments in the blocks they apply to.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-17 10:38:09 +00:00
Rob Murray
29e20fc904 Add static filter-FORWARD rules during bridge driver init
Rules no longer need to be rearranged when creating a network.

Per-network rules are always appended to the FORWARD chain so,
after adding them, there's no need to delete the per-driver
rules to re-insert them at the top of the chain.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-17 09:14:25 +00:00
Sebastiaan van Stijn
01a55860c6 libnetwork/drivers/bridge: setupIPChains: fix defer checking wrong err
The output variable was renamed in 0503cf2510,
but that commit failed to change this defer, which was now checking the
wrong error.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-12-16 16:46:31 +01:00
Albin Kerouanton
678ef1a267 libnet/d/bridge: unconditionally error out if LinkSetMTU fails
Since 89470a7 we ignore errors returned by `LinkSetMTU` when the MTU is
greater than 1500 but lower than 65535 to let CentOS/RHEL 7 users set
an MTU in that range (despite their kernel rejecting that value).

We dropped support for those distros, so we can now remove this code
and unconditionally error out if `LinkSetMTU` returns an error.

Commit 89470a7 introduced two unit tests - these are kept, and both now
have a proper GoDoc describing what they're testing.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-12-14 11:22:40 +01:00
Sebastiaan van Stijn
68dbb8b385 Merge pull request #49022 from robmry/ignore_kernel_ll_for_fcidrv6
Ignore kernel-assigned LL addrs when selecting "bip6"
2024-12-06 21:31:29 +01:00
Rob Murray
f2e1f52a04 Try to load kernel module ip6_tables, when necessary.
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-06 10:27:24 +00:00
Rob Murray
4740820716 Use ioctl to try to trigger kernel module loads
An ioctl() call to get the "interface index" for a kernel module triggers
the kernel to try to load the module, if the process is running with
CAP_SYS_MODULE. This tends to be more reliable than "modprobe" for
docker-in-docker.

If the ioctl() method fails, fall back to trying "modprobe".

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-06 10:27:24 +00:00
Rob Murray
8fee8a759f Remove test env var DOCKER_TEST_CREATE_DEFAULT_BRIDGE
Env var DOCKER_TEST_CREATE_DEFAULT_BRIDGE could be set to
override the name of the default bridge - without the bridge
being user-managed (unlike the '--bridge' daemon option).

It was needed by tests looking at docker0 behaviour, using
their own instance of the daemon, without breaking the
docker0 instance belonging to CI's daemon.

Now, those tests run in their own netns using the name docker0.

So, remove the unused env var.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-12-04 12:02:03 +00:00
Rob Murray
0aba67203a Implement gateway mode "nat-unprotected"
Same as "nat" mode, there's masquerading and port mapping from the
host - but no port/protocol filtering for direct access to the
container's address from remote hosts.

This is the old default behaviour for IPv4 when the filter-FORWARD
chain's default policy was "ACCEPT" (the daemon would only set it
to "DROP" when it set sysctl "ip_forward" itself, but it didn't set
up DROP rules for unpublished ports).

Now, port filtering doesn't depend on the filter-FORWARD policy. So,
this mode is added as a way to restore the old/surprising/insecure
behaviour for anyone who's depending on it. Networks will need to
be re-created with this new gateway mode.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-28 19:33:37 +00:00
Rob Murray
07dff11a7e Wrap errors rather than using err.Error()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-28 19:03:10 +00:00
Sebastiaan van Stijn
a8e5a19706 libnetwork/drivers/bridge: gofumpt code
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-28 10:05:07 +01:00
Shreenidhi Shedi
264c15bfc4 Fix br_netfilter module loading logic
Checking for `/proc/sys/net/bridge` directory alone is not enough to
decide if bridge, br_netfilter module to be loaded.
Check for specific file for br_netfilter module and then
do modprobe if the file is not found under `/proc/sys/net/bridge`

Loading br_netfilter implicitly loads bridge module.

fixes: #48948

Signed-off-by: Shreenidhi Shedi <shreenidhi.shedi@broadcom.com>
2024-11-27 18:20:33 +05:30
Sebastiaan van Stijn
54fff36d95 Merge pull request #48906 from robmry/network_generic_opts
Disallow "network generic data" with type options.Generic
2024-11-25 15:41:00 +01:00
Aleksa Sarai
1b470d15d8 tests: migrate away from assert.Assert(v == nil)
If a values is non-nil when we don't expect it, it would be quite
helpful to get an error message explaining what happened.

  find . -type f -name "*_test.go" | \
    xargs gofmt -w -r "assert.Assert(t, a == nil) -> assert.Assert(t, is.Nil(a))"
  find . -type f -name "*_test.go" | \
    xargs gofmt -w -r "assert.Check(t, a == nil) -> assert.Check(t, is.Nil(a))"

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
2024-11-22 23:59:20 +11:00
Rob Murray
b656cffe4c Disallow "network generic data" with type options.Generic
Field 'generic' in 'libnetwork.Network' is used to store driver options,
it has type 'options.Generic', which is 'map[string]any'.

In that map, there may be a key 'netlabel.GenericData' holding options
known as "network generic options", used for options like:
  -o com.docker.network.bridge.name=br-foo

The value type for key 'netlabel.GenericData' is always 'map[string]string'
when created via an API request. But, some unit tests use type
'options.Generic'.

That works because the bridge, ipvlan and macvlan drivers look for type
'options.Generic' as well as 'map[string]string'. If they find
'options.Generic', Go reflection is used to map keys to fields of the
config struct with the expectation that the value has the same type as
that field. But, that's only used in unit tests (so the tests aren't
testing the same code path as the API would use). The 'options.Generic'
form of the bridge name option is:
  "BridgeName": "br-foo"
(Because "BridgeName" is the name of the field in the bridge driver's
network config struct.)

The libnetwork code expects "network generic options" to have type
'map[string]string', and makes no provision for 'options.Generic'. So,
for example, function Network.DriverOptions will panic if called when
'Network.generic[netlabel.GenericData]' has type 'options.Generic'.

The type of 'Network.generic[netlabel.GenericData]' can't be statically
checked, because it's just a field in a 'map[string]any'.

So - remove the driver code that converts "network generic options"
from type 'options.Generic', as it's only used in tests and just makes
things more confusing.

This should reduce the chances of things appearing to work when the
type is wrong, and converting unit tests to use 'map[string]string'
means they're testing the right thing.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 20:13:39 +00:00
Rob Murray
0546d9084f Routed networks accept traffic from anywhere.
Create ipsets containing the subnet of each non-internal bridge network.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:42 +00:00
Rob Murray
10338053f0 Don't add default-RETURN rules to DOCKER-ISOLATION chains
The default for a user-defined chain is RETURN anyway.

This opens up the possibilty of sorting rules into two groups
by using insert or append, without having to deal with appending
after the unconditional RETURN.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:42 +00:00
Rob Murray
939bc14616 Tidy bridgeNetwork.isolateNetwork
IPv4 before IPv6, with consistent error paths.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:42 +00:00
Rob Murray
255fff4acd Propagate error from INC rule setup
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:42 +00:00
Rob Murray
d3b7d84b23 Simplify setINC()
After an error, there's no need for it to roll back rules
it's created, the caller already does that.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:41 +00:00
Rob Murray
1a607cf257 Rename gwMode.natDisabled() to gwMode.routed()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-19 15:28:41 +00:00
Albin Kerouanton
4c19680fc5 Merge pull request #48854 from robmry/12632_noproxy_masquerade
Only masquerade access to own published ports for userland-proxy=false
2024-11-15 09:38:32 +01:00
Sebastiaan van Stijn
89add8c64f libnetwork/drivers/overlay: remove redundant capturing of loop vars (copyloopvar)
libnetwork/drivers/overlay/encryption.go:682:3: The copy of the 'for' variable "sp" can be deleted (Go 1.22+) (copyloopvar)
            sp := sp
            ^
    libnetwork/drivers/overlay/encryption.go:692:3: The copy of the 'for' variable "sa" can be deleted (Go 1.22+) (copyloopvar)
            sa := sa
            ^
    libnetwork/drivers/overlay/peerdb.go:134:3: The copy of the 'for' variable "pEntry" can be deleted (Go 1.22+) (copyloopvar)
            pEntry := pEntry
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-12 14:02:12 +01:00
Sebastiaan van Stijn
b01a37a6b7 libnetwork/drivers/bridge: remove redundant capturing of loop vars (copyloopvar)
libnetwork/drivers/bridge/bridge_linux_test.go:1079:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^
    libnetwork/drivers/bridge/port_mapping_linux_test.go:763:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-12 14:02:09 +01:00
Sebastiaan van Stijn
b453aa65fa update go:build tags to use go1.22
commit a0807e7cfe configured golangci-lint
to use go1.23 semantics, which alowed linters like `copyloopvar` to lint
using thee correct semantics.

go1.22 now creates a copy of variables when assigned in a loop; make sure we
don't have files that may downgrade semantics to go1.21 in case that also means
disabling that feature; https://go.dev/ref/spec#Go_1.22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-12 14:02:09 +01:00
Rob Murray
bf251c33d0 Only masquerade access to own published ports for userland-proxy=false
When a container sends a packet to one of its own published ports on the
host, it's normally picked up by the userland proxy and sent back.

When the userland proxy is disabled, a masquerade rule is needed in
order for responses to the container to have the host's source address.

The masquerade rule matches the container's address as source and dest,
and the published port as the dest. It's only used for the no-proxy
case.

So, when the userland proxy is enabled, don't create the masquerade
rule.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-12 12:37:25 +00:00
Rob Murray
3cadadb4eb Add daemon option --ip-forward-no-drop
The daemon no longer depends on the iptables/ip6tables filter-FORWARD
chain's policy being DROP in order to implement its port filtering
rules.

However, if the daemon enables IP forwarding in the host's system
config, by default it will set the policy to DROP to avoid potential
security issues for other applications/networks.

If docker does need to enable IP forwarding, but other applications
on the host require filter-FORWARD's policies to be ACCEPT, this
option can be used to tell the daemon to leave the policy unchanged.
(Equivalent to enabling IP forwarding before starting the daemon,
but without needing to do that.)

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-11 12:12:57 +00:00
Rob Murray
5823b05c97 Modify IP forwarding and filter-FORWARD DROP setup.
Before this change, for IPv4:
- sysctl net.ipv4.ip_forward was enabled during bridge driver
  initialisation, if:
  - not already enabled
  - ip-forward=true, and
  - iptables=true.
- the filter-FORWARD chain's policy was set to DROP, if the daemon
  updated the sysctl.
- if setting the policy failed, the sysctl change was reverted.

But, for IPv6:
- sysctls net.ipv6.conf.[default|all].forwarding were both enabled
  when creating the first IPv6-enabled network, if:
  - they weren't already enabled,
  - ip-forward=true, and
  - ip6tables=true.
- the filter-FORWARD chain's policy was set to DROP when creating
  an IPv4 enabled bridge network (inc. the default bridge), if:
  - ip6tables=true.
  - (this happened whether or not the daemon would ever enable
    IPv6 forwarding, or even create an IPv6 network.)

The bridge driver no longer needs the default policy to be DROP to
implement its own port-filtering rules. But, enabling IP forwarding
without setting the filter-FORWARD policy to DROP would potentially
be a security risk.

This change aligns IPv4 and IPv6 behaviours:
- only try to set the sysctls when creating a bridge network that
  needs them (for IPv4, that's still during daemon init because
  the default bridge is IPv4 enabled).
- only check/set the filter-FORWARD policy after updating sysctls.
- if the filter-FORWARD policy can't be set, treat it as an error
  and revert sysctl changes.

We enabled ip6tables by default in 27.0. Setting the filter-FORWARD
policy to DROP even when no IPv6 enabled network was created
caused issues for some users. In particular, those running with
iptables=false suddenly got the IPv6 DROP policy enabled (which
broke unrelated services on the host). This change solves that by
only setting the policy when necessary.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-11 12:08:43 +00:00
Rob Murray
a4d74c6383 Move setupIPv6Forwarding() to setup_ip_forwarding.go
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-11 12:08:43 +00:00
Rob Murray
a6e5f7f86b Structured logs in sbJoin/sbLeave and funcs they call
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-07 17:17:21 +00:00
Rob Murray
869f7996fc Option to disable implicit mapping IPv6 host to IPv4.
When the host has IPv6 support but the container doesn't, the
default bind address is 0.0.0.0, and a port mapping does not
include a host address - the default behaviour (of the bridge
driver) is to run a docker-proxy to map from any IPv6 host
address to the IPv4 address of a container's gateway endpoint.

Driver option com.docker.network.driver.private.no_proxy_6to4
can now be used to disable that behaviour when configuring
a Sandbox's external connectivity.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-07 17:17:21 +00:00
Rob Murray
4ac7f14e99 Add EnableIPv4 to the bridge driver
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-07 17:17:21 +00:00
Sebastiaan van Stijn
88c178328f libnetwork/drivers/bridge: ignore G103 (gosec)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 21:12:45 +01:00
Rob Murray
ec3dde7001 Only allocate IPv6 addresses if IPv6 is enabled.
When a container doesn't support IPv6 and it's joined to an IPv6
network, don't allocate an IPv6 address for it.

Update the DNS resolver to understand that it can have an 'ipv6miss'
(meaning an IPv4 address exists, but no IPv6) when a network is
IPv6 enabled.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-05 10:01:52 +00:00
Rob Murray
4c553defce Separate Sandbox/Endpoint construction
If config for legacy links needs to be added to a libnetwork.Sandbox,
add it when constructing the Endpoint that needs it - removing the
constraint on ordering of Endpoint construction, and the dependency
between Endpoint and Sandbox construction.

So, now a Sandbox can be constructed in one place, before the first
Endpoint.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-05 10:00:10 +00:00
Rob Murray
c9fdeaf70e Explicitly DROP packets for ports/protos that aren't mapped.
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-10-22 14:38:13 +01:00
Rob Murray
6634fc5136 Allow ICMP in routed mode
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-10-22 14:38:13 +01:00
Albin Kerouanton
6649a32cb3 Merge pull request #48676 from robmry/48664_br_netfilter_noproxy
Enable bridge netfiltering if userland-proxy=false
2024-10-17 15:31:45 +02:00
Rob Murray
091b09f26e Merge pull request #48575 from robmry/port_mapping_validation
Log rather than error if port mapping is overspecified
2024-10-17 12:39:14 +01:00
Rob Murray
0548fe251c Enable bridge netfiltering if userland-proxy=false
In release 27.0, ip6tables was enabled by default. That caused a
problem on some hosts where iptables was explicitly disabled and
loading the br_netfilter module (which loads with its nf-call-iptables
settings enabled) caused user-defined iptables rules to block traffic
on bridges, breaking inter-container communication.

In 27.3.0, commit 5c499fc4b2 delayed
loading of the br_netfilter module until it was needed. The load
now happens in the function that sets bridge-nf-call-ip[6]tables when
needed. It was only called for icc=false networks.

However, br_netfilter is also needed when userland-proxy=false.
Without it, packets addressed to a host-mapped port for a container
on the same network are not DNAT'd properly (responses have the server
container's address instead of the host's).

That means, in all releases including 26.x, if br_netfilter was loaded
before the daemon started - and the OS/user/other-application had
disabled bridge-nf-call-ip[6]tables, it would not be enabled by the
daemon. So, ICC would fail for host-mapped ports with the userland-proxy
disabled.

The change in 27.3.0 made this worse - previously, loading br_netfilter
whenever iptables/ip6tables was enabled meant that bridge-netfiltering
got enabled, even though the daemon didn't check it was enabled.

So... check that br_netfilter is loaded, with bridge-nf-call-ip[6]tables
enabled, if userland-proxy=false.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-10-17 12:33:49 +01:00
Sebastiaan van Stijn
87390ae849 Merge pull request #48571 from akerouanton/proxy-udp-with-correct-saddr
cmd/docker-proxy: UDP: reply to clients with original daddr
2024-10-15 20:51:37 +02:00
Albin Kerouanton
6c6174b371 cmd/docker-proxy: UDP: reply to clients with original daddr
When a UDP server is running on a multihomed server, as is the case with
pretty much _all_ Docker hosts (eg. eth0 + docker0), the kernel has to
choose which source address is used when replying to a UDP client. But
that process is based on heuristics and is fallible.

If the address picked doesn't match the original destination address
used by the client, it'll drop the datagram and return an ICMP Port
Unreachable.

To prevent that, we need to:

- `setsockopt(IP_PKTINFO)` on proxy's sockets.
- Extract the original destination address from an ancillary message
  every time a new 'UDP connection' is 'established' (ie. every time we
  insert a new entry into the UDP conntrack table).
- And finally, pass a control message containing the desired source
  address to the kernel, every time we send a response back to the
  client.

Also, update the inline comment on read errors in `(*UDPProxy).Run()`.
This comment was misleadingly referencing ECONNREFUSED - Linux's UDP
implementation never returns this error (see [1]). Instead, state why
`net.ErrClosed` is perfectly fine and doesn't need to be logged
(although, docker-proxy currently logs to nowhere).

[1]: https://github.com/search?q=repo%3Atorvalds%2Flinux+ECONNREFUSED+path%3A%2F%5Enet%5C%2F%28ipv4%7Cipv6%29%5C%2F%28udp%7Ctcp%29%2F&type=code

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-10-15 12:42:15 +02:00
Rob Murray
d7ff538467 Fix iptables rule descriptions
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-10-09 10:25:16 +01:00
Albin Kerouanton
aafdd33c35 Merge pull request #48567 from akerouanton/add-SO_REUSEADDR-to-docker-proxy
cmd/docker-proxy: re-add SO_REUSEADDR
2024-10-08 21:30:23 +02:00