docker-compose:VPN后的映像在本地网络中不可用

euoag5mw  于 2023-08-03  发布在  Docker
关注(0)|答案(1)|浏览(91)

我在一个docker-compose文件中有四个服务。vpn_img1和vpn_img2在vpn后面(如下所述配置),host_img1和host_img2不在后面。
我可以通过http://localhost:<port_num>访问主机上的所有四个“img”服务,但只能访问本地网络上其他机器上的“host_img”映像;“vpn_img”映像超时。我得到相同的结果与其他类似的VPN图像。有人知道为什么吗?vpn服务上是否有允许访问“vpn_img”映像的任何设置?我还需要做些什么来实现这种访问吗?
此外,host_img1需要与vpn_imgs对话,但host_img2不需要。

version: "3.4"
services:    
  vpn:
    container_name: vpn
    image: ghcr.io/bubuntux/nordlynx
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PRIVATE_KEY=xxx
      - QUERY=filters\[country_id\]=228
      - NET_LOCAL=192.168.1.0/24
      # - ALLOWED_IPS=192.168.0.0/24
    ports:
      - 1234:1234 # vpn_img1
      - 2345:2345 # vpn_img2
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1

  vpn_img1:
    container_name: vpn_img1
    image: vpn_img1
    restart: unless-stopped
    network_mode: service:vpn # run on the vpn network
    depends_on:
      - vpn

  vpn_img2:
    container_name: vpn_img2
    image: vpn_img2
    restart: unless-stopped
    network_mode: service:vpn # run on the vpn network
    depends_on:
      - vpn

  host_img1:
    container_name: host_img1
    image: host_img1
    restart: unless-stopped
    network_mode: host

  host_img2:
    container_name: host_img2
    image: host_img2
    restart: unless-stopped
    network_mode: host

字符串

ecr0jaav

ecr0jaav1#

问题的关键应该是VPN服务只允许来自主机(VPN客户端正在运行)的流量:本地网络上的其他机器无法访问VPN后面的服务。
它们的网络可访问性仅限于与VPN服务相同的范围。
由于安全原因,VPN (Virtual Private Network)服务本身倾向于将网络流量限制为仅通过VPN隧道,从而使其后面的服务无法从外部访问,除非另有特别配置。
当一个容器使用另一个容器的网络堆栈时,它实际上就“隐藏”在那个服务后面。该容器的所有传入和传出网络流量都经过主服务的网络,在本例中,该网络是VPN服务。这就是为什么您可以从主机(VPN客户端正在运行的位置)访问vpn_img1vpn_img2服务,而不能从本地网络中的其他计算机访问的原因。
您可以尝试使用可以从本地网络访问的反向代理,并将请求转发到VPN保护的服务。
在Docker Compose文件中为反向代理设置一个新服务。您可以使用类似NginxTraefik的代码。该服务不应该在VPN后面。
配置反向代理将请求转发到vpn_img1vpn_img2。对于Nginx,您可以使用proxy_pass指令。并确保代理的端口正确转发,以便您可以从本地网络访问它。
请注意,vpn_img1vpn_img2仍然在VPN后面,它们建立的任何连接都将通过它。
但是,其他服务可以通过反向代理访问它们,该代理不在VPN后面。
例如,使用NGiNX:

version: "3.4"
services:    
  vpn:
    container_name: vpn
    image: ghcr.io/bubuntux/nordlynx
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PRIVATE_KEY=xxx
      - QUERY=filters\[country_id\]=228
      - NET_LOCAL=192.168.1.0/24
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1
    network_mode: bridge

  vpn_img1:
    container_name: vpn_img1
    image: vpn_img1
    restart: unless-stopped
    network_mode: service:vpn
    depends_on:
      - vpn

  vpn_img2:
    container_name: vpn_img2
    image: vpn_img2
    restart: unless-stopped
    network_mode: service:vpn
    depends_on:
      - vpn

  host_img1:
    container_name: host_img1
    image: host_img1
    restart: unless-stopped
    network_mode: host

  host_img2:
    container_name: host_img2
    image: host_img2
    restart: unless-stopped
    network_mode: host

  nginx:
    container_name: nginx
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 8000:80
    depends_on:
      - vpn
    network_mode: bridge

字符串
并且,与docker-compose.yml文件位于同一目录的nginx.conf文件应该是:

events {}

http {
    server {
        listen 80;

        location /vpn_img1/ {
            proxy_pass http://vpn:1234/;
        }

        location /vpn_img2/ {
            proxy_pass http://vpn:2345/;
        }
    }
}


