skip to Main Content

I’d like to create a container via the docker rest api which will be accessible from the host but without external network access.

I’ve read the docs a couple times but still not able to get it to work.

When passing in the network name as the network to connect to when starting, there are no ports exposed.

If I connect at a later time, the container is connected to 2 networks – the default bridge, and my network. The exposed ports are working correctly in the latter case.

How do I configure this?

EDIT:

I did some more experiments – I was setting

Internal: true

Which according to the docs makes it so that only other containers connected will have access to the container.

I’d like to access the container from the host so the network cannot be internal.

Setting it to False allows me to access the container, but I can also connect to google from within the container.

Relevant reading:

https://docs.docker.com/engine/api/v1.43/#tag/Container/operation/ContainerCreate

https://threatpicture.com/blog/restrict-internet-access-docker-container/#:~:text=Another%20way%20to%20prevent%20a,the%20host%20and%20the%20internet.

The second link talks about creating a bridge network which by default shouldn’t have an external gateway.

Here is the network inspected as well as some other useful info

=> docker ps

CONTAINER ID   IMAGE                            COMMAND                  CREATED         STATUS         PORTS                   NAMES
cecb660da65c   datumai/mh-mock-runtime:latest   "uvicorn app.main:ap…"   5 minutes ago   Up 5 minutes   0.0.0.0:55807->80/tcp   datumai-model

=> docker inspect ID_CONTAINER

 "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "f5509cdf337b740ad50290766854aebddadd802366c780cf4d5e31a7050ffbd7",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "55807"
                    }
                ]
            },
            "SandboxKey": "/var/run/docker/netns/f5509cdf337b",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "datumai-model-network-offline": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "cecb660da65c"
                    ],
                    "NetworkID": "59d034f30a97de377c6d3745b1b5764c487d58a5aa2e69dedd2dc7f4ece71e33",
                    "EndpointID": "f12b9a39c85ecda7f60edea34c09cc8773eb61be90dee281228b59d7bb6eba47",
                    "Gateway": "172.27.0.1",
                    "IPAddress": "172.27.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:1b:00:02",
                    "DriverOpts": null
                }
            }
        }
    }

=> docker inspect ID_OF_NETWORK

[
    {
        "Name": "datumai-model-network-offline",
        "Id": "59d034f30a97de377c6d3745b1b5764c487d58a5aa2e69dedd2dc7f4ece71e33",
        "Created": "2024-01-07T02:48:35.182210136Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.27.0.0/16",
                    "Gateway": "172.27.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "e42176ec9ae1b540a3703ccd6c09d44782dd17e5fba8bc7d4ff2a7403daf729a": {
                "Name": "datumai-model",
                "EndpointID": "e9ec6900d53c772a84f136d2599b1410aca0366518009a522a31b78de70a00d0",
                "MacAddress": "02:42:ac:1b:00:02",
                "IPv4Address": "172.27.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.datumai.model": "true"
        }
    }
]

EDIT 2:

The answer below mentions that an internal network should work just fine. Unfortunately, it seems that for my setup – which is Docker Desktop on a MacOSX M2, it doesn’t work that way.

Reproducing the issue:

docker network create --internal internal
docker run --net internal --rm --name web docker.io/alpinelinux/darkhttpd

in a new tab

=> docker container inspect web --format '{{.NetworkSettings.Networks.internal.IPAddress}}'

172.29.0.2:8080

The call to curl from host:

=> curl 172.29.0.2:8080
hangs, then shows
curl: (28) Failed to connect to 172.29.0.2 port 8080 after 75009 ms: Couldn't connect to server

2

Answers


  1. Chosen as BEST ANSWER

    I finally managed to get the behavior I want, but unfortunatelly that required a workaround by running an additional proxy. The approach isn't perfect but it might help someone down the line.

    Here are the rough steps to follow

    • Create an internal network as suggested
    • Create another container with an nginx proxy. It should connect to the default bridge network. You also need to publish a single port of the proxy so your host can access the proxy.
    • Connect that container to the internal network above.

    I've used https://hub.docker.com/r/jwilder/nginx-proxy.

    The configuration requires the following:

    • mount the docker socket when starting the proxy container
    • add env variable called VIRTUAL_HOST, in my case I set it to datumai-model.local

    The calls to the containers need to be routed through the proxy container, which needs the Host header set to the VIRTUAL_HOST above.

    e.g.

    curl -H "Host: <VIRTUAL_HOST>" http://localhost:<FORWARDED_PORT_OF_PROXY>
    

    Finally a call to the service succeeds, but any queries inside the container to e.g. google fail.


  2. I’d like to access the container from the host so the network cannot be internal.

    In fact, you can access a container from the host even when using the internal network as long as you access the container ip directly (rather than using port publishing). For example, if we start up a simple web server like this:

    docker network create --internal internal
    docker run --net internal --rm --name web docker.io/alpinelinux/darkhttpd
    

    We can get the container ip address like this:

    $ docker container inspect web --format '{{.NetworkSettings.Networks.internal.IPAddress}}'
    192.168.224.2
    

    And we can successfully connect to the web service in that container:

    $ curl 192.168.224.2:8080
    <html>
    ...
    </html>
    

    Because it’s on an internal network, the container doesn’t have outbound network connectivity:

    $ docker exec -it web wget -O- google.com
    wget: bad address 'google.com'
    $ docker exec -it web wget -O- 142.251.40.206
    Connecting to 142.251.40.206 (142.251.40.206:80)
    wget: can't connect to remote host (142.251.40.206): Operation timed out
    

    The second link talks about creating a bridge network which by default shouldn’t have an external gateway.

    That is highly inaccurate. A bridge network is the most common type of Docker network, and of course it has a default gateway because most folks do want outbound network access:

    $ docker network create --driver=bridge mynetwork
    52d2506adc651486300a95ea80855822180c2af0242ca1572ed6ab71e748de37
    $ docker run --rm --net mynetwork docker.io/alpine ip route
    default via 192.168.240.1 dev eth0
    192.168.240.0/20 dev eth0 scope link  src 192.168.240.2
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search