Compare commits

...

16 Commits

Author SHA1 Message Date
Albin Kerouanton
8e96db1c32 Merge pull request #47832 from vvoland/v26.1-47821
[26.1 backport] Forward DNS requests into --internal networks
2024-05-15 09:33:02 +02:00
Rob Murray
c21fe3efa3 Forward DNS requests into --internal networks
A recent change to prevent containers only connected to --internal
networks from communicating with external DNS servers inadvertently
prevented the daemon's internal DNS server from forwarding requests
within an internal network to a containerised DNS server.

Relax the check, so that only requests that need to be forwarded
from the host's network namespace are dropped.

External DNS servers remain unreachable from the internal network.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 77a47dba3b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 15:17:56 +02:00
Sebastiaan van Stijn
86af4eddb3 Merge pull request #47833 from vvoland/v26.1-47744
[26.1 backport] Do not forward DNS requests to self.
2024-05-14 15:14:02 +02:00
Sebastiaan van Stijn
73511cdee0 Merge pull request #47829 from vvoland/v26.1-47749
[26.1 backport] apparmor: Allow confined runc to kill containers
2024-05-14 14:12:35 +02:00
Rob Murray
9326cda7bf Do not forward DNS requests to self.
If a container is configured with the internal DNS resolver's own
address as an external server, try the next ext server rather than
recursing (return SERVFAIL if there are no other servers).

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 87506142d8)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 12:27:01 +02:00
Tomáš Virtus
76fcf9a8e0 apparmor: Allow confined runc to kill containers
/usr/sbin/runc is confined with "runc" profile[1] introduced in AppArmor
v4.0.0. This change breaks stopping of containers, because the profile
assigned to containers doesn't accept signals from the "runc" peer.
AppArmor >= v4.0.0 is currently part of Ubuntu Mantic (23.10) and later.

In the case of Docker, this regression is hidden by the fact that
dockerd itself sends SIGKILL to the running container after runc fails
to stop it. It is still a regression, because graceful shutdowns of
containers via "docker stop" are no longer possible, as SIGTERM from
runc is not delivered to them. This can be seen in logs from dockerd
when run with debug logging enabled and also from tracing signals with
killsnoop utility from bcc[2] (in bpfcc-tools package in Debian/Ubuntu):

  Test commands:

    root@cloudimg:~# docker run -d --name test redis
    ba04c137827df8468358c274bc719bf7fc291b1ed9acf4aaa128ccc52816fe46
    root@cloudimg:~# docker stop test

  Relevant syslog messages (with wrapped long lines):

    Apr 23 20:45:26 cloudimg kernel: audit:
      type=1400 audit(1713905126.444:253): apparmor="DENIED"
      operation="signal" class="signal" profile="docker-default" pid=9289
      comm="runc" requested_mask="receive" denied_mask="receive"
      signal=kill peer="runc"
    Apr 23 20:45:36 cloudimg dockerd[9030]:
      time="2024-04-23T20:45:36.447016467Z"
      level=warning msg="Container failed to exit within 10s of kill - trying direct SIGKILL"
      container=ba04c137827df8468358c274bc719bf7fc291b1ed9acf4aaa128ccc52816fe46
      error="context deadline exceeded"

  Killsnoop output after "docker stop ...":

    root@cloudimg:~# killsnoop-bpfcc
    TIME      PID      COMM             SIG  TPID     RESULT
    20:51:00  9631     runc             3    9581     -13
    20:51:02  9637     runc             9    9581     -13
    20:51:12  9030     dockerd          9    9581     0

This change extends the docker-default profile with rules that allow
receiving signals from processes that run confined with either runc or
crun profile (crun[4] is an alternative OCI runtime that's also confined
in AppArmor >= v4.0.0, see [1]). It is backward compatible because the
peer value is a regular expression (AARE) so the referenced profile
doesn't have to exist for this profile to successfully compile and load.

Note that the runc profile has an attachment to /usr/sbin/runc. This is
the path where the runc package in Debian/Ubuntu puts the binary. When
the docker-ce package is installed from the upstream repository[3], runc
is installed as part of the containerd.io package at /usr/bin/runc.
Therefore it's still running unconfined and has no issues sending
signals to containers.

[1] https://gitlab.com/apparmor/apparmor/-/commit/2594d936
[2] https://github.com/iovisor/bcc/blob/master/tools/killsnoop.py
[3] https://download.docker.com/linux/ubuntu
[4] https://github.com/containers/crun

Signed-off-by: Tomáš Virtus <nechtom@gmail.com>
(cherry picked from commit 5ebe2c0d6b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 11:40:53 +02:00
Paweł Gronowski
ef1912d8b6 Merge pull request #47806 from vvoland/v26.1-47805
[26.1 backport] update to go1.21.10
2024-05-08 10:59:02 +02:00
Paweł Gronowski
10739af81a update to go1.21.10
These minor releases include 2 security fixes following the security policy:

- cmd/go: arbitrary code execution during build on darwin
On Darwin, building a Go module which contains CGO can trigger arbitrary code execution when using the Apple version of ld, due to usage of the -lto_library flag in a "#cgo LDFLAGS" directive.
Thanks to Juho Forsén of Mattermost for reporting this issue.
This is CVE-2024-24787 and Go issue https://go.dev/issue/67119.

- net: malformed DNS message can cause infinite loop
A malformed DNS message in response to a query can cause the Lookup functions to get stuck in an infinite loop.
Thanks to long-name-let-people-remember-you on GitHub for reporting this issue, and to Mateusz Poliwczak for bringing the issue to our attention.
This is CVE-2024-24788 and Go issue https://go.dev/issue/66754.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.22.3

- https://github.com/golang/go/issues?q=milestone%3AGo1.21.10+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.21.9...go1.21.10

**- Description for the changelog**

```markdown changelog
Update Go runtime to 1.21.10
```

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 6c97e0e0b5)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-08 09:39:08 +02:00
Paweł Gronowski
ac2de55998 Merge pull request #47775 from vvoland/v26.1-47771
[26.1 backport] Option to avoid deleting the kernel_ll address from bridges.
2024-04-30 11:46:49 +02:00
Sebastiaan van Stijn
9a2b531127 Merge pull request #47774 from vvoland/v26.1-47769
[26.1 backport] Allow for a read-only "/proc/sys/net".
2024-04-30 10:05:23 +02:00
Rob Murray
2f5bbbe16b Option to avoid deleting the kernel_ll address from bridges.
If env var DOCKER_BRIDGE_PRESERVE_KERNEL_LL=1, don't assign fe80::1/64
to a bridge, and don't delete any link local address with prefix fe80::/64.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 57ada4b848)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-29 21:53:53 +02:00
Rob Murray
40618081f1 Allow for a read-only "/proc/sys/net".
If dockerd runs on a host with a read-only /proc/sys/net filesystem,
it isn't able to enable or disable IPv6 on network interfaces when
attaching a container to a network (including initial networks during
container creation).

In release 26.0.2, a read-only /proc/sys/net meant container creation
failed in all cases.

So, don't attempt to enable/disable IPv6 on an interface if it's already
set appropriately.

If it's not possible to enable IPv6 when it's needed, just log (because
that's what libnetwork has always done if IPv6 is disabled in the
kernel).

