-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Add host.containers.internal entry into container's etc/hosts #9972
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1358,6 +1358,34 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti | |
return c.save() | ||
} | ||
|
||
// Retrieves a container's "root" net namespace container dependency. | ||
func (c *Container) getRootNetNsDepCtr() (depCtr *Container, err error) { | ||
containersVisited := map[string]int{c.config.ID: 1} | ||
nextCtr := c.config.NetNsCtr | ||
for nextCtr != "" { | ||
// Make sure we aren't in a loop | ||
if _, visited := containersVisited[nextCtr]; visited { | ||
return nil, errors.New("loop encountered while determining net namespace container") | ||
} | ||
containersVisited[nextCtr] = 1 | ||
|
||
depCtr, err = c.runtime.state.Container(nextCtr) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "error fetching dependency %s of container %s", c.config.NetNsCtr, c.ID()) | ||
} | ||
// This should never happen without an error | ||
if depCtr == nil { | ||
break | ||
} | ||
nextCtr = depCtr.config.NetNsCtr | ||
} | ||
|
||
if depCtr == nil { | ||
return nil, errors.New("unexpected error depCtr is nil without reported error from runtime state") | ||
} | ||
return depCtr, nil | ||
} | ||
|
||
// Make standard bind mounts to include in the container | ||
func (c *Container) makeBindMounts() error { | ||
if err := os.Chown(c.state.RunDir, c.RootUID(), c.RootGID()); err != nil { | ||
|
@@ -1396,24 +1424,9 @@ func (c *Container) makeBindMounts() error { | |
// We want /etc/resolv.conf and /etc/hosts from the | ||
// other container. Unless we're not creating both of | ||
// them. | ||
var ( | ||
depCtr *Container | ||
nextCtr string | ||
) | ||
|
||
// I don't like infinite loops, but I don't think there's | ||
// a serious risk of looping dependencies - too many | ||
// protections against that elsewhere. | ||
nextCtr = c.config.NetNsCtr | ||
for { | ||
depCtr, err = c.runtime.state.Container(nextCtr) | ||
if err != nil { | ||
return errors.Wrapf(err, "error fetching dependency %s of container %s", c.config.NetNsCtr, c.ID()) | ||
} | ||
nextCtr = depCtr.config.NetNsCtr | ||
if nextCtr == "" { | ||
break | ||
} | ||
depCtr, err := c.getRootNetNsDepCtr() | ||
if err != nil { | ||
return errors.Wrapf(err, "error fetching network namespace dependency container for container %s", c.ID()) | ||
} | ||
|
||
// We need that container's bind mounts | ||
|
@@ -1698,7 +1711,12 @@ func (c *Container) generateResolvConf() (string, error) { | |
nameservers = resolvconf.GetNameservers(resolv.Content) | ||
// slirp4netns has a built in DNS server. | ||
if c.config.NetMode.IsSlirp4netns() { | ||
nameservers = append([]string{slirp4netnsDNS}, nameservers...) | ||
slirp4netnsDNS, err := GetSlirp4netnsDNS(c.slirp4netnsSubnet) | ||
if err != nil { | ||
logrus.Warn("failed to determine Slirp4netns DNS: ", err.Error()) | ||
} else { | ||
nameservers = append([]string{slirp4netnsDNS.String()}, nameservers...) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -1779,7 +1797,12 @@ func (c *Container) getHosts() string { | |
if c.Hostname() != "" { | ||
if c.config.NetMode.IsSlirp4netns() { | ||
// When using slirp4netns, the interface gets a static IP | ||
hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", slirp4netnsIP, c.Hostname(), c.config.Name) | ||
slirp4netnsIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) | ||
if err != nil { | ||
logrus.Warn("failed to determine slirp4netnsIP: ", err.Error()) | ||
} else { | ||
hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", slirp4netnsIP.String(), c.Hostname(), c.config.Name) | ||
} | ||
} else { | ||
hasNetNS := false | ||
netNone := false | ||
|
@@ -1802,6 +1825,36 @@ func (c *Container) getHosts() string { | |
} | ||
} | ||
} | ||
|
||
// Add gateway entry | ||
var depCtr *Container | ||
if c.config.NetNsCtr != "" { | ||
// ignoring the error because there isn't anything to do | ||
depCtr, _ = c.getRootNetNsDepCtr() | ||
} else if len(c.state.NetworkStatus) != 0 { | ||
depCtr = c | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This last block is not necessary. depCtr will be nil by default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure syntactically how to get rid of that block 😬 I added it because I wanted to ensure mutual exclusion between shared container network namespace configurations and containers with network namespaces created for them. Basically I didn't want the situation where
But If that is not possible / something to worry about I can make them both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rhatdan just in case you didn't see my response. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that is fine. |
||
depCtr = nil | ||
} | ||
|
||
if depCtr != nil { | ||
for _, pluginResultsRaw := range depCtr.state.NetworkStatus { | ||
pluginResult, _ := cnitypes.GetResult(pluginResultsRaw) | ||
for _, ip := range pluginResult.IPs { | ||
hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway) | ||
} | ||
} | ||
} else if c.config.NetMode.IsSlirp4netns() { | ||
gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) | ||
if err != nil { | ||
logrus.Warn("failed to determine gatewayIP: ", err.Error()) | ||
} else { | ||
hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String()) | ||
} | ||
} else { | ||
logrus.Debug("network configuration does not support host.containers.internal address") | ||
} | ||
|
||
return hosts | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,16 +37,12 @@ import ( | |
) | ||
|
||
const ( | ||
// slirp4netnsIP is the IP used by slirp4netns to configure the tap device | ||
// inside the network namespace. | ||
slirp4netnsIP = "10.0.2.100" | ||
|
||
// slirp4netnsDNS is the IP for the built-in DNS server in the slirp network | ||
slirp4netnsDNS = "10.0.2.3" | ||
|
||
// slirp4netnsMTU the default MTU override | ||
slirp4netnsMTU = 65520 | ||
|
||
// default slirp4ns subnet | ||
defaultSlirp4netnsSubnet = "10.0.2.0/24" | ||
|
||
// rootlessCNINSName is the file name for the rootless network namespace bind mount | ||
rootlessCNINSName = "rootless-cni-ns" | ||
) | ||
|
@@ -360,15 +356,20 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { | |
} | ||
|
||
// build a new resolv.conf file which uses the slirp4netns dns server address | ||
resolveIP := slirp4netnsDNS | ||
resolveIP, err := GetSlirp4netnsDNS(nil) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to determine default slirp4netns DNS address") | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The block below here should also use GetSlirp4netnsDNS for consistency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed: ef6027b2db9d7ebd4a294af002c08c0c93d0e5db |
||
if netOptions.cidr != "" { | ||
_, cidr, err := net.ParseCIDR(netOptions.cidr) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to parse slirp4netns cidr") | ||
} | ||
// the slirp dns ip is always the third ip in the subnet | ||
cidr.IP[len(cidr.IP)-1] = cidr.IP[len(cidr.IP)-1] + 3 | ||
resolveIP = cidr.IP.String() | ||
resolveIP, err = GetSlirp4netnsDNS(cidr) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to determine slirp4netns DNS address from cidr: %s", cidr.String()) | ||
} | ||
} | ||
conf, err := resolvconf.Get() | ||
if err != nil { | ||
|
@@ -377,7 +378,7 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { | |
searchDomains := resolvconf.GetSearchDomains(conf.Content) | ||
dnsOptions := resolvconf.GetOptions(conf.Content) | ||
|
||
_, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP}, searchDomains, dnsOptions) | ||
_, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP.String()}, searchDomains, dnsOptions) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf") | ||
} | ||
|
@@ -577,7 +578,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error { | |
// set up port forwarder for CNI-in-slirp4netns | ||
netnsPath := ctr.state.NetNS.Path() | ||
// TODO: support slirp4netns port forwarder as well | ||
return r.setupRootlessPortMappingViaRLK(ctr, netnsPath, "") | ||
return r.setupRootlessPortMappingViaRLK(ctr, netnsPath) | ||
} | ||
return nil | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,6 +162,27 @@ load helpers | |
done | ||
} | ||
|
||
@test "podman run with slirp4ns assigns correct gateway address to host.containers.internal" { | ||
CIDR="$(random_rfc1918_subnet)" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Much better, thank you, but I'm kind of uncomfortable now with the variable name: a CIDR is defined as a full subnet, slash, and mask. A future maintainer may find this a stumbling point, and waste time (e.g. on line 169) wondering how a CIDR can have |
||
run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ | ||
$IMAGE grep 'host.containers.internal' /etc/hosts | ||
is "$output" "${CIDR}.2 host.containers.internal" "host.containers.internal should be the cidr+2 address" | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not feeling this behavior is correct. I suggest exposing the "real" host IP regardless to rootful/rootless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So what would be the alternative? Are you suggesting to derive all interface ip addresses not including The issue (#5651) was to add behavior that reflects docker's I had a look through some of the docker code and as far as I can tell that is a configuration option given to the daemon so if a user asks for the host gateway address to be added to their container it is added based on that configuration docker-ce reference So should podman have a similar configuration option? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The first interface (eth0) would be fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about this solution. There can be so many different interfaces in so many different configurations that trying to derive an ip address from one of the interfaces seems unstable. I don't necessarily disagree that exposing the loopback interface to the container could result in a container breakout but this change doesn't give that access it simply puts a Like I said above. I think the only viable alternative is to make the interface a configurable option similar to dockerd's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AkihiroSuda bump in case you didn't see my last response There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having an option SGTM |
||
|
||
@test "podman run with slirp4ns adds correct dns address to resolv.conf" { | ||
CIDR="$(random_rfc1918_subnet)" | ||
run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ | ||
$IMAGE grep "${CIDR}" /etc/resolv.conf | ||
is "$output" "nameserver ${CIDR}.3" "resolv.conf should have slirp4netns cidr+3 as a nameserver" | ||
} | ||
|
||
@test "podman run with slirp4ns assigns correct ip address container" { | ||
CIDR="$(random_rfc1918_subnet)" | ||
run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ | ||
$IMAGE sh -c "ip address | grep ${CIDR}" | ||
is "$output" ".*inet ${CIDR}.100/24 \+" "container should have slirp4netns cidr+100 assigned to interface" | ||
} | ||
|
||
# "network create" now works rootless, with the help of a special container | ||
@test "podman network create" { | ||
myport=54322 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you note that this returns nil if there is no dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this can be null unless there is an error, although I could be wrong. So would you like a note about it returning an nil on error? That being said I am adding some extra error handling in that function based on your comment, but it wont make sense if
State.Container
interface function can returnnil, nil
.