在我的服务器(具有单个 IP 地址)上,多个服务在不同的端口上运行。Nginx 也在端口 80 和 443 上运行,以根据域请求提供页面。所有服务和 nginx 都是通过 docker-compose 启动的。如何使用 nginx、iptables、防火墙和其他开发工具拒绝对 IP 地址的所有外部请求(例如,对 56.56.56.56:3000 或对 56.56.56.56)但同时允许对域名的所有请求?一切都在 Ubutntu 服务器 22.04 上运行

nginx.conf

http {
    limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
    limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;

    server {
        listen 80 default_server;
        server_name my.domain www.my.domain;

        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name my.domain;

        ssl_certificate /etc/nginx/ssl/my.domain/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/my.domain/privkey.pem;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        client_body_timeout 10s;
        client_header_timeout 10s;
        keepalive_timeout 10s;
        send_timeout 10s;

        location / {
            proxy_pass http://56.56.56.56:3002; # адрес для примера
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            limit_conn conn_limit_per_ip 10;
            limit_req zone=req_limit_per_ip burst=10 nodelay;
        }
    }
    ...
}

docker-compose.yml

...
  site:
    build:
      context: ...
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "3002:3002"
    logging:
      driver: "json-file"
      options:
        max-size: "1m"
        max-file: "1"

  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl/:/etc/nginx/ssl/

  grafana:
    image: grafana/grafana:latest
    ports:
      - '3000:3000'
    volumes:
      - grafana-data:/var/lib/grafana
...

提前致谢!

重要信息

  • 如果我尝试通过 iptables 限制外部 TCP 请求并仅允许内部请求(以便请求只能通过 nginx 从外部发送,即发送到域名),防火墙甚至会阻止通过 nginx 的请求。我认为在向域发送请求时,它们会通过 nginx 路由,因此防火墙认为该请求是从本地 IP 发送的。

  • 如果我在机器本地运行所有服务(例如app.run("127.0.0.1", port=3000)),nginx 不会检测到这一点,这意味着proxy_pass http://127.0.0.1:3000将无法工作。我原本打算在本地运行所有服务,这样它们就无法直接从外部访问,但 nginx 仍然可以访问它们,因为它在同一个容器中运行,但只能通过域请求。然而,由于某种原因,当 nginx 尝试请求时,我收到“连接被拒绝”错误127.0.0.1

1

  • 您根本不需要发布端口(当然,nginx 除外)。您可以让 nginx 通过其名称在内部访问其他服务,例如http://grafana:3000


    – 


最佳答案
1

您的容器网络暴露了不必要的主机端口。

  • 删除除外部代理ports之外docker-compose.yml的所有内容的外部代理,在本例中名为nginx
  • 将这些前端和后端容器保持在同一个 docker-compose 网络中。
  • 使用容器名称在网络上相互引用。proxy_pass 到后端 http://site:3002http://grafana:3000

导致您的容器定义也生成了具有区域概念的防火墙。容器可以相互访问,但外部主机流量只能通过端口进入 nginx。无需额外手动生成的私有网络和防火墙规则即可实现此隔离。

此 IP 级网络和防火墙不关心您的 http 名称虚拟主机。但是,该 nginx 配置已在应用程序级别需要名称。并且 https 重定向将让用户代理也验证名称。(具有 IP 地址主体的证书在理论上是可行的,但与 (DNS) 名称相比,几乎没有人使用它们。)

不指定 IP 地址可使您的地址计划更加灵活。

  • 如果反向代理不是通过环回地址,则容器可以在不同的主机上运行。
  • 当您实现 IPv6 地址时,您不再需要担心地址空间重叠。您应该使用的所有空间都是互联网可路由的,但这没关系。容器地址之间的区别2001:db8:116:7483::a 可能::b在于一个暴露主机端口,而另一个不暴露。