nginx服务处于bridge网络模式,因此它可以访问本地网络,并且还能够访问vpn服务,因为它也处于bridge模式。proxy_pass指令被设置为http://vpn:<port>/,因为Nginx需要将请求发送到vpn服务,该服务将它们转发到vpn_img服务。<host_ip>是运行Docker的机器的IP地址。
这意味着您应该能够从本地网络中的任何机器访问http://<host_ip>:8000/vpn_img1/上的vpn_img1服务和http://<host_ip>:8000/vpn_img2/上的vpn_img2服务。
不幸的是,nginx无法启动,日志抛出host not found in upstream "vpn" in /etc/nginx/nginx.conf:8,这是第一个proxy_pass命令所在的位置(如果我注解掉第一个位置块,我在第12行,下一个proxy_pass命令中得到相同的错误)。
我已经确保VPN和Nginx都有network_mode:bridge,并尝试将vpn_img1vpn_img2添加到nginx的depends_on部分。
错误host not found in upstream "vpn"通常意味着Nginx容器无法将主机名vpn解析为IP地址。在这种情况下,这意味着Nginx无法找到您的vpn服务。
这通常是因为Docker内部DNS服务器无法将服务名称解析为其IP地址。
network_mode: service:<name>有点特殊,因为它将依赖服务放置在指定服务的网络堆栈中,而不是公共网络中。这就是为什么nginx服务不能直接到达vpn服务,即使它们都处于桥接网络模式。nginx服务位于默认网桥网络中,而vpn服务由于network_mode: service:vpn而被隔离在自己的网络堆栈中。
一个潜在的解决方案是创建一个自定义网络,并确保所有服务都在该网络上,如suggested in this thread

version: "3.4"
services:    
  vpn:
    container_name: vpn
    image: ghcr.io/bubuntux/nordlynx
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PRIVATE_KEY=xxx
      - QUERY=filters\[country_id\]=228
      - NET_LOCAL=192.168.1.0/24
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=1
    networks:
      - custom_network

  vpn_img1:
    container_name: vpn_img1
    image: vpn_img1
    restart: unless-stopped
    network_mode: service:vpn
    depends_on:
      - vpn
    networks:
      - custom_network

  vpn_img2:
    container_name: vpn_img2
    image: vpn_img2
    restart: unless-stopped
    network_mode: service:vpn
    depends_on:
      - vpn
    networks:
      - custom_network

  host_img1:
    container_name: host_img1
    image: host_img1
    restart: unless-stopped
    network_mode: host

  host_img2:
    container_name: host_img2
    image: host_img2
    restart: unless-stopped
    network_mode: host

  nginx:
    container_name: nginx
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 8000:80
    depends_on:
      - vpn
    networks:
      - custom_network

networks:
  custom_network:
    driver: bridge


nginx.conf

events {}

http {
    server {
        listen 80;

        location /vpn_img1/ {
            proxy_pass http://vpn_img1:1234/;
        }

        location /vpn_img2/ {
            proxy_pass http://vpn_img2:2345/;
        }
    }
}


这种方法将vpnvpn_img1vpn_img2nginx服务都放在同一个自定义网桥网络上,允许它们直接通信。
这应该可以解决host not found in upstream "vpn"错误。
现在host_img1host_img2无法找到vpn_img1vpn_img2
代理似乎有了一个良好的开端:http://<ip_addr>:8000/vpn_img1/本身返回200,但它的所有内容和子页面返回404。
host_img1host_img2服务中的network_mode: host意味着这些服务可以访问主机的网络堆栈,从而绕过Docker提供的隔离。因此,它们不是custom_network的一部分,不能直接与vpn_img1vpn_img2服务通信。
nginx反向代理的404错误来看,这可能是因为一些资源(如脚本,样式表,图像等)是用绝对路径请求的,而Nginx没有配置为正确处理这些请求。
首先,尝试将host_img1host_img2network_mode: host更改为networks: - custom_network,如果这些服务不一定需要在主机的网络堆栈中。
如果由于某种原因这些服务仍然需要在主机的网络堆栈中,请考虑在vpn_img1vpn_img2容器中添加另一个网络接口以连接到custom_network

其次,对于nginx 404问题,您可能需要根据vpn_img1vpn_img2服务的设置方式调整nginx配置。如果这些服务返回具有绝对路径的资源(如/styles/main.css),则需要添加另一个位置块来处理这些路径。如果可能,请考虑在vpn_img1vpn_img2服务中将这些路径更改为相对路径。
例如,nginx.conf可以是:

events {}

http {
    server {
        listen 80;

        location /vpn_img1/ {
            proxy_pass http://vpn_img1:1234/;
        }

        location /vpn_img2/ {
            proxy_pass http://vpn_img2:2345/;
        }

        # handle absolute paths from vpn_img1
        location /styles/ {
            proxy_pass http://vpn_img1:1234/styles/;
        }

        # handle absolute paths from vpn_img2
        location /scripts/ {
            proxy_pass http://vpn_img2:2345/scripts/;
        }
    }
}


