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

Port forwarding doesn't work for non-127.0.0.1 localhost #3539

Closed
matthew-nichols opened this issue Oct 14, 2024 · 8 comments
Closed

Port forwarding doesn't work for non-127.0.0.1 localhost #3539

matthew-nichols opened this issue Oct 14, 2024 · 8 comments
Labels
area/network area/rootless Rootless mode question Further information is requested

Comments

@matthew-nichols
Copy link

Description

nerdctl run --rm -p 127.0.0.2:8080:80 nginx does not work as expected:

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

nerdctl run --rm -p 127.0.0.1:8080:80 nginx does work, and outputs the appropriate nginx welcome page from curl 127.0.0.1:8080

Can confirm (on another Linux box) that the same functionality (listen on 127.0.0.2:8080) works in rootless Podman.

Note: the output for nerdctl port in general, seems reversed to what I would expect but contains the right address:

$ nerdctl port nginx
80/tcp -> 127.0.0.2:8080
$ nerdctl port nginx
80/tcp -> 127.0.0.1:8080

ss also confirms something is listening on the right address:

$ sudo ss -tupln | grep 8080
tcp   LISTEN 0      4096            127.0.0.2:8080       0.0.0.0:*    users:(("rootlesskit",pid=1820,fd=30))

Steps to reproduce the issue

1.Install and setup rootless nerdctl
2.Run nerdctl run --rm -p 127.0.0.2:8080:80 nginx
3.Attempt curl 127.0.0.2:8080
4.Get reset by peer

Describe the results you received and expected

Received: reset by peer
Expected: to be connected to the service in the container

What version of nerdctl are you using?

nerdctl version 2.0.0-rc.2, rootless, Linux (Pop!_OS 22.04 LTS), install from tar

Are you using a variant of nerdctl? (e.g., Rancher Desktop)

None

Host information

$ nerdctl info
Client:
 Namespace:	default
 Debug Mode:	false

Server:
 Server Version: v2.0.0-rc.4
 Storage Driver: overlayfs
 Logging Driver: json-file
  Cgroup Driver:  : systemd
  Cgroup Version: : 2
 Plugins:
  Log:     fluentd journald json-file syslog
  Storage: native overlayfs stargz
 Security Options:
  apparmor
  seccomp
   Profile:	builtin
  cgroupns
  rootless
 Kernel Version:   6.9.3-76060903-generic
 Operating System: Pop!_OS 22.04 LTS
 OSType:           linux
 Architecture:     x86_64
 CPUs:             16
 Total Memory:     30.56GiB
 Name:             hostname
 ID:               2eb24a77-0449-485d-b84a-e18aca278b1e

WARNING: AppArmor profile "nerdctl-default" is not loaded.
         Use 'sudo nerdctl apparmor load' if you prefer to use AppArmor with rootless mode.
         This warning is negligible if you do not intend to use AppArmor.
WARNING: No cpu cfs period support
WARNING: No cpu cfs quota support
WARNING: No cpu shares support
WARNING: No cpuset support
WARNING: IPv4 forwarding is disabled
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
@matthew-nichols matthew-nichols added the kind/unconfirmed-bug-claim Unconfirmed bug claim label Oct 14, 2024
@AkihiroSuda AkihiroSuda added bug Something isn't working area/rootless Rootless mode area/network and removed kind/unconfirmed-bug-claim Unconfirmed bug claim labels Oct 15, 2024
@apostasie
Copy link
Contributor

@AkihiroSuda tag expert?

From a glance, I am not sure this can be solved inside nerdctl.

@AkihiroSuda
Copy link
Member

This is probably a bug of https://github.com/rootless-containers/rootlesskit.

Workarounds:

  1. Set Environment="CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns" https://rootlesscontaine.rs/getting-started/containerd/#changing-the-port-forwarder
  2. Or, use rootful mode (sudo nerdctl)

@fahedouch
Copy link
Member

fahedouch commented Dec 7, 2024

This is probably a bug of https://github.com/rootless-containers/rootlesskit.

Workarounds:

  1. Set Environment="CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns" https://rootlesscontaine.rs/getting-started/containerd/#changing-the-port-forwarder
  2. Or, use rootful mode (sudo nerdctl)

it is not bug. The default ROOTLESSKIT_PORT_DRIVER is builtin which limits network functionality by not propagating source IP addresses. Can we consider slirp4netns as default driver which provide more realistic network behavior ?

@fahedouch fahedouch added question Further information is requested and removed bug Something isn't working expert labels Dec 10, 2024
@haytok
Copy link
Contributor

haytok commented Dec 21, 2024

Hi, @AkihiroSuda @fahedouch

In the current implementation, when --port-driver is specified as buildin, Rootlesskit Parent NetNS accept() external connections for the address or 127.0.0.1 and port number specified by -p.

On the other hand, Rootlesskit Child NetNS will send the request to 127.0.0.1 regardless of the ip by accept() in Parent NetNS.

However, that is the expected behavior as described in the document below.

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.

Question

I think propagation of the ip that is accept() in Parent NetNS to Child NetNS will solve this problem, so I'll create a PR in rootlesskit for propagation of parent ip to improve the behavior of this issue. What do you think?

@AkihiroSuda
Copy link
Member

Thanks, does your PR pass the nerdctl CI?
LGTM then.
(The proper LGTM will be after reviewing the actual code)

@fahedouch
Copy link
Member

Thanks for your efforts but I'm not sure how you plan to forward traffic from a child network namespace to the host network while preserving the source IP address when using the builtin driver with detached network namespace

@haytok
Copy link
Contributor

haytok commented Dec 22, 2024

Thanks for comments !!!

does your PR pass the nerdctl CI?

