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

support outbound-addr #7216

Merged
merged 1 commit into from
Aug 9, 2020
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
11 changes: 8 additions & 3 deletions docs/source/markdown/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,14 @@ Valid values are:
- `ns:<path>`: path to a network namespace to join
- `private`: create a new namespace for the container (default)
- `slirp4netns[:OPTIONS,...]`: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
**port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default.
**port_handler=slirp4netns**: Use the slirp4netns port forwarding.
**allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default to false.
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
- **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`).
- **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only).
- **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp should bind to.
- **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only).
- **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to.
- **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default.
- **port_handler=slirp4netns**: Use the slirp4netns port forwarding.

**--network-alias**=*alias*

Expand Down
91 changes: 81 additions & 10 deletions libpod/networking_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ type slirpFeatures struct {
HasMTU bool
HasEnableSandbox bool
HasEnableSeccomp bool
HasOutboundAddr bool
HasIPv6 bool
}

type slirp4netnsCmdArg struct {
Expand All @@ -197,6 +199,8 @@ func checkSlirpFlags(path string) (*slirpFeatures, error) {
HasMTU: strings.Contains(string(out), "--mtu"),
HasEnableSandbox: strings.Contains(string(out), "--enable-sandbox"),
HasEnableSeccomp: strings.Contains(string(out), "--enable-seccomp"),
HasOutboundAddr: strings.Contains(string(out), "--outbound-addr"),
HasIPv6: strings.Contains(string(out), "--enable-ipv6"),
}, nil
}

Expand Down Expand Up @@ -225,21 +229,64 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {

isSlirpHostForward := false
disableHostLoopback := true
enableIPv6 := false
outboundAddr := ""
outboundAddr6 := ""

if ctr.config.NetworkOptions != nil {
slirpOptions := ctr.config.NetworkOptions["slirp4netns"]
for _, o := range slirpOptions {
switch o {
case "port_handler=slirp4netns":
isSlirpHostForward = true
case "port_handler=rootlesskit":
isSlirpHostForward = false
case "allow_host_loopback=true":
disableHostLoopback = false
case "allow_host_loopback=false":
disableHostLoopback = true
parts := strings.Split(o, "=")
option, value := parts[0], parts[1]

switch option {
case "port_handler":
switch value {
case "slirp4netns":
isSlirpHostForward = true
case "rootlesskit":
isSlirpHostForward = false
default:
return errors.Errorf("unknown port_handler for slirp4netns: %q", value)
}
case "allow_host_loopback":
switch value {
case "true":
disableHostLoopback = false
case "false":
disableHostLoopback = true
default:
return errors.Errorf("invalid value of allow_host_loopback for slirp4netns: %q", value)
}
case "enable_ipv6":
5eraph marked this conversation as resolved.
Show resolved Hide resolved
switch value {
case "true":
enableIPv6 = true
case "false":
enableIPv6 = false
default:
return errors.Errorf("invalid value of enable_ipv6 for slirp4netns: %q", value)
}
case "outbound_addr":
ipv4 := net.ParseIP(value)
if ipv4 == nil || ipv4.To4() == nil {
_, err := net.InterfaceByName(value)
if err != nil {
return errors.Errorf("invalid outbound_addr %q", value)
}
}
outboundAddr = value
case "outbound_addr6":
ipv6 := net.ParseIP(value)
if ipv6 == nil || ipv6.To4() != nil {
_, err := net.InterfaceByName(value)
if err != nil {
return errors.Errorf("invalid outbound_addr6: %q", value)
}
}
outboundAddr6 = value
default:
return errors.Errorf("unknown option for slirp4netns: %q", o)

}
}
}
Expand All @@ -262,6 +309,30 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
cmdArgs = append(cmdArgs, "--enable-seccomp")
}

if enableIPv6 {
if !slirpFeatures.HasIPv6 {
return errors.Errorf("enable_ipv6 not supported")
}
cmdArgs = append(cmdArgs, "--enable-ipv6")
}

if outboundAddr != "" {
if !slirpFeatures.HasOutboundAddr {
return errors.Errorf("outbound_addr not supported")
}
cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr=%s", outboundAddr))
}

if outboundAddr6 != "" {
if !slirpFeatures.HasOutboundAddr || !slirpFeatures.HasIPv6 {
return errors.Errorf("outbound_addr6 not supported")
}
if !enableIPv6 {
return errors.Errorf("enable_ipv6=true is required for outbound_addr6")
}
cmdArgs = append(cmdArgs, fmt.Sprintf("--outbound-addr6=%s", outboundAddr6))
5eraph marked this conversation as resolved.
Show resolved Hide resolved
}

var apiSocket string
if havePortMapping && isSlirpHostForward {
apiSocket = filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
Expand Down
50 changes: 50 additions & 0 deletions test/e2e/run_networking_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package integration

import (
"fmt"
"os"
"strings"

. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/uber/jaeger-client-go/utils"
)

var _ = Describe("Podman run networking", func() {
Expand Down Expand Up @@ -278,6 +281,53 @@ var _ = Describe("Podman run networking", func() {
Expect(session.ExitCode()).To(Equal(0))
})

It("podman run network bind to 127.0.0.1", func() {
slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"})
Expect(slirp4netnsHelp.ExitCode()).To(Equal(0))
networkConfiguration := "slirp4netns:outbound_addr=127.0.0.1,allow_host_loopback=true"

if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") {
ncListener := StartSystemExec("nc", []string{"-v", "-n", "-l", "-p", "8083"})
session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8083"})
session.Wait(30)
ncListener.Wait(30)

Expect(session.ExitCode()).To(Equal(0))
Expect(ncListener.ExitCode()).To(Equal(0))
Expect(ncListener.ErrorToString()).To(ContainSubstring("127.0.0.1"))
} else {
session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8083"})
session.Wait(30)
Expect(session.ExitCode()).ToNot(Equal(0))
Expect(session.ErrorToString()).To(ContainSubstring("outbound_addr not supported"))
}
})

It("podman run network bind to HostIP", func() {
ip, err := utils.HostIP()
Expect(err).To(BeNil())

slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"})
Expect(slirp4netnsHelp.ExitCode()).To(Equal(0))
networkConfiguration := fmt.Sprintf("slirp4netns:outbound_addr=%s,allow_host_loopback=true", ip.String())

if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") {
ncListener := StartSystemExec("nc", []string{"-v", "-n", "-l", "-p", "8084"})
session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8084"})
session.Wait(30)
ncListener.Wait(30)

Expect(session.ExitCode()).To(Equal(0))
Expect(ncListener.ExitCode()).To(Equal(0))
Expect(ncListener.ErrorToString()).To(ContainSubstring(ip.String()))
} else {
session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8084"})
session.Wait(30)
Expect(session.ExitCode()).ToNot(Equal(0))
Expect(session.ErrorToString()).To(ContainSubstring("outbound_addr not supported"))
}
})

It("podman run network expose ports in image metadata", func() {
session := podmanTest.Podman([]string{"create", "--name", "test", "-dt", "-P", nginx})
session.Wait(90)
Expand Down
10 changes: 10 additions & 0 deletions test/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,16 @@ func SystemExec(command string, args []string) *PodmanSession {
return &PodmanSession{session}
}

// StartSystemExec is used to start exec a system command
func StartSystemExec(command string, args []string) *PodmanSession {
c := exec.Command(command, args...)
session, err := gexec.Start(c, GinkgoWriter, GinkgoWriter)
if err != nil {
Fail(fmt.Sprintf("unable to run command: %s %s", command, strings.Join(args, " ")))
}
return &PodmanSession{session}
}

// StringInSlice determines if a string is in a string slice, returns bool
func StringInSlice(s string, sl []string) bool {
for _, i := range sl {
Expand Down