如何通过VPN安全远程访问Docker容器并保障内部通信
问题与解决方案
问题背景
生产服务器上部署了Docker环境,希望从本地机器通过WireGuard访问Postgres和Redis容器,但将这两个容器设置为network_mode: service:wireguard后,导致应用等其他容器无法与它们通信。
目标
- 通过VPN安全访问Postgres和Redis,不将其暴露给公网;
- 允许应用及其他容器正常连接Postgres和Redis。
解决方案:调整网络配置,分离容器网络与VPN转发
核心思路是让Postgres和Redis留在Docker的backend桥接网络中,仅监听该网络的内部IP,同时通过WireGuard容器做端口转发,只允许VPN子网的流量访问这两个服务,既保证本地VPN访问,又不影响容器间通信。
修改后的docker-compose.yml配置
services: wireguard: image: ghcr.io/linuxserver/wireguard container_name: wireguard environment: - PUID=1000 - PGID=1000 - TZ=UTC - SERVER_URL=my_ip_or_domain - SERVER_PORT=51820 - PEERS=5 - PEERDNS=auto - ALLOWEDIPS=10.0.0.0/24 volumes: - ./config/wireguard:/config - /lib/modules:/lib/modules ports: - "51820:51820/udp" # 仅将Postgres和Redis端口转发到WireGuard容器的内部IP,仅VPN子网可访问 - "10.13.13.1:5432:5432" - "10.13.13.1:6379:6379" cap_add: - NET_ADMIN - SYS_MODULE sysctls: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 networks: backend: # 给WireGuard容器分配固定IP,方便端口转发配置 ipv4_address: 10.13.13.1 restart: unless-stopped app: image: myimage:latest container_name: backend_app depends_on: - redis - postgres networks: - backend restart: unless-stopped postgres: image: postgres:latest environment: POSTGRES_DB: '${DB_DATABASE}' POSTGRES_USER: '${DB_USERNAME}' POSTGRES_PASSWORD: '${DB_PASSWORD}' # 让Postgres仅监听backend网络的IP,避免暴露到其他网络 POSTGRES_LISTEN_ADDRESSES: 10.13.13.2 volumes: - 'backend-pgsql:/var/lib/postgresql/data' networks: backend: ipv4_address: 10.13.13.2 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"] interval: 30s timeout: 5s retries: 3 restart: unless-stopped redis: image: redis:alpine volumes: - 'backend-redis:/data' # 让Redis仅监听backend网络的IP command: redis-server --bind 10.13.13.3 networks: backend: ipv4_address: 10.13.13.3 healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 5s retries: 3 restart: unless-stopped networks: backend: driver: bridge # 给backend网络指定固定子网,确保容器IP稳定 ipam: config: - subnet: 10.13.13.0/24 volumes: backend-pgsql: driver: local backend-redis: driver: local
关键改动说明
- 移除Postgres/Redis的
network_mode: service:wireguard,将它们放回backend网络,并分配固定内部IP; - 限制Postgres/Redis的监听地址:
- Postgres通过
POSTGRES_LISTEN_ADDRESSES环境变量指定仅监听自身在backend网络的IP; - Redis通过启动命令
--bind参数指定仅监听自身在backend网络的IP;
- Postgres通过
- WireGuard容器配置固定IP,并添加端口转发规则,仅将Postgres和Redis的端口绑定到WireGuard容器的内部IP(而非主机公网IP);
- 指定backend网络的固定子网,确保容器IP稳定。
应用配置无需修改
原应用配置可以继续使用容器名(postgres/redis)作为连接地址,因为它们在同一个backend网络中,Docker DNS会自动解析到对应容器的IP:
DB_CONNECTION=pgsql DB_HOST=postgres DB_PORT=5432 DB_DATABASE=qa DB_USERNAME=root REDIS_CLIENT=predis REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379
本地VPN访问方式
连接WireGuard VPN后,直接使用WireGuard容器的内部IP(10.13.13.1)作为Postgres和Redis的主机地址,即可访问这两个服务。
其他可选方案
- 方案1:使用WireGuard客户端模式容器:部署单独的WireGuard客户端容器,让Postgres/Redis连接到该容器的网络,同时让客户端容器加入
backend网络,容器间通过backend网络通信,本地通过WireGuard服务器连接客户端所在VPN子网访问服务。 - 方案2:使用Docker macvlan网络:创建macvlan网络,让WireGuard和Postgres/Redis都加入该网络,配置macvlan与主机网络的路由,适合复杂网络场景,但需要主机网卡支持,配置相对复杂。
内容的提问来源于stack exchange,提问作者Wushu06
相关产品推荐
相关产品推荐

