Restrict exposed ports to worker nodes only?

I am trying out Docker Swarm to use in production app. My concern here is security, I would love to isolate manager nodes to be somewhat separate (such as not running any application there, only possibly portainer for web interface).

I can restrict all stack components from running on managers using constraints. But even I do that I still see published ports (i.e. 8080) also being exposed on manager nodes. And I can successfully query application through manager nodes.

While routing mesh is a nice feature, I think there should be a way to avoid having ports exposed on absolutely all nodes.

I may have missed some obvious way to do that, will appreciate pointers to docs that describe how to do this.

with Swarm, by default a mapped port uses the ingress network. Thus, it will be bound to all nodes of the cluster. If you want to bind it to nodes that actualy execute the container, you need to use “mode: host” in your port binding declarion.

Be sure to lock down the nodes with a label and add a deployment contraint to your container to ensure that it is only deployed to specific nodes.

Example compose.yml snippet:

services:
  myService:
  ...
    ports:
    - target: 80
      published: 8080
      protocol: tcp
      mode: host
 ...

See: https://docs.docker.com/compose/compose-file/#long-syntax-1

A good example to use this sort of setting is to run your own loadbalancer on dedicated worker nodes for a stack deployed to the same set of worker nodes. Though, we use deploy “mode: global” and placement contraints additionaly for those particular containers, as each node should run exactly one instace.