akagixxer
akagixxer

Reputation: 1859

docker-compose - How to specify which network for listening port?

I'm using:
docker-compose 3.7
docker engine 18.09.7

In a docker-compose file, how do I specify which network I want a specific listening port bound to?
For example:

version: "3.7"
services:
  service-a:
    image: service-a:0.1.0
    networks:
      - network1
  service-b:
    image: service-b:0.1.0
    networks:
      - network1
      - network2
    expose:
      - "8000"
      - "9000"
    ports:
      - target: 8000
        published: 8000
        protocol: tcp
        mode: host
      - target: 9000
        published: 9000
        protocol: tcp
        mode: host
  service-c:
    image: service-c:0.1.0
    networks:
      - network2
networks:
  network1:
  network2:

In this contrived example service-b is listening on port 8000 and 9000.
Is there a way to specify that port 8000 is only accessible on network1 while 9000 is only accessible on network2?

This would be most helpful in the case where a server listens on, say 0.0.0.0 as the host.

Upvotes: 1

Views: 2631

Answers (1)

acran
acran

Reputation: 8988

So if I get this right what you want to achive is to grant service-a access to port 8000 of service-b but block any access from service-a to port 9000 of service-b. And the same for service-c but the other way around?

For this you first need to know how the networking with docker-compose works: for each network under the networks section docker-compose (in this case) creates a virtual network connecting a virtual network device of the host machine to it as well as a virtual network device of each container contected to the network. Each of these virtual devices can communicate directly with each other in the same virtual network while the different virtual networks are usally isolated from each other.

The expose keyword now does not actually expose any ports but instead only documents the intent that a process will listen on that port(s). You can examine this information about a container using docker inspect. Besides the added meta-data expose does not actually do much more, see the documentation. So in this case it has no real use.

The ports keyword on the other hand does expose the listed ports to ports on the host machine - see the docs. Since the containers communicate directly via their share networks this is again not of real use for your scenario.

There are also no other configuration options which are intended to limit the communication of containers within the same network, i.e. there is no officially supported way to do this nicely.

One way to do this would be to modify the application itself to not listen on 0.0.0.0 with each port but only bind to the address of the respective network (network1/network2). But this requires application-specific changes and to somehow detect the correct address for each port.

Another way would be to inject your own iptables rules to block undesired access between containers, see the docs on this. The downside of this is that it has to be done completely outside of docker and docker-compose.

And lastly there is this hackish solution: instead of blocking undesired access only allow for explicitly whitelisted ports:

version: "3.7"
services:
  service-a:
    image: service-a:0.1.0
    networks:
      - network1
  service-b:
    image: service-b:0.1.0
    networks:
      - network2
    ports:
      - 172.101.0.1:8000:8000
      - 172.103.0.1:9000:9000
  service-c:
    image: service-c:0.1.0
    networks:
      - network3
networks:
  network1:
    ipam:
      config:
        - subnet: 172.101.0.0/24
  network2:
  network3:
    ipam:
      config:
        - subnet: 172.103.0.0/24

This works by assigning each container to its very own network completely isolating them from each other. But for network1/network3 we explicitly configure the subnet so we know the gatway IPs (172.101.0.1/172.103.0.1) of them which are assigned to the virtual network devices of the host.

Now we can "expose" the ports 8000/9000 of the service-b container to these host IP addresses, i.e. port 8000 on 172.101.0.1 will be forwarded to port 8000 of the service-b container. 172.101.0.1 belongs to the host but is part of network1 and thus can be accessed by service-a allowing it to only access that one port of service-b.

Upvotes: 3

Related Questions