If it's not possible to disable IPv6 when it needs to be disabled,
refuse to create the container and raise an error that suggests setting
environment variable "DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1", to tell
the daemon it's ok to ignore the problem.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 01ea18f1e3)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-29 21:49:04 +02:00
Paweł Gronowski
21da192ae4 Merge pull request #47767 from austinvazquez/cherry-pick-eeec716e332e5c058dfe70ddc0006d8552f632b2-to-26.1
[26.1 backport] vendor: update containerd to v1.7.16
2024-04-29 12:08:54 +02:00
Derek McGowan
2c91196921 Update containerd to v1.7.16
Includes fix for HTTP fallback

Signed-off-by: Derek McGowan <derek@mcg.dev>
(cherry picked from commit eeec716e33)
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2024-04-26 16:05:37 +00:00
Paweł Gronowski
a9a8787c93 Merge pull request #47762 from tonistiigi/26.1-update-buildkit-v0.13.2
[26.1] vendor: update buildkit to v0.13.2
2024-04-26 10:31:10 +02:00
Tonis Tiigi
c9689eccf5 vendor: update buildkit to v0.13.2
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-25 11:19:43 -07:00
39 changed files with 669 additions and 138 deletions

View File

@@ -19,7 +19,7 @@ on:
default: false
env:
GO_VERSION: "1.21.9"
GO_VERSION: "1.21.10"
GOTESTLIST_VERSION: v0.3.1
TESTSTAT_VERSION: v0.1.25
WINDOWS_BASE_IMAGE: mcr.microsoft.com/windows/servercore

View File

@@ -13,7 +13,7 @@ on:
pull_request:
env:
GO_VERSION: "1.21.9"
GO_VERSION: "1.21.10"
DESTDIR: ./build
jobs:

View File

@@ -13,7 +13,7 @@ on:
pull_request:
env:
GO_VERSION: "1.21.9"
GO_VERSION: "1.21.10"
GIT_PAGER: "cat"
PAGER: "cat"

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.7
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.10
ARG BASE_DEBIAN_DISTRO="bookworm"
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"
ARG XX_VERSION=1.4.0

View File

@@ -5,7 +5,7 @@
# This represents the bare minimum required to build and test Docker.
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.10
ARG BASE_DEBIAN_DISTRO="bookworm"
ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}"

View File

@@ -161,7 +161,7 @@ FROM ${WINDOWS_BASE_IMAGE}:${WINDOWS_BASE_IMAGE_TAG}
# Use PowerShell as the default shell
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.10
ARG GOTESTSUM_VERSION=v1.8.2
ARG GOWINRES_VERSION=v0.3.1
ARG CONTAINERD_VERSION=v1.7.15

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.10
ARG BASE_DEBIAN_DISTRO="bookworm"
ARG PROTOC_VERSION=3.11.4

View File

