Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pasta: use new --map-guest-addr option #2136

Merged
merged 8 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 42 additions & 14 deletions libnetwork/etchosts/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,27 @@ import (
"github.com/containers/storage/pkg/unshare"
)

// GetHostContainersInternalIP returns the host.containers.internal ip
// if netStatus is not nil then networkInterface also must be non nil otherwise this function panics
func GetHostContainersInternalIP(conf *config.Config, netStatus map[string]types.StatusBlock, networkInterface types.ContainerNetwork) string {
return GetHostContainersInternalIPExcluding(conf, netStatus, networkInterface, nil)
// HostContainersInternalOptions contains the options for GetHostContainersInternalIP()
type HostContainersInternalOptions struct {
// Conf is the containers.Conf, must not be nil
Conf *config.Config
// NetStatus is the network status for the container,
// if this is set networkInterface must not be nil
NetStatus map[string]types.StatusBlock
// NetworkInterface of the current runtime
NetworkInterface types.ContainerNetwork
// Exclude are then ips that should not be returned, this is
// useful to prevent returning the same ip as in the container.
Exclude []net.IP
// PreferIP is a ip that should be used if set but it has a
// lower priority than the containers.conf config option.
// This is used for the pasta --map-guest-addr ip.
PreferIP string
}

// GetHostContainersInternalIPExcluding returns the host.containers.internal ip
// Exclude are ips that should not be returned, this is useful to prevent returning the same ip as in the container.
// if netStatus is not nil then networkInterface also must be non nil otherwise this function panics
func GetHostContainersInternalIPExcluding(conf *config.Config, netStatus map[string]types.StatusBlock, networkInterface types.ContainerNetwork, exclude []net.IP) string {
switch conf.Containers.HostContainersInternalIP {
// GetHostContainersInternalIP returns the host.containers.internal ip
func GetHostContainersInternalIP(opts HostContainersInternalOptions) string {
switch opts.Conf.Containers.HostContainersInternalIP {
case "":
// if empty (default) we will automatically choose one below
// if machine using gvproxy we let the gvproxy dns server handle the dns name so do not add it
Expand All @@ -30,16 +40,22 @@ func GetHostContainersInternalIPExcluding(conf *config.Config, netStatus map[str
case "none":
return ""
default:
return conf.Containers.HostContainersInternalIP
return opts.Conf.Containers.HostContainersInternalIP
}

// caller has a specific ip it prefers
if opts.PreferIP != "" {
return opts.PreferIP
}

ip := ""
// Only use the bridge ip when root, as rootless the interfaces are created
// inside the special netns and not the host so we cannot use them.
if unshare.IsRootless() {
return util.GetLocalIPExcluding(exclude)
return util.GetLocalIPExcluding(opts.Exclude)
}
for net, status := range netStatus {
network, err := networkInterface.NetworkInspect(net)
for net, status := range opts.NetStatus {
network, err := opts.NetworkInterface.NetworkInspect(net)
// only add the host entry for bridge networks
// ip/macvlan gateway is normally not on the host
if err != nil || network.Driver != types.BridgeNetworkDriver {
Expand All @@ -60,7 +76,19 @@ func GetHostContainersInternalIPExcluding(conf *config.Config, netStatus map[str
if ip != "" {
return ip
}
return util.GetLocalIPExcluding(exclude)
return util.GetLocalIPExcluding(opts.Exclude)
}

// GetHostContainersInternalIPExcluding returns the host.containers.internal ip
// Exclude are ips that should not be returned, this is useful to prevent returning the same ip as in the container.
// if netStatus is not nil then networkInterface also must be non nil otherwise this function panics
func GetHostContainersInternalIPExcluding(conf *config.Config, netStatus map[string]types.StatusBlock, networkInterface types.ContainerNetwork, exclude []net.IP) string {
return GetHostContainersInternalIP(HostContainersInternalOptions{
Conf: conf,
NetStatus: netStatus,
NetworkInterface: networkInterface,
Exclude: exclude,
})
}

// GetNetworkHostEntries returns HostEntries for all ips in the network status
Expand Down
82 changes: 59 additions & 23 deletions libnetwork/internal/rootlessnetns/netns_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rootlessnetns

import (
"encoding/json"
"errors"
"fmt"
"io/fs"
Expand Down Expand Up @@ -34,6 +35,9 @@ const (
// refCountFile file name for the ref count file
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 = "rootless-netns-conn.pid"

Expand All @@ -54,11 +58,9 @@ type Netns struct {
// config contains containers.conf options.
config *config.Config

// ipAddresses used in the netns, this is needed to store
// the netns ips that are used by pasta. This is then handed
// back to the caller via IPAddresses() which then can make
// sure to not use them for host.containers.internal.
ipAddresses []net.IP
// info contain information about ip addresses used in the netns.
// A caller can get this info via Info().
info *types.RootlessNetnsInfo
}

type rootlessNetnsError struct {
Expand Down Expand Up @@ -115,6 +117,9 @@ func (n *Netns) getOrCreateNetns() (ns.NetNS, bool, error) {
// quick check if pasta/slirp4netns are still running
err := unix.Kill(pid, 0)
if err == nil {
if err := n.deserializeInfo(); err != nil {
return nil, false, wrapError("deserialize info", err)
}
// All good, return the netns.
return nsRef, false, nil
}
Expand Down Expand Up @@ -227,6 +232,15 @@ func (n *Netns) setupPasta(nsPath string) error {
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
}

Expand Down Expand Up @@ -261,6 +275,12 @@ func (n *Netns) setupSlirp4netns(nsPath string) error {
if err != nil {
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{
Path: n.getPath(resolvConfName),
Expand All @@ -270,10 +290,19 @@ func (n *Netns) setupSlirp4netns(nsPath string) error {
},
IPv6Enabled: res.IPv6,
KeepHostServers: true,
Nameservers: []string{resolveIP.String()},
Nameservers: nameservers,
}); err != nil {
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
}

Expand Down Expand Up @@ -541,20 +570,6 @@ func (n *Netns) runInner(toRun func() error, cleanup bool) (err error) {
if err := toRun(); err != nil {
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
})
}
Expand Down Expand Up @@ -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
// These should then not be assigned for the host.containers.internal entry.
func (n *Netns) Info() *types.RootlessNetnsInfo {
return &types.RootlessNetnsInfo{
IPAddresses: n.ipAddresses,
}
return n.info
}

func refCount(dir string, inc int) (int, error) {
Expand Down Expand Up @@ -671,3 +684,26 @@ func readPidFile(path string) (int, error) {
}
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)
}
Loading