rootlessnetns: cache dns and guest addr options

When using the rootless netns (bridge mode) so far podman ignored the
proper pasta or slirp4netns dns sever for networks without aardvark-dns.
This is not good. We should try to use them by default, and with the new
MapGuestAddr option we need to use that as well for
host.containers.internal. The problem is that becuase we only know what
options we uses when we started the process later container starts from
a new podman process do not really see these options if we just cache
the result in memory. So in order to make all following podman process
aware we serialize this info struct as json and later processes read it
when needed.

It also means we do not have to lookup the netns ip evey time so I
removed that code.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger 2024-08-26 17:49:19 +02:00
parent a3c9f2bd9d
commit 74553777c6
2 changed files with 63 additions and 23 deletions

View File

@ -1,6 +1,7 @@
package rootlessnetns package rootlessnetns
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
@ -34,6 +35,9 @@ const (
// refCountFile file name for the ref count file // refCountFile file name for the ref count file
refCountFile = "ref-count" refCountFile = "ref-count"
// infoCacheFile file name for the cache file used to store the rootless netns info
infoCacheFile = "info.json"
// rootlessNetNsConnPidFile is the name of the rootless netns slirp4netns/pasta pid file // rootlessNetNsConnPidFile is the name of the rootless netns slirp4netns/pasta pid file
rootlessNetNsConnPidFile = "rootless-netns-conn.pid" rootlessNetNsConnPidFile = "rootless-netns-conn.pid"
@ -54,11 +58,9 @@ type Netns struct {
// config contains containers.conf options. // config contains containers.conf options.
config *config.Config config *config.Config
// ipAddresses used in the netns, this is needed to store // info contain information about ip addresses used in the netns.
// the netns ips that are used by pasta. This is then handed // A caller can get this info via Info().
// back to the caller via IPAddresses() which then can make info *types.RootlessNetnsInfo
// sure to not use them for host.containers.internal.
ipAddresses []net.IP
} }
type rootlessNetnsError struct { type rootlessNetnsError struct {
@ -115,6 +117,9 @@ func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) {
// quick check if pasta/slirp4netns are still running // quick check if pasta/slirp4netns are still running
err := unix.Kill(pid, 0) err := unix.Kill(pid, 0)
if err == nil { if err == nil {
if err := n.deserializeInfo(); err != nil {
return nil, false, wrapError("deserialize info", err)
}
// All good, return the netns. // All good, return the netns.
return nsRef, false, nil return nsRef, false, nil
} }
@ -227,6 +232,15 @@ func (n *Netns) setupPasta(nsPath string) error {
return wrapError("create resolv.conf", err) return wrapError("create resolv.conf", err)
} }
n.info = &types.RootlessNetnsInfo{
IPAddresses: res.IPAddresses,
DnsForwardIps: res.DNSForwardIPs,
MapGuestIps: res.MapGuestAddrIPs,
}
if err := n.serializeInfo(); err != nil {
return wrapError("serialize info", err)
}
return nil return nil
} }
@ -261,6 +275,12 @@ func (n *Netns) setupSlirp4netns(nsPath string) error {
if err != nil { if err != nil {
return wrapError("determine default slirp4netns DNS address", err) return wrapError("determine default slirp4netns DNS address", err)
} }
nameservers := []string{resolveIP.String()}
netnsIP, err := slirp4netns.GetIP(res.Subnet)
if err != nil {
return wrapError("determine default slirp4netns ip address", err)
}
if err := resolvconf.New(&resolvconf.Params{ if err := resolvconf.New(&resolvconf.Params{
Path: n.getPath(resolvConfName), Path: n.getPath(resolvConfName),
@ -270,10 +290,19 @@ func (n *Netns) setupSlirp4netns(nsPath string) error {
}, },
IPv6Enabled: res.IPv6, IPv6Enabled: res.IPv6,
KeepHostServers: true, KeepHostServers: true,
Nameservers: []string{resolveIP.String()}, Nameservers: nameservers,
}); err != nil { }); err != nil {
return wrapError("create resolv.conf", err) return wrapError("create resolv.conf", err)
} }
n.info = &types.RootlessNetnsInfo{
IPAddresses: []net.IP{*netnsIP},
DnsForwardIps: nameservers,
}
if err := n.serializeInfo(); err != nil {
return wrapError("serialize info", err)
}
return nil return nil
} }
@ -541,20 +570,6 @@ func (n *Netns) runInner(toRun func() error, cleanup bool) (err error) {
if err := toRun(); err != nil { if err := toRun(); err != nil {
return err return err
} }
// get the current active addresses in the netns, and store them
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
}
ips := make([]net.IP, 0, len(addrs))
for _, addr := range addrs {
// make sure to skip localhost and other special addresses
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
ips = append(ips, ipnet.IP)
}
}
n.ipAddresses = ips
return nil return nil
}) })
} }
@ -630,9 +645,7 @@ func (n *Netns) Run(lock *lockfile.LockFile, toRun func() error) error {
// IPAddresses returns the currently used ip addresses in the netns // IPAddresses returns the currently used ip addresses in the netns
// These should then not be assigned for the host.containers.internal entry. // These should then not be assigned for the host.containers.internal entry.
func (n *Netns) Info() *types.RootlessNetnsInfo { func (n *Netns) Info() *types.RootlessNetnsInfo {
return &types.RootlessNetnsInfo{ return n.info
IPAddresses: n.ipAddresses,
}
} }
func refCount(dir string, inc int) (int, error) { func refCount(dir string, inc int) (int, error) {
@ -671,3 +684,26 @@ func readPidFile(path string) (int, error) {
} }
return strconv.Atoi(strings.TrimSpace(string(b))) return strconv.Atoi(strings.TrimSpace(string(b)))
} }
func (n *Netns) serializeInfo() error {
f, err := os.Create(filepath.Join(n.dir, infoCacheFile))
if err != nil {
return err
}
return json.NewEncoder(f).Encode(n.info)
}
func (n *Netns) deserializeInfo() error {
f, err := os.Open(filepath.Join(n.dir, infoCacheFile))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
defer f.Close()
if n.info == nil {
n.info = new(types.RootlessNetnsInfo)
}
return json.NewDecoder(f).Decode(n.info)
}

View File

@ -342,6 +342,10 @@ type TeardownOptions struct {
type RootlessNetnsInfo struct { type RootlessNetnsInfo struct {
// IPAddresses used in the netns, must not be used for host.containers.internal // IPAddresses used in the netns, must not be used for host.containers.internal
IPAddresses []net.IP IPAddresses []net.IP
// DnsForwardIps ips used in resolv.conf
DnsForwardIps []string
// MapGuestIps should be used for the host.containers.internal entry when set
MapGuestIps []string
} }
// FilterFunc can be passed to NetworkList to filter the networks. // FilterFunc can be passed to NetworkList to filter the networks.