If we don't specify host ip address when declaring the ports in the compose file, like:
ports:
- "8000:8000"
docker-compose will assume the host ip address to use is 0.0.0.0
, but if we run the docker daemon with the --ip=<default-ip>
option, it should use <default-ip>
instead.
See in the docker daemon documentation:
--ip=0.0.0.0 Default IP when binding container ports
The --ip=<default-ip>
option is correctly taken into account when running a container directly with docker run
, but when I want to run it through docker-compose
, it does not work and we have to force host ip value for all services in the docker-compose.yml file like:
ports:
- "10.0.0.1:8000:8000"
Seems strange, because we just pass these values on to the API, and I would have expected the engine to handle it, but maybe it's actually the docker-cli which is doing the work here. If that's the case, the correct fix might be to have the engine do it instead of the client.
Needs more investigation.
As far as I can see, when a version 2 YAML definition does not specify what networking to use, docker-compose creates a projectname_default
network instead of using the default bridge.
These projectname_default
networks do not have _host_binding_ipv4_ set, even if the daemon was started with e.g. --ip=192.168.17.1
:
# Default ‘bridge’ network
$ docker network inspect bridge | grep host_binding_ipv4
"com.docker.network.bridge.host_binding_ipv4": "192.168.17.1",
# docker-compose-created ‘example_default’ (see YAML below)
$ docker network inspect example_default | grep host_binding_ipv4 || echo not present
not present
_example_default_ was created by docker-compose for a very simple file:
version: "2"
services:
test:
image: busybox
command: sleep 60
ports:
- "7560:8080"
One simple workaround is to specify network_mode: default
in services defined in docker-compose.yml.
network_mode: default
is not a workaround. You just destroy projects isolation when using this default mode.
I found a better workaround for this issue: create a new network bound to a specific IP, and instruct compose to use the external network:
docker network create -o "com.docker.network.bridge.host_binding_ipv4"="192.168.17.1" bridge2
Use the following docker-compose.yml
:
version: "3"
services:
test:
image: busybox
command: sleep 60
ports:
- "7560:8080"
networks:
- bridge2
networks:
bridge2:
external: true
I use compose v3, this may also work on v2.
This also avoids the need to change the docker daemon settings.
An alternate approach not requiring any special networks, or hard coded IP addresses:
docker_compose.yml
version: '3'
services:
web:
image: nginx
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8080:80"
By default, this will start the container so its not viewable externally (bound to 127.0.0.1).
If you want to expose it externally just run export MY_DOCKER_IP=0.0.0.0
then restart your container with docker-compose up -d --force-recreate
.
Changed to:
ports:
- "0.0.0.0:8080:8080"
and it's works.
@SteveEasley , Do you see some problem with that alteration?
@arthurhenrique 0.0.0.0 is the default value. The point of this issue is how NOT to bind 0.0.0.0 (all interfaces).
Add network_mode: "bridge"
can solve this, for example:
version: "2"
services:
nginx:
image: nginx
ports:
- 80:80
network_mode: "bridge"
Because by default, docker-comose will create a new network and attach the containers to that network, and the network's IP address is not configured whitch is default "0.0.0.0".
And the bridge
network specifics like this:
root@ubuntu-03:~/nginx# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "787060ef9e42fce87ad52ef31e94edebd7011a681499b0169195da5048271564",
"Created": "2019-03-06T16:04:56.942488612+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "192.168.56.3",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
And that's explains that create a new network with the option "com.docker.network.bridge.host_binding_ipv4"="192.168.17.1"
can solve this problem as well.
What about just specify the driver option in compose like below:
networks:
foo_network:
driver_opts:
com.docker.network.bridge.host_binding_ipv4: "1.2.3.4"
Additionaly, the workaround that specify network_mode
to bridge
or default
will lose the compose hostname resolve capability.
The issue here is that all of those "solutions" require modifications of docker-compose.yml
file, that is stored in project repository, while we need something that will be configured globally per machine and won't affect other machines that use the project.
Also setting this for every project and every container in that project is tedious and error prone.
Especially if you need to change the config later for some reason.
Hello folks, is there any update on this issue? I agree with @hubertnnn that a solution that requires a manual changing of a source file is not ideal, ideally docker-compose would respect the daemon's ip setting. Unfortunately this makes docker-compose not a viable option in production since it requires the developer to know that docker-compose could expose port to the open internet even when the docker daemon defaults to another location.
Hey guys, we would really like to see some updates in this matter. Security should be kept as maximum as one can. thanks for the amount of good job guys. Please take a look into it.
This somewhat related to https://github.com/docker/for-linux/issues/370
Here is a how-to-repeat with docker-compose, after adjusting the dockerd daemon.json file as described in the other issue.
docker-compose.yml
version: "3"
services:
webserver:
build: .
ports:
- "8989:80"
docker-compose up -d
docker ps
It will still report a bind address of 0.0.0.0 rather than 127.0.0.1, which is wrong.
And bad. And insecure.
Ideally default bind address for software should always be 127.0.0.1, then a human has to make the conscious decision to bind it to something else or open it up to the world. I understand the problem in making that change for an existing product. However, at least the proper config should be picked up, then people will be able to secure their systems appropriately.
Ok, I got into this because portainer was showing wrong IP-addresses in the GUI. They mentioned in their FAQ: https://portainer.readthedocs.io/en/1.13.1/faq.html#exposed-ports-in-the-container-view-redirects-me-to-0-0-0-0-what-can-i-do
After spending an hour to read Docker documentation on how to change that and restarting my Docker daemon several times, I found this issue https://github.com/portainer/portainer/issues/388 and a reference to this issue https://github.com/docker/compose/issues/2999#issue-135739554, finally with a workaround to make it work. It's quite frustrating. Can we just stick to the options set on the Docker daemon even when docker-compose is used? This issue seems to be open quite long already. For now, I'm using the workaround and changing all my docker-compose files.
I agree with @ElleshaHackett, seems like docker-compose honoring the docker daemon's base ip address is what many users would assume, and that assumption leads to misconfigured and exposed containers. Given the rise of docker as an attack vector, fixing this would surely reduce accidentally exposing your container on the web.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
please remove it's stale state
This issue has been automatically marked as not stale anymore due to the recent activity.
Most helpful comment
I found a better workaround for this issue: create a new network bound to a specific IP, and instruct compose to use the external network:
docker network create -o "com.docker.network.bridge.host_binding_ipv4"="192.168.17.1" bridge2
Use the following
docker-compose.yml
:I use compose v3, this may also work on v2.
This also avoids the need to change the docker daemon settings.