Skip to content

Commit

Permalink
fix: allow to propagate the address specified in -p option
Browse files Browse the repository at this point in the history
When containerd is running as a root user and an address other than
`127.0.0.1`, such as `127.0.0.2`, is specified in `-p` when `nerdctl run`
is used to start a container, the combination of the address and port
number allows connections to an application running in the container.

Specifically, `curl 127.0.0.2:8080` will access a nginx application
running on port `80` in the container created by the following command.

```
$ sudo nerdctl run --name nginx -d -p 127.0.0.2:8080:80 nginx
7abce48d42ef809613365ffaa54d9526e8388c78f5f5c2d8a2850628543ca27e

$ sudo nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED          STATUS    PORTS                     NAMES
7abce48d42ef    docker.io/library/nginx:latest    "/docker-entrypoint.…"    3 seconds ago    Up        127.0.0.2:8080->80/tcp    nginx

$ curl 127.0.0.2:8080
<!DOCTYPE html>
...
```

On the other hand, when running containerd as a non-root user, similarly,
suppose we start a container with an address other than `127.0.0.1`, such
as `127.0.0.2` for -p.
In this case, curl `127.0.0.2:8080` will not connect to a nginx
application running inside the container.

Details are described below.

```
$ nerdctl run --name nginx -d -p 127.0.0.2:8080:80 nginx
7687f9612afe6847fb3946254718fafef935ffdc05f9cbb4496fc40dd35a6abc

$ nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED          STATUS    PORTS                     NAMES
7687f9612afe    docker.io/library/nginx:latest    "/docker-entrypoint.…"    5 seconds ago    Up        127.0.0.2:8080->80/tcp    nginx

$ curl 127.0.0.2:8080
curl: (56) Recv failure: Connection reset by peer
```

When a connection is requested from the outside to the container in Parent
NetNS, the rootlesskit port driver relays the connection from Parent
NetNS to Child NetNS.
When, for example, an address such as `127.0.0.2` is specified in -p,
Child NetNS need to establish a connection to the specified address.

However, the current implementation will establish a connection to
`127.0.0.1` even when `127.0.0.2` is specified in -p.

The behavior of not propagating the address specified by -p is assumed at
this time, as described in the following document.

- https://github.com/rootless-containers/rootlesskit/blob/master/docs/port.md#port-drivers

> --port-driver 	Throughput 	Source IP
> ...
> builtin 	30.0 Gbps 	Always 127.0.0.1
> ...
> The builtin driver is fast, but be aware that the source IP is not propagated and always set to 127.0.0.1.

However, an issue to improve this behavior is reported below.

- containerd/nerdctl#3539

Therefore, this commit fixes when running containerd as a non-root user,
the address specified in -p will be propagated.

Signed-off-by: Hayato Kiwata <haytok@amazon.co.jp>
  • Loading branch information
haytok committed Jan 3, 2025
1 parent 9c9049a commit 2363620
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
5 changes: 5 additions & 0 deletions pkg/port/builtin/child/child.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) er
ip := req.IP
if ip == "" {
ip = "127.0.0.1"
if req.ParentIP != "" {
if req.ParentIP != req.HostGatewayIP && req.ParentIP != "0.0.0.0" {
ip = req.ParentIP
}
}
} else {
p := net.ParseIP(ip)
if p == nil {
Expand Down
37 changes: 29 additions & 8 deletions pkg/port/builtin/msg/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ const (

// Request and Response are encoded as JSON with uint32le length header.
type Request struct {
Type string // "init" or "connect"
Proto string // "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"
IP string
Port int
Type string // "init" or "connect"
Proto string // "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6"
IP string
Port int
ParentIP string
HostGatewayIP string
}

// Reply may contain FD as OOB
Expand All @@ -48,14 +50,33 @@ func Initiate(c *net.UnixConn) error {
return c.CloseRead()
}

func hostGatewayIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}

for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}

return ""
}

// ConnectToChild connects to the child UNIX socket, and obtains TCP or UDP socket FD
// that corresponds to the port spec.
func ConnectToChild(c *net.UnixConn, spec port.Spec) (int, error) {
req := Request{
Type: RequestTypeConnect,
Proto: spec.Proto,
Port: spec.ChildPort,
IP: spec.ChildIP,
Type: RequestTypeConnect,
Proto: spec.Proto,
Port: spec.ChildPort,
IP: spec.ChildIP,
ParentIP: spec.ParentIP,
HostGatewayIP: hostGatewayIP(),
}
if _, err := lowlevelmsgutil.MarshalToWriter(c, &req); err != nil {
return 0, err
Expand Down

0 comments on commit 2363620

Please sign in to comment.