Different HTTPS response in container and host machine using same CURL command in Docker

This morning i have encountered a weird issue in my Docker. I was trying the following on the Host machine

curl --location 'https://widgets.sir.sportradar.com/translations/en.json' --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36' --header 'Accept: */*' --header 'Connection: keep-alive' --header 'Cache-Control: no-cache' --verbose

and it worked perfectly fine without any issue and i see a JSON response aswell.

But when i run the same command inside the Docker (a container, with no proxy, no https proxy, and even Docker is not behind a proxy) , i see the following response

# curl --location 'https://widgets.sir.sportradar.com/translations/en.json' --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36' --header 'Host: widgets.sir.sportradar.com' --header 'Origin: widgets.sir.sportradar.com' --header 'Accept: */*' --header 'Connection: keep-alive' --header 'Cache-Control: no-cache' --verbose
 *   Trying 23.15.33.210:443...
 *   Trying 2600:1417:3f::1728:28a8:443...
 * Immediate connect fail for 2600:1417:3f::1728:28a8: Network is unreachable
 *   Trying 2600:1417:3f::1728:2871:443...
 * Immediate connect fail for 2600:1417:3f::1728:2871: Network is unreachable
 * Connected to widgets.sir.sportradar.com (23.15.33.210) port 443 (#0)
 * ALPN, offering h2
 * ALPN, offering http/1.1
 *  CAfile: /etc/ssl/certs/ca-certificates.crt
 *  CApath: /etc/ssl/certs
 * TLSv1.0 (OUT), TLS header, Certificate Status (22):
 * TLSv1.3 (OUT), TLS handshake, Client hello (1):
 * TLSv1.2 (IN), TLS header, Certificate Status (22):
 * TLSv1.3 (IN), TLS handshake, Server hello (2):
 * TLSv1.2 (IN), TLS header, Finished (20):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, Certificate (11):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, CERT verify (15):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, Finished (20):
 * TLSv1.2 (OUT), TLS header, Finished (20):
 * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
 * TLSv1.3 (OUT), TLS handshake, Finished (20):
 * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
 * ALPN, server accepted to use h2
 * Server certificate:
 *  subject: C=AT; ST=Wien; L=Wien; O=Sportradar Media Services GmbH; CN=widgets.sir.sportradar.com
 *  start date: Sep 12 00:00:00 2024 GMT
 *  expire date: May 14 23:59:59 2025 GMT
 *  subjectAltName: host "widgets.sir.sportradar.com" matched cert's "widgets.sir.sportradar.com"
 *  issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS RSA SHA256 2020 CA1
 *  SSL certificate verify ok.
 * Using HTTP2, server supports multiplexing
 * Connection state changed (HTTP/2 confirmed)
 * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
 * Using Stream ID: 1 (easy handle 0x559081b41eb0)
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET /translations/en.json HTTP/2
> Host: widgets.sir.sportradar.com
> user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
> origin: widgets.sir.sportradar.com
> accept: */*
> connection: keep-alive
> cache-control: no-cache
> 
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
 * old SSL session ID is stale, removing
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.2 (OUT), TLS header, Supplemental data (23):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 403 
< server: AkamaiGHost
< mime-version: 1.0
< content-type: text/html
< content-length: 410
< expires: Fri, 13 Sep 2024 08:03:33 GMT
< date: Fri, 13 Sep 2024 08:03:33 GMT
< strict-transport-security: max-age=31536000 ; includeSubDomains ; preload
< 
 * TLSv1.2 (IN), TLS header, Supplemental data (23):
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
 
You don't have permission to access "http&#58;&#47;&#47;widgets&#46;sir&#46;sportradar&#46;com&#47;translations&#47;en&#46;json" on this server.<P>
Reference&#32;&#35;18&#46;7ec21160&#46;1726214612&#46;dd56e61
<P>https&#58;&#47;&#47;errors&#46;edgesuite&#46;net&#47;18&#46;7ec21160&#46;1726214612&#46;dd56e61</P>
</BODY>
</HTML>
 * Connection #0 to host widgets.sir.sportradar.com left intact

Now, if we do the same request on our Host machine but add the header X-Forwarded-Proto: https the request would fail on the host machine aswell and we will see the same error “Access Denied”.

Note that the response for “Access Denied” says “http://” instead of the “https://” scheme.

Now if we run the same container but network mode to host , and run the curl command, it will work perfectly fine in the container aswell.

This means that somehow docker is interfering with the requests and adding additional headers maybe?

My docker-compose is as below

    testprogram:
    build:
    context: ./testprogram/
    dockerfile: Dockerfile
    environment:
    - HTTP_PROXY=
    - HTTPS_PROXY=
    - NO_PROXY=localhost,127.0.0.1,testprogram,nginx
    volumes:
    - /etc/ssl/certs:/etc/ssl/certs
    ports:
    - "8989:8989"
    restart: always
    networks:
    - backend
    - frontend
    deploy:
    resources:
    limits:
    cpus: "4"
    memory: 2048M

The weird issue is that the curl command works on host machine and in container only if network mode is host but if network mode is not set, the request fails in the container.

What could be missing here?

The issue you’re encountering seems to be related to how Docker handles network traffic and potentially the headers that it adds or modifies in container networking mode. Here are a few things to consider and troubleshoot:

1. Network Configuration

When using Docker’s default bridge network, requests made from a container might appear to come from a different IP address or might have different routing rules than those on the host. The server you’re trying to reach might have IP-based restrictions or other checks that deny access based on the request’s origin.

2. User-Agent and Headers

Although you are setting the User-Agent header in your curl command, the server may be looking for additional headers or specific conditions related to the source IP or network. When you switch to host networking, your container’s requests are coming from the host’s IP, which might be recognized and allowed by the server.

3. IP Whitelisting

Some services implement security measures based on IP addresses. If your host’s IP is whitelisted while the Docker bridge network IPs are not, requests from the container will be denied.

4. Firewall/Security Groups

If your Docker setup has any firewall rules or if the server has firewall rules, these may be affecting how requests are treated. Make sure that the Docker bridge network IPs are not being blocked.

5. Inspecting Headers

To see exactly what headers are being sent from both the host and the container, you can use tools like tcpdump or a reverse proxy like nginx to log incoming requests. This might reveal differences in headers that could explain the discrepancy.

6. Check DNS Resolution

The container might resolve DNS differently than the host. Make sure that the DNS settings in the container are correct. You can try explicitly setting DNS in your docker-compose.yml:

services:
  testprogram:
    dns:
      - 8.8.8.8
      - 8.8.4.4

7. Akamai Configuration

The server seems to be behind Akamai, which could apply its own access rules based on geographical or network-specific criteria. Consider checking with the service provider for any specific requirements they have for requests coming from containers.

8. Using cURL Options for Debugging

You can add the -v (verbose) and --trace options to your curl command in the container to see more details about the request being sent and the response headers.

Example Command for Further Debugging:

curl -v --trace-ascii /dev/stdout 'https://widgets.sir.sportradar.com/translations/en.json' ...