@@ -47,6 +47,13 @@ func WithNetworkMode(mode string) func(*TestContainerConfig) {
}
}
// WithDNS sets external DNS servers for the container
func WithDNS(dns []string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.DNS = append([]string(nil), dns...)
}
}
// WithSysctls sets sysctl options for the container
func WithSysctls(sysctls map[string]string) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {

View File

@@ -9,6 +9,8 @@ import (
"github.com/docker/docker/integration/internal/network"
"github.com/docker/docker/testutil"
"github.com/docker/docker/testutil/daemon"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
@@ -33,3 +35,59 @@ func TestDaemonDNSFallback(t *testing.T) {
poll.WaitOn(t, container.IsSuccessful(ctx, c, cid), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(10*time.Second))
}
// Check that, when the internal DNS server's address is supplied as an external
// DNS server, the daemon doesn't start talking to itself.
func TestIntDNSAsExtDNS(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start daemon on Windows test run")
skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
ctx := setupTest(t)
d := daemon.New(t)
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
testcases := []struct {
name string
extServers []string
expExitCode int
expStdout string
}{
{
name: "only self",
extServers: []string{"127.0.0.11"},
expExitCode: 1,
expStdout: "SERVFAIL",
},
{
name: "self then ext",
extServers: []string{"127.0.0.11", "8.8.8.8"},
expExitCode: 0,
expStdout: "Non-authoritative answer",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
ctx := testutil.StartSpan(ctx, t)
const netName = "testnet"
network.CreateNoError(ctx, t, c, netName)
defer network.RemoveNoError(ctx, t, c, netName)
res := container.RunAttach(ctx, t, c,
container.WithNetworkMode(netName),
container.WithDNS(tc.extServers),
container.WithCmd("nslookup", "docker.com"),
)
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
assert.Check(t, is.Equal(res.ExitCode, tc.expExitCode))
assert.Check(t, is.Contains(res.Stdout.String(), tc.expStdout))
})
}
}

View File

@@ -469,7 +469,6 @@ func TestDefaultBridgeAddresses(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
ctx := setupTest(t)
d := daemon.New(t)
type testStep struct {
stepName string
@@ -487,13 +486,13 @@ func TestDefaultBridgeAddresses(t *testing.T) {
{
stepName: "Set up initial UL prefix",
fixedCIDRV6: "fd1c:f1a0:5d8d:aaaa::/64",
expAddrs: []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::1/64"},
expAddrs: []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::"},
},
{
// Modify that prefix, the default bridge's address must be deleted and re-added.
stepName: "Modify UL prefix - address change",
fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/64",
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::"},
},
{
// Modify the prefix length, the default bridge's address should not change.
@@ -501,7 +500,7 @@ func TestDefaultBridgeAddresses(t *testing.T) {
fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/80",
// The prefix length displayed by 'ip a' is not updated - it's informational, and
// can't be changed without unnecessarily deleting and re-adding the address.
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::"},
},
},
},
@@ -511,14 +510,14 @@ func TestDefaultBridgeAddresses(t *testing.T) {
{
stepName: "Standard LL subnet prefix",
fixedCIDRV6: "fe80::/64",
expAddrs: []string{"fe80::1/64"},
expAddrs: []string{"fe80::"},
},
{
// Modify that prefix, the default bridge's address must be deleted and re-added.
// The bridge must still have an address in the required (standard) LL subnet.
stepName: "Nonstandard LL prefix - address change",
fixedCIDRV6: "fe80:1234::/32",
expAddrs: []string{"fe80:1234::1/32", "fe80::1/64"},
expAddrs: []string{"fe80:1234::1/32", "fe80::"},
},
{
// Modify the prefix length, the addresses should not change.
@@ -526,32 +525,48 @@ func TestDefaultBridgeAddresses(t *testing.T) {
fixedCIDRV6: "fe80:1234::/64",
// The prefix length displayed by 'ip a' is not updated - it's informational, and
// can't be changed without unnecessarily deleting and re-adding the address.
expAddrs: []string{"fe80:1234::1/", "fe80::1/64"},
expAddrs: []string{"fe80:1234::1/", "fe80::"},
},
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
for _, step := range tc.steps {
// Check that the daemon starts - regression test for:
// https://github.com/moby/moby/issues/46829
d.Start(t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6)
d.Stop(t)
for _, preserveKernelLL := range []bool{false, true} {
var dopts []daemon.Option
if preserveKernelLL {
dopts = append(dopts, daemon.WithEnvVars("DOCKER_BRIDGE_PRESERVE_KERNEL_LL=1"))
}
d := daemon.New(t, dopts...)
c := d.NewClientT(t)
// Check that the expected addresses have been applied to the bridge. (Skip in
// rootless mode, because the bridge is in a different network namespace.)
if !testEnv.IsRootless() {
res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0")
assert.Equal(t, res.ExitCode, 0, step.stepName)
stdout := res.Stdout()
for _, expAddr := range step.expAddrs {
assert.Check(t, is.Contains(stdout, expAddr))
for _, tc := range testcases {
for _, step := range tc.steps {
tcName := fmt.Sprintf("kernel_ll_%v/%s/%s", preserveKernelLL, tc.name, step.stepName)
t.Run(tcName, func(t *testing.T) {
ctx := testutil.StartSpan(ctx, t)
// Check that the daemon starts - regression test for:
// https://github.com/moby/moby/issues/46829
d.StartWithBusybox(ctx, t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6)
// Start a container, so that the bridge is set "up" and gets a kernel_ll address.
cID := container.Run(ctx, t, c)
defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
d.Stop(t)
// Check that the expected addresses have been applied to the bridge. (Skip in
// rootless mode, because the bridge is in a different network namespace.)
if !testEnv.IsRootless() {
res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0")
assert.Equal(t, res.ExitCode, 0, step.stepName)
stdout := res.Stdout()
for _, expAddr := range step.expAddrs {
assert.Check(t, is.Contains(stdout, expAddr))
}
}
}
})
}
})
}
}
}
@@ -738,3 +753,78 @@ func TestSetInterfaceSysctl(t *testing.T) {
stdout := runRes.Stdout.String()
assert.Check(t, is.Contains(stdout, scName))
}
// With a read-only "/proc/sys/net" filesystem (simulated using env var
// DOCKER_TEST_RO_DISABLE_IPV6), check that if IPv6 can't be disabled on a
// container interface, container creation fails - unless the error is ignored by
// setting env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1.
// Regression test for https://github.com/moby/moby/issues/47751
func TestReadOnlySlashProc(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
ctx := setupTest(t)
testcases := []struct {
name string
daemonEnv []string
expErr string
}{
{
name: "Normality",
},
{
name: "Read only no workaround",
daemonEnv: []string{
"DOCKER_TEST_RO_DISABLE_IPV6=1",
},
expErr: "failed to disable IPv6 on container's interface eth0, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
},
{
name: "Read only with workaround",
daemonEnv: []string{
"DOCKER_TEST_RO_DISABLE_IPV6=1",
"DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1",
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
ctx := testutil.StartSpan(ctx, t)
d := daemon.New(t, daemon.WithEnvVars(tc.daemonEnv...))
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
const net4Name = "testnet4"
network.CreateNoError(ctx, t, c, net4Name)
defer network.RemoveNoError(ctx, t, c, net4Name)
id4 := container.Create(ctx, t, c,
container.WithNetworkMode(net4Name),
container.WithCmd("ls"),
)
defer c.ContainerRemove(ctx, id4, containertypes.RemoveOptions{Force: true})
err := c.ContainerStart(ctx, id4, containertypes.StartOptions{})
if tc.expErr == "" {
assert.Check(t, err)
} else {
assert.Check(t, is.ErrorContains(err, tc.expErr))
}
// It should always be possible to create a container on an IPv6 network (IPv6
// doesn't need to be disabled on the interface).
const net6Name = "testnet6"
network.CreateNoError(ctx, t, c, net6Name,
network.WithIPv6(),
network.WithIPAM("fd5c:15e3:0b62:5395::/64", "fd5c:15e3:0b62:5395::1"),
)
defer network.RemoveNoError(ctx, t, c, net6Name)
id6 := container.Run(ctx, t, c,
container.WithNetworkMode(net6Name),
container.WithCmd("ls"),
)
defer c.ContainerRemove(ctx, id6, containertypes.RemoveOptions{Force: true})
})
}
}

View File

@@ -2,11 +2,14 @@ package networking
import (
"context"
"os"
"path"
"strings"
"testing"
"time"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/integration/internal/network"
"github.com/docker/docker/testutil/daemon"
@@ -134,6 +137,59 @@ func TestInternalNetworkDNS(t *testing.T) {
assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
}
// Check that '--dns' can be used to name a server inside a '--internal' network.
// Regression test for https://github.com/moby/moby/issues/47822
func TestInternalNetworkLocalDNS(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No internal networks on Windows")
skip.If(t, testEnv.IsRootless, "Can't write an accessible dnsd.conf in rootless mode")
ctx := setupTest(t)
d := daemon.New(t)
d.StartWithBusybox(ctx, t)
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
intNetName := "intnet"
network.CreateNoError(ctx, t, c, intNetName,
network.WithDriver("bridge"),
network.WithInternal(),
)
defer network.RemoveNoError(ctx, t, c, intNetName)
// Write a config file for busybox's dnsd.
td := t.TempDir()
fname := path.Join(td, "dnsd.conf")
err := os.WriteFile(fname, []byte("foo.example 192.0.2.42\n"), 0644)
assert.NilError(t, err)
// Start a DNS server on the internal network.
serverId := container.Run(ctx, t, c,
container.WithNetworkMode(intNetName),
container.WithMount(mount.Mount{
Type: mount.TypeBind,
Source: fname,
Target: "/etc/dnsd.conf",
}),
container.WithCmd("dnsd"),
)
defer c.ContainerRemove(ctx, serverId, containertypes.RemoveOptions{Force: true})
// Get the DNS server's address.
inspect := container.Inspect(ctx, t, c, serverId)
serverIP := inspect.NetworkSettings.Networks[intNetName].IPAddress
// Query the internal network's DNS server (via the daemon's internal DNS server).
res := container.RunAttach(ctx, t, c,
container.WithNetworkMode(intNetName),
container.WithDNS([]string{serverIP}),
container.WithCmd("nslookup", "-type=A", "foo.example"),
)
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
assert.Check(t, is.Contains(res.Stdout.String(), "192.0.2.42"))
}
// TestNslookupWindows checks that nslookup gets results from external DNS.
// Regression test for https://github.com/moby/moby/issues/46792
func TestNslookupWindows(t *testing.T) {

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/netip"
"os"
"github.com/containerd/log"
"github.com/docker/docker/errdefs"
@@ -73,18 +74,20 @@ func (i *bridgeInterface) addresses(family int) ([]netlink.Addr, error) {
func getRequiredIPv6Addrs(config *networkConfiguration) (requiredAddrs map[netip.Addr]netip.Prefix, err error) {
requiredAddrs = make(map[netip.Addr]netip.Prefix)
// Always give the bridge 'fe80::1' - every interface is required to have an
// address in 'fe80::/64'. Linux may assign an address, but we'll replace it with
// 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool
// assigned address will not be a second address in the LL subnet.
ra, ok := netiputil.ToPrefix(bridgeIPv6)
if !ok {
err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix")
return nil, err
if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") != "1" {
// Always give the bridge 'fe80::1' - every interface is required to have an
// address in 'fe80::/64'. Linux may assign an address, but we'll replace it with
// 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool
// assigned address will not be a second address in the LL subnet.
ra, ok := netiputil.ToPrefix(bridgeIPv6)
if !ok {
err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix")
return nil, err
}
requiredAddrs[ra.Addr()] = ra
}
requiredAddrs[ra.Addr()] = ra
ra, ok = netiputil.ToPrefix(config.AddressIPv6)
ra, ok := netiputil.ToPrefix(config.AddressIPv6)
if !ok {
err = fmt.Errorf("failed to convert bridge IPv6 address '%s' to netip.Prefix", config.AddressIPv6.String())
return nil, err
@@ -116,6 +119,14 @@ func (i *bridgeInterface) programIPv6Addresses(config *networkConfiguration) err
if !ok {
return errdefs.System(fmt.Errorf("Failed to convert IPv6 address '%s' to netip.Addr", config.AddressIPv6))
}
// Optionally, avoid deleting the kernel-assigned link local address.
// (Don't delete fe80::1 either - if it was previously assigned to the bridge, and the
// kernel_ll address was deleted, the bridge won't get a new kernel_ll address.)
if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") == "1" {
if p, _ := ea.Prefix(64); p == linkLocalPrefix {
continue
}
}
// Ignore the prefix length when comparing addresses, it's informational
// (RFC-5942 section 4), and removing/re-adding an address that's still valid
// would disrupt traffic on live-restore.

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"os"
"github.com/containerd/log"
@@ -13,6 +14,9 @@ import (
// bridgeIPv6 is the default, link-local IPv6 address for the bridge (fe80::1/64)
var bridgeIPv6 = &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)}
// Standard link local prefix
var linkLocalPrefix = netip.MustParsePrefix("fe80::/64")
const (
ipv6ForwardConfPerm = 0o644
ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding"

View File

@@ -646,17 +646,47 @@ func setIPv6(nspath, iface string, enable bool) error {
value = '0'
}
if _, err := os.Stat(path); err != nil {
if curVal, err := os.ReadFile(path); err != nil {
if os.IsNotExist(err) {
log.G(context.TODO()).WithError(err).Warn("Cannot configure IPv6 forwarding on container interface. Has IPv6 been disabled in this node's kernel?")
if enable {
log.G(context.TODO()).WithError(err).Warn("Cannot enable IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
} else {
log.G(context.TODO()).WithError(err).Debug("Not disabling IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
}
return
}
errCh <- err
return
} else if len(curVal) > 0 && curVal[0] == value {
// Nothing to do, the setting is already correct.
return
}
if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil {
errCh <- fmt.Errorf("failed to %s IPv6 forwarding for container's interface %s: %w", action, iface, err)
if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil || os.Getenv("DOCKER_TEST_RO_DISABLE_IPV6") != "" {
logger := log.G(context.TODO()).WithFields(log.Fields{
"error": err,
"interface": iface,
})
if enable {
// The user asked for IPv6 on the interface, and we can't give it to them.
// But, in line with the IsNotExist case above, just log.
logger.Warn("Cannot enable IPv6 on container interface, continuing.")
} else if os.Getenv("DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE") == "1" {
// TODO(robmry) - remove this escape hatch for https://github.com/moby/moby/issues/47751
// If the "/proc" file exists but isn't writable, we can't disable IPv6, which is
// https://github.com/moby/moby/security/advisories/GHSA-x84c-p2g9-rqv9 ... so,
// the user is required to override the error (or configure IPv6, or disable IPv6
// by default in the OS, or make the "/proc" file writable). Once it's possible
// to enable IPv6 without having to configure IPAM etc, the env var should be
// removed. Then the user will have to explicitly enable IPv6 if it can't be
// disabled on the interface.
logger.Info("Cannot disable IPv6 on container interface but DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1, continuing.")
} else {
logger.Error("Cannot disable IPv6 on container interface. Set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore.")
errCh <- fmt.Errorf(
"failed to %s IPv6 on container's interface %s, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
action, iface)
}
return
}
}()

View File

@@ -224,13 +224,7 @@ func (r *Resolver) Stop() {
// when forwarding queries, unless SetExtServersForSrc has configured servers
// for the DNS client making the request.
func (r *Resolver) SetExtServers(extDNS []extDNSEntry) {
l := len(extDNS)
if l > maxExtDNS {
l = maxExtDNS
}
for i := 0; i < l; i++ {
r.extDNSList[i] = extDNS[i]
}
copy(r.extDNSList[:], r.filterExtServers(extDNS))
}
// SetForwardingPolicy re-configures the embedded DNS resolver to either enable or disable forwarding DNS queries to
@@ -244,7 +238,7 @@ func (r *Resolver) SetForwardingPolicy(policy bool) {
// in preference to servers set by SetExtServers. Supplying a nil or empty extDNS
// deletes nameservers for srcAddr.
func (r *Resolver) SetExtServersForSrc(srcAddr netip.Addr, extDNS []extDNSEntry) error {
r.ipToExtDNS.set(srcAddr, extDNS)
r.ipToExtDNS.set(srcAddr, r.filterExtServers(extDNS))
return nil
}
@@ -258,6 +252,23 @@ func (r *Resolver) ResolverOptions() []string {
return []string{"ndots:0"}
}
// filterExtServers removes the resolver's own address from extDNS if present,
// and returns the result.
func (r *Resolver) filterExtServers(extDNS []extDNSEntry) []extDNSEntry {
result := make([]extDNSEntry, 0, len(extDNS))
for _, e := range extDNS {
if !e.HostLoopback {
if ra, _ := netip.ParseAddr(e.IPStr); ra == r.listenAddress {
log.G(context.TODO()).Infof("[resolver] not using own address (%s) as an external DNS server",
r.listenAddress)
continue
}
}
result = append(result, e)
}
return result
}
//nolint:gosec // The RNG is not used in a security-sensitive context.
var (
shuffleRNG = rand.New(rand.NewSource(time.Now().Unix()))
@@ -475,17 +486,15 @@ func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
return
}
if r.proxyDNS.Load() {
// If the user sets ndots > 0 explicitly and the query is
// in the root domain don't forward it out. We will return
// failure and let the client retry with the search domain
// attached.
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
resp = createRespMsg(query)
} else {
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
}
// If the user sets ndots > 0 explicitly and the query is
// in the root domain don't forward it out. We will return
// failure and let the client retry with the search domain
// attached.
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
resp = createRespMsg(query)
} else {
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
}
if resp == nil {
@@ -530,10 +539,18 @@ func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, remoteAddr n
ctx, span := otel.Tracer("").Start(ctx, "resolver.forwardExtDNS")
defer span.End()
proxyDNS := r.proxyDNS.Load()
for _, extDNS := range r.extDNS(netiputil.AddrPortFromNet(remoteAddr)) {
if extDNS.IPStr == "" {
break
}
// If proxyDNS is false, do not forward the request from the host's namespace
// (don't access an external DNS server from an internal network). But, it is
// safe to make the request from the container's network namespace - it'll fail
// if the DNS server is not accessible, but the server may be on-net.
if !proxyDNS && extDNS.HostLoopback {
continue
}
// limits the number of outstanding concurrent queries.
ctx, cancel := context.WithTimeout(ctx, extIOTimeout)

View File

@@ -25,6 +25,10 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
umount,
# Host (privileged) processes may send signals to container processes.
signal (receive) peer=unconfined,
# runc may send signals to container processes (for "docker stop").
signal (receive) peer=runc,
# crun may send signals to container processes (for "docker stop" when used with crun OCI runtime).
signal (receive) peer=crun,
# dockerd may send signals to container processes (for "docker kill").
signal (receive) peer={{.DaemonProfile}},
# Container processes may send signals amongst themselves.

View File

@@ -25,7 +25,7 @@ require (
github.com/aws/smithy-go v1.19.0
github.com/cloudflare/cfssl v1.6.4
github.com/containerd/cgroups/v3 v3.0.3
github.com/containerd/containerd v1.7.15
github.com/containerd/containerd v1.7.16
github.com/containerd/continuity v0.4.3
github.com/containerd/fifo v1.1.0
github.com/containerd/log v0.1.0
@@ -60,7 +60,7 @@ require (
github.com/miekg/dns v1.1.57
github.com/mistifyio/go-zfs/v3 v3.0.1
github.com/mitchellh/copystructure v1.2.0
github.com/moby/buildkit v0.13.1
github.com/moby/buildkit v0.13.2
github.com/moby/docker-image-spec v1.3.1
github.com/moby/ipvs v1.1.0
github.com/moby/locker v1.0.1
@@ -87,7 +87,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c
github.com/tonistiigi/go-archvariant v1.0.0
github.com/vbatts/tar-split v0.11.5
github.com/vishvananda/netlink v1.2.1-beta.2
@@ -110,7 +110,7 @@ require (
google.golang.org/protobuf v1.33.0
gotest.tools/v3 v3.5.1
resenje.org/singleflight v0.4.1
tags.cncf.io/container-device-interface v0.7.1
tags.cncf.io/container-device-interface v0.7.2
)
require (

View File

@@ -156,8 +156,8 @@ github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGD
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes=
github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY=
github.com/containerd/containerd v1.7.16 h1:7Zsfe8Fkj4Wi2My6DXGQ87hiqIrmOXolm72ZEkFU5Mg=
github.com/containerd/containerd v1.7.16/go.mod h1:NL49g7A/Fui7ccmxV6zkBWwqMgmMxFWzujYCc+JLt7k=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
@@ -478,8 +478,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
github.com/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE=
github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k=
github.com/moby/buildkit v0.13.2 h1:nXNszM4qD9E7QtG7bFWPnDI1teUQFQglBzon/IU3SzI=
github.com/moby/buildkit v0.13.2/go.mod h1:2cyVOv9NoHM7arphK9ZfHIWKn9YVZRFd1wXB8kKmEzY=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/ipvs v1.1.0 h1:ONN4pGaZQgAx+1Scz5RvWV4Q7Gb+mvfRh3NsPS+1XQQ=
@@ -673,8 +673,8 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5 h1:oZS8KCqAg62sxJkEq/Ppzqrb6EooqzWtL8Oaex7bc5c=
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c h1:+6wg/4ORAbnSoGDzg2Q1i3CeMcT/jjhye/ZfnBHy7/M=
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
github.com/tonistiigi/go-actions-cache v0.0.0-20240227172821-a0b64f338598 h1:DA/NDC0YbMdnfcOSUzAnbUZE6dSM54d+0hrBqG+bOfs=
github.com/tonistiigi/go-actions-cache v0.0.0-20240227172821-a0b64f338598/go.mod h1:anhKd3mnC1shAbQj1Q4IJ+w6xqezxnyDYlx/yKa7IXM=
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
@@ -1106,7 +1106,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
tags.cncf.io/container-device-interface v0.7.1 h1:MATNCbAD1su9U6zwQe5BrQ2vGGp1GBayD70bYaxYCNE=
tags.cncf.io/container-device-interface v0.7.1/go.mod h1:h1JVuOqTQVORp8DziaWKUCDNzAmN+zeCbqbqD30D0ZQ=
tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU=
tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto=
tags.cncf.io/container-device-interface/specs-go v0.7.0 h1:w/maMGVeLP6TIQJVYT5pbqTi8SCw/iHZ+n4ignuGHqg=
tags.cncf.io/container-device-interface/specs-go v0.7.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=

View File

@@ -394,6 +394,9 @@ The deprecated properties in [`config.toml`](./docs/cri/config.md) are shown in
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `auths` | containerd v1.3 | containerd v2.0 | Use [`ImagePullSecrets`](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). See also [#8228](https://github.com/containerd/containerd/issues/8228). |
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `configs` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `mirrors` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |
|`[plugins."io.containerd.tracing.processor.v1.otlp"]` | `endpoint`, `protocol`, `insecure` | containerd v1.6.29 | containerd v2.0 | Use [OTLP environment variables](https://opentelemetry.io/docs/specs/otel/protocol/exporter/), e.g. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_SDK_DISABLED |
|`[plugins."io.containerd.internal.v1.tracing"]` | `service_name`, `sampling_ratio` | containerd v1.6.29 | containerd v2.0 | Instead use [OTel environment variables](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/), e.g. OTEL_SERVICE_NAME, OTEL_TRACES_SAMPLER* |
> **Note**
>

View File

@@ -102,7 +102,7 @@ EOF
config.vm.provision "install-golang", type: "shell", run: "once" do |sh|
sh.upload_path = "/tmp/vagrant-install-golang"
sh.env = {
'GO_VERSION': ENV['GO_VERSION'] || "1.21.8",
'GO_VERSION': ENV['GO_VERSION'] || "1.21.9",
}
sh.inline = <<~SHELL
#!/usr/bin/env bash

View File

@@ -53,6 +53,10 @@ const (
RuntimeRuncV1 Warning = Prefix + "runtime-runc-v1"
// CRICRIUPath is a warning for the use of the `CriuPath` property
CRICRIUPath Warning = Prefix + "cri-criu-path"
// OTLPTracingConfig is a warning for the use of the `otlp` property
TracingOTLPConfig Warning = Prefix + "tracing-processor-config"
// TracingServiceConfig is a warning for the use of the `tracing` property
TracingServiceConfig Warning = Prefix + "tracing-service-config"
)
var messages = map[Warning]string{
@@ -82,6 +86,10 @@ var messages = map[Warning]string{
RuntimeRuncV1: "The `io.containerd.runc.v1` runtime is deprecated since containerd v1.4 and removed in containerd v2.0. Use the `io.containerd.runc.v2` runtime instead.",
CRICRIUPath: "The `CriuPath` property of `[plugins.\"io.containerd.grpc.v1.cri\".containerd.runtimes.*.options]` is deprecated since containerd v1.7 and will be removed in containerd v2.0. " +
"Use a criu binary in $PATH instead.",
TracingOTLPConfig: "The `otlp` property of `[plugins.\"io.containerd.tracing.processor.v1\".otlp]` is deprecated since containerd v1.6 and will be removed in containerd v2.0." +
"Use OTLP environment variables instead: https://opentelemetry.io/docs/specs/otel/protocol/exporter/",
TracingServiceConfig: "The `tracing` property of `[plugins.\"io.containerd.internal.v1\".tracing]` is deprecated since containerd v1.6 and will be removed in containerd v2.0." +
"Use OTEL environment variables instead: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/",
}
// Valid checks whether a given Warning is valid

View File

@@ -104,6 +104,10 @@ const (
DeprecationsPlugin = "deprecations"
)
const (
SnapshotterRootDir = "root"
)
// Registration contains information for registering a plugin
type Registration struct {
// Type of the plugin

View File

@@ -148,9 +148,11 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
defer a.mu.Unlock()
for _, c := range auth.ParseAuthHeader(last.Header) {
if c.Scheme == auth.BearerAuth {
if err := invalidAuthorization(c, responses); err != nil {
if retry, err := invalidAuthorization(ctx, c, responses); err != nil {
delete(a.handlers, host)
return err
} else if retry {
delete(a.handlers, host)
}
// reuse existing handler
@@ -328,18 +330,24 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (token, refreshToken st
return resp.Token, resp.RefreshToken, nil
}
func invalidAuthorization(c auth.Challenge, responses []*http.Response) error {
func invalidAuthorization(ctx context.Context, c auth.Challenge, responses []*http.Response) (retry bool, _ error) {
errStr := c.Parameters["error"]
if errStr == "" {
return nil
return retry, nil
}
n := len(responses)
if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) {
return nil
limitedErr := errStr
errLenghLimit := 64
if len(limitedErr) > errLenghLimit {
limitedErr = limitedErr[:errLenghLimit] + "..."
}
log.G(ctx).WithField("error", limitedErr).Debug("authorization error using bearer token, retrying")
return true, nil
}
return fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization)
return retry, fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization)
}
func sameRequest(r1, r2 *http.Request) bool {

View File

@@ -704,9 +704,71 @@ func IsLocalhost(host string) bool {
return ip.IsLoopback()
}
// NewHTTPFallback returns http.RoundTripper which allows fallback from https to
// http for registry endpoints with configurations for both http and TLS,
// such as defaulted localhost endpoints.
func NewHTTPFallback(transport http.RoundTripper) http.RoundTripper {
return &httpFallback{
super: transport,
}
}
type httpFallback struct {
super http.RoundTripper
host string
}
func (f *httpFallback) RoundTrip(r *http.Request) (*http.Response, error) {
// only fall back if the same host had previously fell back
if f.host != r.URL.Host {
resp, err := f.super.RoundTrip(r)
if !isTLSError(err) {
return resp, err
}
}
plainHTTPUrl := *r.URL
plainHTTPUrl.Scheme = "http"
plainHTTPRequest := *r
plainHTTPRequest.URL = &plainHTTPUrl
if f.host != r.URL.Host {
f.host = r.URL.Host
// update body on the second attempt
if r.Body != nil && r.GetBody != nil {
body, err := r.GetBody()
if err != nil {
return nil, err
}
plainHTTPRequest.Body = body
}
}
return f.super.RoundTrip(&plainHTTPRequest)
}
func isTLSError(err error) bool {
if err == nil {
return false
}
var tlsErr tls.RecordHeaderError
if errors.As(err, &tlsErr) && string(tlsErr.RecordHeader[:]) == "HTTP/" {
return true
}
if strings.Contains(err.Error(), "TLS handshake timeout") {
return true
}
return false
}
// HTTPFallback is an http.RoundTripper which allows fallback from https to http
// for registry endpoints with configurations for both http and TLS, such as
// defaulted localhost endpoints.
//
// Deprecated: Use NewHTTPFallback instead.
type HTTPFallback struct {
http.RoundTripper
}
@@ -722,6 +784,14 @@ func (f HTTPFallback) RoundTrip(r *http.Request) (*http.Response, error) {
plainHTTPRequest := *r
plainHTTPRequest.URL = &plainHTTPUrl
if r.Body != nil && r.GetBody != nil {
body, err := r.GetBody()
if err != nil {
return nil, err
}
plainHTTPRequest.Body = body
}
return f.RoundTripper.RoundTrip(&plainHTTPRequest)
}

View File

@@ -164,8 +164,10 @@ type CgroupConfig struct {
// ProxyPlugin provides a proxy plugin configuration
type ProxyPlugin struct {
Type string `toml:"type"`
Address string `toml:"address"`
Type string `toml:"type"`
Address string `toml:"address"`
Platform string `toml:"platform"`
Exports map[string]string `toml:"exports"`
}
// Decode unmarshals a plugin specific configuration by plugin id
@@ -251,13 +253,18 @@ func loadConfigFile(path string) (*Config, error) {
}
// resolveImports resolves import strings list to absolute paths list:
// - If path contains *, glob pattern matching applied
// - Non abs path is relative to parent config file directory
// - If path contains *, glob pattern matching applied
// - Abs paths returned as is
func resolveImports(parent string, imports []string) ([]string, error) {
var out []string
for _, path := range imports {
path := filepath.Clean(path)
if !filepath.IsAbs(path) {
path = filepath.Join(filepath.Dir(parent), path)
}
if strings.Contains(path, "*") {
matches, err := filepath.Glob(path)
if err != nil {
@@ -266,11 +273,6 @@ func resolveImports(parent string, imports []string) ([]string, error) {
out = append(out, matches...)
} else {
path = filepath.Clean(path)
if !filepath.IsAbs(path) {
path = filepath.Join(filepath.Dir(parent), path)
}
out = append(out, path)
}
}

View File

@@ -23,7 +23,7 @@ var (
Package = "github.com/containerd/containerd"
// Version holds the complete version number. Filled in at linking time.
Version = "1.7.15+unknown"
Version = "1.7.16+unknown"
// Revision is filled with the VCS (e.g. git) revision being used to build
// the program at linking time.

View File

@@ -40,10 +40,11 @@ type LogConfig struct {
}
type GRPCConfig struct {
Address []string `toml:"address"`
DebugAddress string `toml:"debugAddress"`
UID *int `toml:"uid"`
GID *int `toml:"gid"`
Address []string `toml:"address"`
DebugAddress string `toml:"debugAddress"`
UID *int `toml:"uid"`
GID *int `toml:"gid"`
SecurityDescriptor string `toml:"securityDescriptor"`
TLS TLSConfig `toml:"tls"`
// MaxRecvMsgSize int `toml:"max_recv_message_size"`

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/docker/go-units"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
@@ -104,3 +105,25 @@ func stripQuotes(s string) string {
}
return s
}
func DetectDefaultGCCap() DiskSpace {
return DiskSpace{Percentage: DiskSpacePercentage}
}
func (d DiskSpace) AsBytes(root string) int64 {
if d.Bytes != 0 {
return d.Bytes
}
if d.Percentage == 0 {
return 0
}
diskSize, err := getDiskSize(root)
if err != nil {
bklog.L.Warnf("failed to get disk size: %v", err)
return defaultCap
}
avail := diskSize * d.Percentage / 100
rounded := (avail/(1<<30) + 1) * 1e9 // round up
return rounded
}

View File

@@ -7,23 +7,13 @@ import (
"syscall"
)
func DetectDefaultGCCap() DiskSpace {
return DiskSpace{Percentage: 10}
}
func (d DiskSpace) AsBytes(root string) int64 {
if d.Bytes != 0 {
return d.Bytes
}
if d.Percentage == 0 {
return 0
}
var DiskSpacePercentage int64 = 10
func getDiskSize(root string) (int64, error) {
var st syscall.Statfs_t
if err := syscall.Statfs(root, &st); err != nil {
return defaultCap
return 0, err
}
diskSize := int64(st.Bsize) * int64(st.Blocks)
avail := diskSize * d.Percentage / 100
return (avail/(1<<30) + 1) * 1e9 // round up
return diskSize, nil
}

View File

@@ -3,10 +3,29 @@
package config
func DetectDefaultGCCap() DiskSpace {
return DiskSpace{Bytes: defaultCap}
}
import (
"golang.org/x/sys/windows"
)
func (d DiskSpace) AsBytes(root string) int64 {
return d.Bytes
// set as double that for Linux since
// Windows images are generally larger.
var DiskSpacePercentage int64 = 20
func getDiskSize(root string) (int64, error) {
rootUTF16, err := windows.UTF16FromString(root)
if err != nil {
return 0, err
}
var freeAvailableBytes uint64
var totalBytes uint64
var totalFreeBytes uint64
if err := windows.GetDiskFreeSpaceEx(
&rootUTF16[0],
&freeAvailableBytes,
&totalBytes,
&totalFreeBytes); err != nil {
return 0, err
}
return int64(totalBytes), nil
}

View File

@@ -419,11 +419,16 @@ func (ic *ImageWriter) rewriteRemoteWithEpoch(ctx context.Context, opts *ImageCo
var divergedFromBase bool
for i, desc := range remoteDescriptors {
i, desc := i, desc
info, err := cs.Info(ctx, desc.Digest)
if err != nil {
return nil, err
// Usually we get non-empty diffID here, but if the content was ingested via a third-party containerd client,
// diffID here can be empty, and will be computed by the converter.
diffID := digest.Digest(desc.Annotations[labels.LabelUncompressed])
if diffID == "" {
info, err := cs.Info(ctx, desc.Digest)
if err != nil {
return nil, err
}
diffID = digest.Digest(info.Labels[labels.LabelUncompressed]) // can be still empty
}
diffID := digest.Digest(info.Labels[labels.LabelUncompressed]) // can be empty
var immDiffID digest.Digest
if !divergedFromBase && baseImg != nil && i < len(baseImg.RootFS.DiffIDs) {
immDiffID = baseImg.RootFS.DiffIDs[i]

View File

@@ -1340,11 +1340,10 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
copyOpts := []llb.ConstraintsOpt{
llb.Platform(*d.platform),
}
copy(copyOpts, fileOpt)
copyOpts = append(copyOpts, fileOpt...)
copyOpts = append(copyOpts, llb.ProgressGroup(pgID, pgName, true))
var mergeOpts []llb.ConstraintsOpt
copy(mergeOpts, fileOpt)
mergeOpts := append([]llb.ConstraintsOpt{}, fileOpt...)
d.cmdIndex--
mergeOpts = append(mergeOpts, llb.ProgressGroup(pgID, pgName, false), llb.WithCustomName(prefixCommand(d, "LINK "+name, d.prefixPlatform, &platform, env)))

View File

@@ -8,6 +8,7 @@ import (
type HeaderConverter func(*tar.Header)
// NewReader returns a reader that applies headerConverter.
// srcContent is drained until hitting EOF.
// Forked from https://github.com/moby/moby/blob/v24.0.6/pkg/archive/copy.go#L308-L373 .
func NewReader(srcContent io.Reader, headerConverter HeaderConverter) io.ReadCloser {
rebased, w := io.Pipe()
@@ -21,7 +22,14 @@ func NewReader(srcContent io.Reader, headerConverter HeaderConverter) io.ReadClo
if err == io.EOF {
// Signals end of archive.
rebasedTar.Close()
w.Close()
// drain the reader into io.Discard, until hitting EOF
// https://github.com/moby/buildkit/pull/4807#discussion_r1544621787
_, err = io.Copy(io.Discard, srcContent)
if err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
return
}
if err != nil {

View File

@@ -111,7 +111,7 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b walkerFn, fil
if filter != nil {
filter(f2.path, &statCopy)
}
f2copy = &currentPath{path: filepath.FromSlash(f2.path), stat: &statCopy}
f2copy = &currentPath{path: f2.path, stat: &statCopy}
}
k, p := pathChange(f1, f2copy)
switch k {

View File

@@ -37,6 +37,7 @@ type DiskWriter struct {
ctx context.Context
cancel func()
eg *errgroup.Group
egCtx context.Context
filter FilterFunc
dirModTimes map[string]int64
}
@@ -50,13 +51,14 @@ func NewDiskWriter(ctx context.Context, dest string, opt DiskWriterOpt) (*DiskWr
}
ctx, cancel := context.WithCancel(ctx)
eg, ctx := errgroup.WithContext(ctx)
eg, egCtx := errgroup.WithContext(ctx)
return &DiskWriter{
opt: opt,
dest: dest,
eg: eg,
ctx: ctx,
egCtx: egCtx,
cancel: cancel,
filter: opt.Filter,
dirModTimes: map[string]int64{},
@@ -98,7 +100,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
}
}()
destPath := filepath.Join(dw.dest, filepath.FromSlash(p))
destPath := filepath.Join(dw.dest, p)
if kind == ChangeKindDelete {
if dw.filter != nil {
@@ -183,12 +185,12 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
}
default:
isRegularFile = true
file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY, fi.Mode()) //todo: windows
file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY, fi.Mode())
if err != nil {
return errors.Wrapf(err, "failed to create %s", newPath)
}
if dw.opt.SyncDataCb != nil {
if err := dw.processChange(ChangeKindAdd, p, fi, file); err != nil {
if err := dw.processChange(dw.ctx, ChangeKindAdd, p, fi, file); err != nil {
file.Close()
return err
}
@@ -219,7 +221,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
dw.requestAsyncFileData(p, destPath, fi, &statCopy)
}
} else {
return dw.processChange(kind, p, fi, nil)
return dw.processChange(dw.ctx, kind, p, fi, nil)
}
return nil
@@ -228,7 +230,7 @@ func (dw *DiskWriter) HandleChange(kind ChangeKind, p string, fi os.FileInfo, er
func (dw *DiskWriter) requestAsyncFileData(p, dest string, fi os.FileInfo, st *types.Stat) {
// todo: limit worker threads
dw.eg.Go(func() error {
if err := dw.processChange(ChangeKindAdd, p, fi, &lazyFileWriter{
if err := dw.processChange(dw.egCtx, ChangeKindAdd, p, fi, &lazyFileWriter{
dest: dest,
}); err != nil {
return err
@@ -237,7 +239,7 @@ func (dw *DiskWriter) requestAsyncFileData(p, dest string, fi os.FileInfo, st *t
})
}
func (dw *DiskWriter) processChange(kind ChangeKind, p string, fi os.FileInfo, w io.WriteCloser) error {
func (dw *DiskWriter) processChange(ctx context.Context, kind ChangeKind, p string, fi os.FileInfo, w io.WriteCloser) error {
origw := w
var hw *hashedWriter
if dw.opt.NotifyCb != nil {
@@ -252,7 +254,7 @@ func (dw *DiskWriter) processChange(kind ChangeKind, p string, fi os.FileInfo, w
if fn == nil && dw.opt.AsyncDataCb != nil {
fn = dw.opt.AsyncDataCb
}
if err := fn(dw.ctx, p, w); err != nil {
if err := fn(ctx, p, w); err != nil {
return err
}
} else {
@@ -313,7 +315,7 @@ type lazyFileWriter struct {
func (lfw *lazyFileWriter) Write(dt []byte) (int, error) {
if lfw.f == nil {
file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0) //todo: windows
file, err := os.OpenFile(lfw.dest, os.O_WRONLY, 0)
if os.IsPermission(err) {
// retry after chmod
fi, er := os.Stat(lfw.dest)

View File

@@ -1,6 +1,9 @@
package fsutil
import (
"context"
"io"
gofs "io/fs"
"os"
"syscall"
@@ -46,3 +49,68 @@ func (v *Hardlinks) HandleChange(kind ChangeKind, p string, fi os.FileInfo, err
return nil
}
// WithHardlinkReset returns a FS that fixes hardlinks for FS that has been filtered
// so that original hardlink sources might be missing
func WithHardlinkReset(fs FS) FS {
return &hardlinkFilter{fs: fs}
}
type hardlinkFilter struct {
fs FS
}
var _ FS = &hardlinkFilter{}
func (r *hardlinkFilter) Walk(ctx context.Context, target string, fn gofs.WalkDirFunc) error {
seenFiles := make(map[string]string)
return r.fs.Walk(ctx, target, func(path string, entry gofs.DirEntry, err error) error {
if err != nil {
return err
}
fi, err := entry.Info()
if err != nil {
return err
}
if fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 {
return fn(path, entry, nil)
}
stat, ok := fi.Sys().(*types.Stat)
if !ok {
return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
}
if stat.Linkname != "" {
if v, ok := seenFiles[stat.Linkname]; !ok {
seenFiles[stat.Linkname] = stat.Path
stat.Linkname = ""
entry = &dirEntryWithStat{DirEntry: entry, stat: stat}
} else {
if v != stat.Path {
stat.Linkname = v
entry = &dirEntryWithStat{DirEntry: entry, stat: stat}
}
}
}
seenFiles[path] = stat.Path
return fn(path, entry, nil)
})
}
func (r *hardlinkFilter) Open(p string) (io.ReadCloser, error) {
return r.fs.Open(p)
}
type dirEntryWithStat struct {
gofs.DirEntry
stat *types.Stat
}
func (d *dirEntryWithStat) Info() (gofs.FileInfo, error) {
return &StatInfo{d.stat}, nil
}

View File

@@ -1,10 +1,42 @@
// send.go and receive.go describe the fsutil file-transfer protocol, which
// allows transferring file trees across a network connection.
//
// The protocol operates as follows:
// - The client (the receiver) connects to the server (the sender).
// - The sender walks the target tree lexicographically and sends a series of
// STAT packets that describe each file (an empty stat indicates EOF).
// - The receiver sends a REQ packet for each file it requires the contents for,
// using the ID for the file (determined as its index in the STAT sequence).
// - The sender sends a DATA packet with byte arrays for the contents of the
// file, associated with an ID (an empty array indicates EOF).
// - Once the receiver has received all files it wants, it sends a FIN packet,
// and the file transfer is complete.
// If an error is encountered on either side, an ERR packet is sent containing
// a human-readable error.
//
// All paths transferred over the protocol are normalized to unix-style paths,
// regardless of which platforms are present on either side. These path
// conversions are performed right before sending a STAT packet (for the
// sender) or right after receiving the corresponding STAT packet (for the
// receiver); this abstraction doesn't leak into the rest of fsutil, which
// operates on native platform-specific paths.
//
// Note that in the case of cross-platform file transfers, the transfer is
// best-effort. Some filenames that are valid on a unix sender would not be
// valid on a windows receiver, so these paths are rejected as they are
// received. Additionally, file metadata, like user/group owners and xattrs do
// not have an exact correspondence on windows, and so would be discarded by
// a windows receiver.
package fsutil
import (
"context"
"io"
"os"
"path/filepath"
"sync"
"syscall"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil/types"
@@ -184,13 +216,24 @@ func (r *receiver) run(ctx context.Context) error {
}
break
}
// normalize unix wire-specific paths to platform-specific paths
path := filepath.FromSlash(p.Stat.Path)
if filepath.ToSlash(path) != p.Stat.Path {
// e.g. a linux path foo/bar\baz cannot be represented on windows
return errors.WithStack(&os.PathError{Path: p.Stat.Path, Err: syscall.EINVAL, Op: "unrepresentable path"})
}
p.Stat.Path = path
p.Stat.Linkname = filepath.FromSlash(p.Stat.Linkname)
if fileCanRequestData(os.FileMode(p.Stat.Mode)) {
r.mu.Lock()
r.files[p.Stat.Path] = i
r.mu.Unlock()
}
i++
cp := &currentPath{path: p.Stat.Path, stat: p.Stat}
cp := &currentPath{path: path, stat: p.Stat}
if err := r.orderValidator.HandleChange(ChangeKindAdd, cp.path, &StatInfo{cp.stat}, nil); err != nil {
return err
}

View File

@@ -29,7 +29,7 @@ type Stream interface {
func Send(ctx context.Context, conn Stream, fs FS, progressCb func(int, bool)) error {
s := &sender{
conn: &syncStream{Stream: conn},
fs: fs,
fs: WithHardlinkReset(fs),
files: make(map[uint32]string),
progressCb: progressCb,
sendpipeline: make(chan *sendHandle, 128),
@@ -161,6 +161,7 @@ func (s *sender) walk(ctx context.Context) error {
return errors.WithStack(&os.PathError{Path: path, Err: syscall.EBADMSG, Op: "fileinfo without stat info"})
}
stat.Path = filepath.ToSlash(stat.Path)
stat.Linkname = filepath.ToSlash(stat.Linkname)
p := &types.Packet{
Type: types.PACKET_STAT,
Stat: stat,

8
vendor/modules.txt vendored
View File

@@ -260,7 +260,7 @@ github.com/containerd/cgroups/v3/cgroup2/stats
# github.com/containerd/console v1.0.4
## explicit; go 1.13
github.com/containerd/console
# github.com/containerd/containerd v1.7.15
# github.com/containerd/containerd v1.7.16
## explicit; go 1.21
github.com/containerd/containerd
github.com/containerd/containerd/api/events
@@ -709,7 +709,7 @@ github.com/mitchellh/hashstructure/v2
# github.com/mitchellh/reflectwalk v1.0.2
## explicit
github.com/mitchellh/reflectwalk
# github.com/moby/buildkit v0.13.1
# github.com/moby/buildkit v0.13.2
## explicit; go 1.21
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types
@@ -1086,7 +1086,7 @@ github.com/syndtr/gocapability/capability
# github.com/tinylib/msgp v1.1.8
## explicit; go 1.15
github.com/tinylib/msgp/msgp
# github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
# github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c
## explicit; go 1.20
github.com/tonistiigi/fsutil
github.com/tonistiigi/fsutil/copy
@@ -1600,7 +1600,7 @@ resenje.org/singleflight
# sigs.k8s.io/yaml v1.3.0
## explicit; go 1.12
sigs.k8s.io/yaml
# tags.cncf.io/container-device-interface v0.7.1
# tags.cncf.io/container-device-interface v0.7.2
## explicit; go 1.20
tags.cncf.io/container-device-interface/internal/validation
tags.cncf.io/container-device-interface/internal/validation/k8s