When I locally apply the modification that is trying to create a PR, curl 127.0.0.2:8080 confirms that it succeeds.

Details

[ec2-user@ip-172-31-40-91 rootlesskit]$ make && sudo make install
make: Nothing to be done for 'all'.
install -D -m 755 /home/ec2-user/rootlesskit/bin/rootlesskit /usr/local/bin/rootlesskit
install -D -m 755 /home/ec2-user/rootlesskit/bin/rootlessctl /usr/local/bin/rootlessctl
install -D -m 755 /home/ec2-user/rootlesskit/bin/rootlesskit-docker-proxy /usr/local/bin/rootlesskit-docker-proxy
[ec2-user@ip-172-31-40-91 rootlesskit]$ sudo systemctl restart user@1000.service
[ec2-user@ip-172-31-40-91 rootlesskit]$ n rm -f nginx && nerdctl run -d --name nginx -p 127.0.0.2:8080:80 nginx && nerdctl ps
nginx
72824134f1fb2d642ce386642f7229316a400ce927025605c4f4decc5cf3a7fd
CONTAINER ID    IMAGE                             COMMAND                   CREATED                   STATUS    PORTS                     NAMES
72824134f1fb    docker.io/library/nginx:latest    "/docker-entrypoint.…"    Less than a second ago    Up        127.0.0.2:8080->80/tcp    nginx
[ec2-user@ip-172-31-40-91 rootlesskit]$ curl 127.0.0.2:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

As for CI, I haven't been able to confirm this yet, so I'll check.


I'm not sure how you plan to forward traffic from a child network namespace to the host network while preserving the source IP address when using the builtin driver with detached network namespace

When Parent NetNS sends a request to Child NetNS, the following processing is performed.

This spec variable has ParentIP, I think it is enough to propagate it to Child NetNS.

I haven't written the PR yet, but the diff is as follows.

Details

[ec2-user@ip-172-31-40-91 rootlesskit]$ gd
diff --git a/pkg/port/builtin/child/child.go b/pkg/port/builtin/child/child.go
index 83ab638..ebcdc67 100644
--- a/pkg/port/builtin/child/child.go
+++ b/pkg/port/builtin/child/child.go
@@ -122,7 +122,8 @@ func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) er
        var dialer net.Dialer
        ip := req.IP
        if ip == "" {
-               ip = "127.0.0.1"
+               // ip = "127.0.0.1"
+               ip = req.ParentIP
        } else {
                p := net.ParseIP(ip)
                if p == nil {
diff --git a/pkg/port/builtin/msg/msg.go b/pkg/port/builtin/msg/msg.go
index f77468e..4b07161 100644
--- a/pkg/port/builtin/msg/msg.go
+++ b/pkg/port/builtin/msg/msg.go
@@ -19,10 +19,11 @@ 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
 }

 // Reply may contain FD as OOB
@@ -52,10 +53,11 @@ func Initiate(c *net.UnixConn) error {
 // 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,
        }
        if _, err := lowlevelmsgutil.MarshalToWriter(c, &req); err != nil {
                return 0, err

haytok added a commit to haytok/rootlesskit that referenced this issue Jan 3, 2025
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>
haytok added a commit to haytok/nerdctl that referenced this issue Jan 22, 2025
…in rootless mode

When running a rootless container, specifying an address such as 127.0.0.2
for the -p option, we will not be able to access the published container
through that address.

This behavior is reported in the following issue:

- containerd#3539

This behavior is caused by the behavior in rootlesskit, and the following
pull request has been made to improve the behavior.

- rootless-containers/rootlesskit#477

Therefore, this commit adds a test to ensure that the behavior of the
issue has been improved.

Signed-off-by: Hayato Kiwata <haytok@amazon.co.jp>
haytok added a commit to haytok/nerdctl that referenced this issue Jan 25, 2025
…in rootless mode

When running a rootless container, specifying an address such as 127.0.0.2
for the -p option, we will not be able to access the published container
through that address.

This behavior is reported in the following issue:

- containerd#3539

This behavior is caused by the behavior in rootlesskit, and the following
pull request has been made to improve the behavior.

- rootless-containers/rootlesskit#477

Therefore, this commit adds a test to ensure that the behavior of the
issue has been improved.

Signed-off-by: Hayato Kiwata <haytok@amazon.co.jp>
haytok added a commit to haytok/nerdctl that referenced this issue Jan 25, 2025
…in rootless mode

When running a rootless container, specifying an address such as 127.0.0.2
for the -p option, we will not be able to access the published container
through that address.

This behavior is reported in the following issue:

- containerd#3539

This behavior is caused by the behavior in rootlesskit, and the following
pull request has been made to improve the behavior.

- rootless-containers/rootlesskit#477

Therefore, this commit adds a test to ensure that the behavior of the
issue has been improved.

Signed-off-by: Hayato Kiwata <haytok@amazon.co.jp>
haytok added a commit to haytok/nerdctl that referenced this issue Jan 28, 2025
…in rootless mode

When running a rootless container, specifying an address such as 127.0.0.2
for the -p option, we will not be able to access the published container
through that address.

This behavior is reported in the following issue:

- containerd#3539

This behavior is caused by the behavior in rootlesskit, and the following
pull request has been made to improve the behavior.

- rootless-containers/rootlesskit#477

Therefore, this commit adds a test to ensure that the behavior of the
issue has been improved.

Signed-off-by: Hayato Kiwata <haytok@amazon.co.jp>
@djdongjin
Copy link
Member

I believe this is fixed with #3831, closing. Feel free to reopen if you still see the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/network area/rootless Rootless mode question Further information is requested
Projects
None yet
Development

No branches or pull requests

6 participants