1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
package oci
import (
"context"
"os"
"path/filepath"
"github.com/docker/docker/libnetwork/resolvconf"
"github.com/docker/docker/pkg/idtools"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/flightcontrol"
"github.com/pkg/errors"
)
var g flightcontrol.Group[struct{}]
var notFirstRun bool
var lastNotEmpty bool
// overridden by tests
var resolvconfPath = func(netMode pb.NetMode) string {
// The implementation of resolvconf.Path checks if systemd resolved is activated and chooses the internal
// resolv.conf (/run/systemd/resolve/resolv.conf) in such a case - see resolvconf_path.go of libnetwork.
// This, however, can be problematic, see https://github.com/moby/buildkit/issues/2404 and is not necessary
// in case the networking mode is set to host since the locally (127.0.0.53) running resolved daemon is
// accessible from inside a host networked container.
// For details of the implementation see https://github.com/moby/buildkit/pull/5207#discussion_r1705362230.
if netMode == pb.NetMode_HOST {
return "/etc/resolv.conf"
}
return resolvconf.Path()
}
type DNSConfig struct {
Nameservers []string
Options []string
SearchDomains []string
}
func GetResolvConf(ctx context.Context, stateDir string, idmap *idtools.IdentityMapping, dns *DNSConfig, netMode pb.NetMode) (string, error) {
p := filepath.Join(stateDir, "resolv.conf")
if netMode == pb.NetMode_HOST {
p = filepath.Join(stateDir, "resolv-host.conf")
}
_, err := g.Do(ctx, p, func(ctx context.Context) (struct{}, error) {
generate := !notFirstRun
notFirstRun = true
if !generate {
fi, err := os.Stat(p)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return struct{}{}, errors.WithStack(err)
}
generate = true
}
if !generate {
fiMain, err := os.Stat(resolvconfPath(netMode))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return struct{}{}, err
}
if lastNotEmpty {
generate = true
lastNotEmpty = false
}
} else if fi.ModTime().Before(fiMain.ModTime()) {
generate = true
}
}
}
if !generate {
return struct{}{}, nil
}
dt, err := os.ReadFile(resolvconfPath(netMode))
if err != nil && !errors.Is(err, os.ErrNotExist) {
return struct{}{}, errors.WithStack(err)
}
tmpPath := p + ".tmp"
if dns != nil {
var (
dnsNameservers = dns.Nameservers
dnsSearchDomains = dns.SearchDomains
dnsOptions = dns.Options
)
if len(dns.Nameservers) == 0 {
dnsNameservers = resolvconf.GetNameservers(dt, resolvconf.IP)
}
if len(dns.SearchDomains) == 0 {
dnsSearchDomains = resolvconf.GetSearchDomains(dt)
}
if len(dns.Options) == 0 {
dnsOptions = resolvconf.GetOptions(dt)
}
f, err := resolvconf.Build(tmpPath, dnsNameservers, dnsSearchDomains, dnsOptions)
if err != nil {
return struct{}{}, errors.WithStack(err)
}
dt = f.Content
}
if netMode != pb.NetMode_HOST || len(resolvconf.GetNameservers(dt, resolvconf.IP)) == 0 {
f, err := resolvconf.FilterResolvDNS(dt, true)
if err != nil {
return struct{}{}, errors.WithStack(err)
}
dt = f.Content
}
if err := os.WriteFile(tmpPath, dt, 0644); err != nil {
return struct{}{}, errors.WithStack(err)
}
if idmap != nil {
root := idmap.RootPair()
if err := os.Chown(tmpPath, root.UID, root.GID); err != nil {
return struct{}{}, errors.WithStack(err)
}
}
if err := os.Rename(tmpPath, p); err != nil {
return struct{}{}, errors.WithStack(err)
}
return struct{}{}, nil
})
if err != nil {
return "", err
}
return p, nil
}
|