Docker port fowarding working on IPv6 but not IPv4

Viewed 3901

I have the following very simple docker-compose.yml, running on a Mac:

version: "3.7"
services:
  apache:
    image: httpd:2.4.41
    ports:
      - 80:80

I run docker-compose up, then I run this curl and Apache returns content:

/tmp/test $ curl -v http://localhost
*   Trying ::1:80...
* TCP_NODELAY set
* Connected to localhost (::1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.66.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sat, 26 Oct 2019 18:30:03 GMT
< Server: Apache/2.4.41 (Unix)
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html
< 
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host localhost left intact

However, if I try to access the container using 127.0.0.1 instead of localhost, I get connection refused:

/tmp/test $ curl -v http://127.0.0.1
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connection failed
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to 127.0.0.1 port 80: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused

Localhost does point to 127.0.0.1:

/tmp/test $ ping localhost
PING localhost (127.0.0.1): 56 data bytes

And netstat shows all local IP addresses port 80 to be forwarded:

/tmp/test $ netstat -tna | grep 80
...
tcp46      0      0  *.80                   *.*                    LISTEN     
...

I came to this actually trying to access the container using a custom domain I had on my /etc/hosts file pointing to 127.0.0.1. I thought there was something wrong with that domain name, but then I tried 127.0.0.1 and didn't work either, so I'm concluding there is something very basic about docker I'm not doing right.

Why is curl http://localhost working but curl http://127.0.0.1 is not?

UPDATE

It seems localhost is resolving to IPv6 ::1, so port forwarding seems to be working on IPv6 but not IPv4 addresses. Does that make any sense?

UPDATE 2

I wasn't able to fix it, but pointing my domain name to ::1 instead of 127.0.0.1 in my /etc/hosts serves as a workaround for the time being.

UPDATE 3

8 months later I bumped into the same issue and found my own question here, still unanswered. But this time I can't apply the same workaround, because I need to bind the port forwarding to my IPv4 address so it can be accessed from other hosts.

1 Answers

Found the culprit: pfctl

AFAIK, pfctl is not supposed to run automatically but my /System/Library/LaunchDaemons/com.apple.pfctl.plist said otherwise.

The Packet Filtering was configured to redirect all incoming traffic on port 80 to 8080, and 443 to 8443. And this is done without any process actually listening to port 80 and 443, that's why lsof and netstat wouldn't show anything,.

/Library/LaunchDaemons/it.winged.httpdfwd.plist has the following

 <key>ProgramArguments</key>
 <array>
  <string>sh</string>
  <string>-c</string>
  <string>echo "rdr pass proto tcp from any to any port {80,8080} -> 127.0.0.1 port 8080" | pfctl -a "com.apple/260.HttpFwdFirewall" -Ef - &amp;&amp; echo "rdr pass proto tcp from any to any port {443,8443} -> 127.0.0.1 port 8443" | pfctl -a "com.apple/261.HttpFwdFirewall" -Ef - &amp;&amp; sysctl -w net.inet.ip.forwarding=1</string>
 </array>
 <key>RunAtLoad</key>

The solution was simply to listen on ports 8080 and 8443. All requests to ports 80 and 443 are now being redirected transparently.

While debugging this I found countless open questions about similar problems without answers. I hope this helps somebody.

Related