即使给定位置的/文件也会得到404;因此/index.html/gettext.js返回404。
看来我得好好研究一下我的NGiNX了。有什么想法/建议吗?
这里的问题可能与您所代理的应用程序如何解释传入请求有关。
例如,对<ip_address>:8000/vpn_img1/index.html的请求将被转发到http://vpn_img1:1234/index.html
现在,如果您的vpn_img1服务设置为希望位于根目录(/),则它可能会尝试从绝对路径(如/styles/main.css/script/main.js)加载资源。这可能会导致nginx返回404,因为它未配置为处理对这些路径的请求。
要解决此问题,可以在将请求代理到相应的服务之前,从请求URI中去掉路径前缀(/vpn_img1/vpn_img2)。
另请参阅“Guide on how to use regex in Nginx location block section“。

events {}

http {
    server {
        listen 80;

        location ~ ^/vpn_img1/(.*)$ {
            proxy_pass http://vpn_img1:1234/$1;
        }

        location ~ ^/vpn_img2/(.*)$ {
            proxy_pass http://vpn_img2:2345/$1;
        }
    }
}


在这里,正则表达式与location匹配,并且/vpn_img1//vpn_img2/之后的任何路径都被捕获到$1变量中。然后,该值将用于proxy_pass指令中。现在,对<ip_address>:8000/vpn_img1/index.html的请求将被转发到http://vpn_img1:1234/index.html,并且index.html从根请求的任何资源也将被正确地代理到http://vpn_img1:1234/
只要vpn_img1vpn_img2所服务的应用程序使用相对路径来引用应用程序中的其他资源,这种方法就应该有效。如果它们使用绝对路径(例如,使用/styles/main.css而不是styles/main.css),则仍可能存在问题,您需要修改应用程序以使用相对路径。
我正在购买502,其中包含最新的nginx.conf(以及类似的):

GET /vpn_img1/ HTTP/1.1" 502 157 
"-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0" 
[error] 22#22: *1 no resolver defined to resolve vpn_img1, client: 192.168.0.185, server: , 
request: "GET /vpn_img1/ HTTP/1.1", host: "192.168.0.195:8000"


我无法想象这是一个 Docker 作曲的问题,但我不知道还能剩下什么。
"no resolver defined to resolve vpn_img1":这应该表明Nginx无法将主机名vpn_img1解析为IP地址。这是因为Nginx不使用系统解析器,它是uses its own DNS resolution implementation
要解决这个问题,您可以在Nginx配置中显式指定DNS解析器。它可以是本地网络的DNS服务器,也可以是公共的DNS服务器,如Google的DNS服务器8.8.8.8
您应该将resolver指示词加入http区块中,就在server区块之前,在nginx.conf中:

events {}

http {
    resolver 127.0.0.11;

    server {
        listen 80;

        location ~ ^/vpn_img1/(.*)$ {
            proxy_pass http://vpn_img1:1234/$1;
        }

        location ~ ^/vpn_img2/(.*)$ {
            proxy_pass http://vpn_img2:2345/$1;
        }
    }
}


在上面的配置中,使用resolver 127.0.0.11是因为Docker有一个内置的DNS服务器,它在该IP地址运行,用于为容器提供DNS解析。这应该有助于Nginx将容器名称解析为各自的IP地址。插图/故障排除:“Docker Network Nginx Resolver“的字符串。
另请参阅Adriano Galello中的“如何连接Docker容器”,了解有关Docker DNS服务的更多信息。和“docker-compose internal DNS server 127.0.0.11 connection refused“(如果有问题)。
OP在评论中确认:
location块中添加该解析器和rewrite /vpn_app1(.*) $1 break; proxy_set_header <hostname> "/vpn_app1/";(根据文档)就做到了!
最终的Nginx配置如下所示:

events {}

http {
    resolver 127.0.0.11;

    server {
        listen 80;

        location ~ ^/vpn_img1/(.*)$ {
            rewrite /vpn_img1/(.*) /$1 break;
            proxy_set_header Host $host;
            proxy_pass http://vpn_img1:1234;
        }

        location ~ ^/vpn_img2/(.*)$ {
            rewrite /vpn_img2/(.*) /$1 break;
            proxy_set_header Host $host;
            proxy_pass http://vpn_img2:2345;
        }
    }
}


使用此配置时,对/vpn_img1/.../vpn_img2/...的传入请求在传递到相应的服务之前会被重写为/...proxy_set_header Host $host;行确保传入HTTP请求的主机头被转发到代理服务器。

相关问题