mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00
Merge pull request #9972 from bblenard/issue-5651-hostname-for-container-gateway
Add host.containers.internal entry into container's etc/hosts
This commit is contained in:
@ -126,6 +126,8 @@ type Container struct {
|
||||
|
||||
// This is true if a container is restored from a checkpoint.
|
||||
restoreFromCheckpoint bool
|
||||
|
||||
slirp4netnsSubnet *net.IPNet
|
||||
}
|
||||
|
||||
// ContainerState contains the current state of the container
|
||||
|
@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -38,16 +38,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"
|
||||
)
|
||||
@ -361,15 +357,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")
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -378,7 +379,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")
|
||||
}
|
||||
@ -578,7 +579,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
|
||||
}
|
||||
|
@ -308,15 +308,89 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
|
||||
_, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet)
|
||||
|
||||
// Set slirp4netnsSubnet addresses now that we are pretty sure the command executed
|
||||
if netOptions.cidr != "" {
|
||||
ipv4, ipv4network, err := net.ParseCIDR(netOptions.cidr)
|
||||
if err != nil || ipv4.To4() == nil {
|
||||
return errors.Errorf("invalid cidr %q", netOptions.cidr)
|
||||
}
|
||||
ctr.slirp4netnsSubnet = ipv4network
|
||||
}
|
||||
|
||||
if havePortMapping {
|
||||
if netOptions.isSlirpHostForward {
|
||||
return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
|
||||
}
|
||||
return r.setupRootlessPortMappingViaRLK(ctr, netnsPath, netOptions.cidr)
|
||||
return r.setupRootlessPortMappingViaRLK(ctr, netnsPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get expected slirp ipv4 address based on subnet. If subnet is null use default subnet
|
||||
// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
|
||||
func GetSlirp4netnsIP(subnet *net.IPNet) (*net.IP, error) {
|
||||
_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
|
||||
if subnet != nil {
|
||||
slirpSubnet = subnet
|
||||
}
|
||||
expectedIP, err := addToIP(slirpSubnet, uint32(100))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error calculating expected ip for slirp4netns")
|
||||
}
|
||||
return expectedIP, nil
|
||||
}
|
||||
|
||||
// Get expected slirp Gateway ipv4 address based on subnet
|
||||
// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
|
||||
func GetSlirp4netnsGateway(subnet *net.IPNet) (*net.IP, error) {
|
||||
_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
|
||||
if subnet != nil {
|
||||
slirpSubnet = subnet
|
||||
}
|
||||
expectedGatewayIP, err := addToIP(slirpSubnet, uint32(2))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error calculating expected gateway ip for slirp4netns")
|
||||
}
|
||||
return expectedGatewayIP, nil
|
||||
}
|
||||
|
||||
// Get expected slirp DNS ipv4 address based on subnet
|
||||
// Reference: https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md#description
|
||||
func GetSlirp4netnsDNS(subnet *net.IPNet) (*net.IP, error) {
|
||||
_, slirpSubnet, _ := net.ParseCIDR(defaultSlirp4netnsSubnet)
|
||||
if subnet != nil {
|
||||
slirpSubnet = subnet
|
||||
}
|
||||
expectedDNSIP, err := addToIP(slirpSubnet, uint32(3))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error calculating expected dns ip for slirp4netns")
|
||||
}
|
||||
return expectedDNSIP, nil
|
||||
}
|
||||
|
||||
// Helper function to calculate slirp ip address offsets
|
||||
// Adapted from: https://github.com/signalsciences/ipv4/blob/master/int.go#L12-L24
|
||||
func addToIP(subnet *net.IPNet, offset uint32) (*net.IP, error) {
|
||||
// I have no idea why I have to do this, but if I don't ip is 0
|
||||
ipFixed := subnet.IP.To4()
|
||||
|
||||
ipInteger := uint32(ipFixed[3]) | uint32(ipFixed[2])<<8 | uint32(ipFixed[1])<<16 | uint32(ipFixed[0])<<24
|
||||
ipNewRaw := ipInteger + offset
|
||||
// Avoid overflows
|
||||
if ipNewRaw < ipInteger {
|
||||
return nil, errors.Errorf("integer overflow while calculating ip address offset, %s + %d", ipFixed, offset)
|
||||
}
|
||||
ipNew := net.IPv4(byte(ipNewRaw>>24), byte(ipNewRaw>>16&0xFF), byte(ipNewRaw>>8)&0xFF, byte(ipNewRaw&0xFF))
|
||||
if !subnet.Contains(ipNew) {
|
||||
return nil, errors.Errorf("calculated ip address %s is not within given subnet %s", ipNew.String(), subnet.String())
|
||||
}
|
||||
return &ipNew, nil
|
||||
}
|
||||
|
||||
func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout time.Duration) error {
|
||||
prog := filepath.Base(cmd.Path)
|
||||
if len(cmd.Args) > 0 {
|
||||
@ -363,7 +437,7 @@ func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath, slirp4CIDR string) error {
|
||||
func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) error {
|
||||
syncR, syncW, err := os.Pipe()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to open pipe")
|
||||
@ -390,17 +464,11 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath, slir
|
||||
}
|
||||
}
|
||||
|
||||
childIP := slirp4netnsIP
|
||||
// set the correct childIP when a custom cidr is set
|
||||
if slirp4CIDR != "" {
|
||||
_, cidr, err := net.ParseCIDR(slirp4CIDR)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse slirp4netns cidr")
|
||||
}
|
||||
// the slirp container ip is always the hundredth ip in the subnet
|
||||
cidr.IP[len(cidr.IP)-1] = cidr.IP[len(cidr.IP)-1] + 100
|
||||
childIP = cidr.IP.String()
|
||||
slirp4netnsIP, err := GetSlirp4netnsIP(ctr.slirp4netnsSubnet)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get slirp4ns ip")
|
||||
}
|
||||
childIP := slirp4netnsIP.String()
|
||||
outer:
|
||||
for _, r := range ctr.state.NetworkStatus {
|
||||
for _, i := range r.IPs {
|
||||
|
@ -162,6 +162,27 @@ load helpers
|
||||
done
|
||||
}
|
||||
|
||||
@test "podman run with slirp4ns assigns correct gateway address to host.containers.internal" {
|
||||
CIDR="$(random_rfc1918_subnet)"
|
||||
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"
|
||||
}
|
||||
|
||||
@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
|
||||
|
Reference in New Issue
Block a user