docker-compose 只向外界暴露一个网络

Blu*_*lue 2 iptables docker docker-compose

我有一个通过 docker-compose 文件创建的新应用程序。该文件包含2个网络:

version: '2.1'

# ----------------------------------
# Services
# ----------------------------------
services:
  application:
    image: tianon/true
    volumes:
      - ${APPLICATION_PATH}:/var/www

  nginx:
    build:
      context: ./docker/nginx
    volumes_from: 
      - application
    volumes:
      - ${DOCKER_STORAGE}/nginx-logs:/var/log/nginx
      - ${NGINX_SITES_PATH}:/etc/nginx/sites-available
    ports:
      - "${NGINX_HTTP_PORT}:80"
      - "${NGINX_HTTPS_PORT}:443"
    networks:
      - frontend
      - backend

  redis:
    build:
      context: ./docker/redis
    volumes: 
      - ${DOCKER_STORAGE}/redis:/data
    ports:
      - "${REDIS_PORT}:6379"
    networks: 
      - backend

# ----------------------------------
# Networks
# ----------------------------------
networks:
  frontend:
    driver: "bridge"
  backend:
    driver: "bridge"

# ----------------------------------
# Volumes
# ----------------------------------
volumes:
  redis:
    driver: "local"
Run Code Online (Sandbox Code Playgroud)

您会注意到我这里有 2 个网络,frontend并且backend. 问题很简单:

  • 我如何frontend向世界公开,但允许backend服务相互通信而不向世界公开?(我假设使用 iptables,或者在 docker 中指定我想要向主机公开哪个网络)
  • 这是一个简单的 1 服务器设置(Digital Ocean),运行 Ubuntu 18.04 LTS
  • 在上面的示例中,我应该能够从外部世界的端口 80 或 443 访问 nginx,但不能访问 redis。Nginx容器应该能够在内部访问redis服务6379。
  • 更明确地说:我现在的目的是让后端服务暂时相互通信,因此删除 EXPOSE 现在就可以了,但最终,我想分区这两个网络。后端服务,我想限制 IP 范围(允许其他一些非 Docker 网络进行通信),前端服务我想向世界开放暴露的端口。

对于赏金:docker 中显然有一种方法可以定义多个覆盖网络。我原以为,将一个网络暴露给外界会很简单。我正在尝试在 docker 或 iptables 中找到一种简单的方法来做到这一点。当前的答案给了我一些方向,但需要每个端口的手动规则。我想要一个关于如何保护我的“前端”网络的正确答案,而不必在 iptables 中指定特定端口。

EOh*_*Ohm 5

完成@BMitch的回答以满足您的需求..

您可以使用额外的容器间网络(如果您看到需要或用例,则可以使用多个容器间网络。),因为为了使此类设置可靠地工作,每个容器仅分配一个非内部网络至关重要。否则,您无法真正确定将为映射端口上的传入流量选择哪个桥接网络(或者至少它不可真正配置)。

显示此类设置的示例 compose 文件:

version: '3.6'
services:
  c1:
    container_name: c1
    image: "centos:latest"
    entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
    ports:
      - "5000:5000"
    networks:
      - front
      - inter
  c2:
    container_name: c2
    image: "centos:latest"
    entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
    ports:
      - "5001:5001"
    networks:
      - inter
  c3:
    container_name: c3
    image: "centos:latest"
    entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
    ports:
      - "5002:5002"
    networks:
      - back
      - inter
  c4:
    container_name: c4
    image: "centos:latest"
    entrypoint: /bin/bash -c "while sleep 10; do sleep 10; done"
    ports:
      - "5003:5003"
    networks:
      - back
      - inter
networks:
  front:
    name: front
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: dockerfront
  back:
    name: back
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: dockerback
  inter:
    name: inter
    driver: bridge
    internal: true
    driver_opts:
      com.docker.network.bridge.name: dockerinter
Run Code Online (Sandbox Code Playgroud)

它为您的公共服务创建一个网桥([docker]front),为您的后端服务创建一个网桥([docker]back),以及一个内部网桥(即使在请求时也不会发布端口,因此可以安全地添加为附加网络) ([docker]intern) 并将它们分配给容器。
实际上,从外部无法通过端口 5001 访问 c2,因此可以省略该容器的端口。只是为了显示结果:

# iptables -nvL DOCKER -t nat
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 RETURN     all  --  dockerback *       0.0.0.0/0            0.0.0.0/0
    0     0 RETURN     all  --  dockerfront *       0.0.0.0/0            0.0.0.0/0
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0
    0     0 DNAT       tcp  --  !dockerback *       0.0.0.0/0            0.0.0.0/0            tcp dpt:5002 to:172.27.0.2:5002
    0     0 DNAT       tcp  --  !dockerfront *       0.0.0.0/0            0.0.0.0/0            tcp dpt:5000 to:172.25.0.2:5000
    0     0 DNAT       tcp  --  !dockerback *       0.0.0.0/0            0.0.0.0/0            tcp dpt:5003 to:172.27.0.3:5003
Run Code Online (Sandbox Code Playgroud)

dockerback现在您可以添加桥接链的访问规则DOCKER-USER

# iptables -I DOCKER-USER ! -s 10.0.0.0/24 -o dockerback -j DROP
# iptables -nvL DOCKER-USER
Chain DOCKER-USER (1 references)
 pkts bytes target     prot opt in     out     source               destination
    3   156 DROP       all  --  *      dockerback !10.0.0.0/24          0.0.0.0/0
   10   460 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0
Run Code Online (Sandbox Code Playgroud)

请注意:当前版本 19.03.3 存在DOCKER-USER未创建链的错误,该错误已在 19.03.4 中修复,很快就会推出。如果您有该版本,您可以自己添加链(必须在每次重新启动 docker 守护进程后完成):

iptables -N DOCKER-USER
iptables -I FORWARD -j DOCKER-USER
iptables -A DOCKER-USER -j RETURN 
Run Code Online (Sandbox Code Playgroud)

可能你会找到一个更合适的地方来放置这些规则,但这是 docker 建议的方式。(我可以考虑修复网桥的网络,并将规则放入通用转发规则中,不适用于重新启动、接口重新创建等情况下的 docker 操作。)

另外(或相反),您可以在计算机上使用额外的 IP 将后端服务绑定到另一个 IP,然后在该级别使用前端服务和过滤器。另外,意味着与上面完全相同的 docker 设置,但另外指定了com.docker.network.bridge.host_binding_ipv4驱动程序选项(这只是已发布端口的默认 IP) - 或者只使用一个网络并根据服务类型指定绑定地址。然后在 DOCKER-USER 中或在适当的地方使用与传入接口匹配的 conntrack 模块 ctorigdst 来阻止 FORWARD。