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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>