skip to Main Content

Recently we have been updating our Docker images to the latest version of Alpine 3.18.

With this change we noticed that our application was not able to send HTTP requests using cURL to other containers that are running on the same host. This was made possible by using the extra_hosts flag in our Docker Compose file.

Whilst debugging we noticed that Alpine recently switched to cURL version 8, specifically 8.2.1. In this update it feels like it is ignoring the /etc/hosts file, which is where Docker writes the extra_hosts configuration.

docker-compose.yml:

version: "3.8"

services:
    test:
        image: alpine:3.18
        extra_hosts:
            - example.localhost:host-gateway

Run container: docker-compose run test sh
Inside the container:

Installing cURL:

apk add curl

Installed version:

/ # curl --version
curl 8.2.1 (aarch64-alpine-linux-musl) libcurl/8.2.1 OpenSSL/3.1.2 zlib/1.2.13 brotli/1.0.9 libidn2/2.3.4 nghttp2/1.55.1
Release-Date: 2023-07-26
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB SSL threadsafe TLS-SRP UnixSockets

Execute cURL:

/ # curl -v example.localhost
* processing: example.localhost
*   Trying [::1]:80...
* Immediate connect fail for ::1: Address not available
*   Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to example.localhost port 80 after 0 ms: Couldn't connect to server
* Closing connection
curl: (7) Failed to connect to example.localhost port 80 after 0 ms: Couldn't connect to server

Which is odd since

/ # cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
192.168.65.254  example.localhost
172.20.0.3      76a7dfa28161

And

/ # ping example.localhost
PING example.localhost (192.168.65.254): 56 data bytes
64 bytes from 192.168.65.254: seq=0 ttl=62 time=2.224 ms
64 bytes from 192.168.65.254: seq=1 ttl=62 time=0.731 ms
64 bytes from 192.168.65.254: seq=2 ttl=62 time=0.322 ms
^C
--- example.localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.322/1.092/2.224 ms

Am I missing something?

2

Answers


  1. Chosen as BEST ANSWER

    Solved! As mentioned by @β.εηοιτ.βε, the problem was caused by the fact that I was using .localhost for my local development setup. cURL fairly recently started to automatically resolve all requests going to .localhost to 127.0.0.1. This makes cURL ignore /etc/hosts.


  2. The top level domain (TLD) .localhost should be treated as localhost, as per RFC 2606

    The ".localhost" TLD has traditionally been statically defined in
    host DNS implementations as having an A record pointing to the
    loop back IP address and is reserved for such use. Any other use
    would conflict with widely deployed code which assumes this use.

    Source: https://datatracker.ietf.org/doc/html/rfc2606#section-2

    This is further documented in RFC 6761

    The domain "localhost." and any names falling within ".localhost."
    are special in the following ways:

    1. Application software MAY recognize localhost names as special, or
      MAY pass them to name resolution APIs as they would for other
      domain names.

    Source: https://datatracker.ietf.org/doc/html/rfc6761#section-6.3

    Which seems to be the rational behind its implementation in CURL as per this pull request: https://github.com/curl/curl/pull/9296.


    This can be tested with the Dockerfile:

    FROM alpine:3.18
    
    RUN apk add --no-cache curl
    
    ENTRYPOINT [ "curl", "--verbose", "--silent", "--connect-timeout", "0.1" ]
    

    Running it with a .localhost TLD:

    $ docker run 
        --rm 
        --add-host example.localhost:93.184.216.34 
        $(docker build --quiet .) 
        example.localhost
    
    * processing: example.localhost
    *   Trying [::1]:80...
    * Immediate connect fail for ::1: Address not available
    *   Trying 127.0.0.1:80...
    * connect to 127.0.0.1 port 80 failed: Connection refused
    * Failed to connect to example.localhost port 80 after 0 ms: Couldn't connect to server
    * Closing connection
    

    Running it with another TLD, for example .example:

    $ docker run 
        --rm 
        --add-host example.example:93.184.216.34 
        $(docker build --quiet .) 
        example.example
    
    * processing: example.example
    *   Trying 93.184.216.34:80...
    * Connected to example.example (93.184.216.34) port 80
    > GET / HTTP/1.1
    > Host: example.example
    > User-Agent: curl/8.2.1
    > Accept: */*
    > 
    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
            <head>
                    <title>404 - Not Found</title>
            </head>
            <body>
                    <h1>404 - Not Found</h1>
            </body>
    </html>
    < HTTP/1.1 404 Not Found
    < Content-Type: text/html
    < Date: Mon, 21 Aug 2023 15:26:57 GMT
    < Server: ECS (dcb/7F3C)
    < Content-Length: 345
    < 
    { [345 bytes data]
    * Connection #0 to host example.example left intact
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search