0

I set up a wireguard tunnel between an AWS instance (acting as server) and a personal computer acting as client. Wireguard is installed on both in a docker container (using linuxserver image). On the local computer I have a website that I would like to access from the server using a proxy with Nginx. Basically I want to connect to the ip of the AWS instance and be redirected through the Wireguard tunnel to the website on my local machine. I can curl my website from the docker container of Nginx on the AWS instance but the proxy doesn't work. How can I solve this problem?

AWS instance (Wireguard server and Nginx proxy)

version: "3"
services:
  reverseproxy:
    container_name: reverseproxy
    build: .
    restart: unless-stopped
    network_mode: service:wireguard

  wireguard:
    image: lscr.io/linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Rome
      - SERVERURL=107.22.140.0 #optional
      - SERVERPORT=51820 #optional
      - PEERS=1 #optional
      - PEERDNS=auto #optional
      - INTERNAL_SUBNET=10.0.0.0 #optional
      - ALLOWEDIPS=0.0.0.0/0 #optional
      - LOG_CONFS=true #optional
    volumes:
      - /home/ubuntu/wireguard/config:/config
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
      - 80:80
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

Nginx configuration of the proxy:

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream docker-proxy {
        server 10.0.1.2:80;
    }

    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-Host $server_name;

    server {
        listen 80;
        resolver 127.0.0.11 ipv6=off;
        location / {
            proxy_pass         http://docker-proxy/;
            proxy_redirect     off;
        }
    }
}

Local machine with Wireguard and local website:

version: '3'

services:
    nginx:
        container_name: nginx
        #depends_on:
            #- reverseproxy
        image: nginx:alpine
        restart: unless-stopped
        ports:
            - 80:80
        networks:
            vpn:
                ipv4_address: 10.0.1.2
    wireguard:
        image: lscr.io/linuxserver/wireguard:latest
        container_name: wireguard
        cap_add:
            - NET_ADMIN
            - SYS_MODULE
        environment:
            - PUID=1000
            - PGID=1000
            - TZ=Europe/Rome
            - SERVERURL=wireguard.domain.com #optional
            - SERVERPORT=51820 #optional
            - PEERS= #optional
            - PEERDNS=auto #optional
            - INTERNAL_SUBNET=10.0.0.0 #optional
            - ALLOWEDIPS=0.0.0.0/0 #optional
            - LOG_CONFS=true #optional
        volumes:
            - /home/user/dev/nginx-proxy/config:/config
            - /lib/modules:/lib/modules
        ports:
            - 51820:51820/udp
        sysctls:
            - net.ipv4.conf.all.src_valid_mark=1
        restart: unless-stopped
        networks:
            vpn:
                ipv4_address: 10.0.1.5
    
networks:
    vpn:
        ipam:
            config:
                - subnet: 10.0.1.0/8 
Rickj
  • 3
  • 2
  • At the first sight I don't see any problems. Did you try packet capturing to see how packets go (or don't go) through the network? – Nikita Kipriyanov Jun 25 '22 at 14:25
  • No, how could I do that? Should I use ```tcpdump``` on the host or on one of the containers? – Rickj Jun 25 '22 at 15:03
  • Try doing it everywhere. For example, when you're going to listen on the wireguard interface, you have to run it wherever wireguard is running. And on both sides, to confirm that packets that entered the tunnel exit it and then entered the correct Nginx container. I don't really know your complete network layout, so it's hard to give better suggestions. – Nikita Kipriyanov Jun 25 '22 at 15:08

1 Answers1

1

If all you want is port forwarding from your AWS server, you don't need nginx, you can just use an iptables rule in your WireGuard container to forward port 80.

On your AWS server, save this WireGuard config file in some directory, like as server/wireguard/wg0.conf:

# server/wireguard/wg0.conf

# local settings for AWS server
[Interface]
PrivateKey = <server private key>
Address = 10.0.0.1/32
ListenPort = 51820

# port forwarding to Docker `nginx` service on `vpn` network on personal computer
PreUp = iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.1.2
# masquerading for Internet traffic to Docker `vpn` network on personal computer
PreUp = iptables -t nat -A POSTROUTING -d 10.0.1.0/24 -j MASQUERADE

# remote settings for personal computer
[Peer]
PublicKey = <client public key>
AllowedIPs = 10.0.0.2/32, 10.0.1.0/24

Replace <server private key> with the WireGuard private key you generated for your server, and <client public key> with the WireGuard public key you generated for your client.

Then save this Docker Compose file in the directory above it, like as server/docker-compose.yml:

# server/docker-compose.yml

version: '3'
services:
  wireguard:
    image: procustodibus/wireguard
    cap_add:
    - NET_ADMIN
    ports:
    - 80:80
    - 51820:51820/udp
    volumes:
    - ./wireguard:/etc/wireguard

And run docker-compose up from the same directory as the Docker Compose file.

Next, on your personal computer, save this WireGuard config file in some directory, like as client/wireguard/wg0.conf:

# client/wireguard/wg0.conf

# local settings for personal computer
[Interface]
PrivateKey = <client private key>
Address = 10.0.0.2/32
ListenPort = 51820

# masquerading for WireGuard traffic to Docker `vpn` network on personal computer
PreUp = iptables -t nat -A POSTROUTING -d 10.0.1.0/24 -j MASQUERADE

# remote settings for AWS server
[Peer]
PublicKey = <server public key>
AllowedIPs = 10.0.0.1/32
Endpoint = <server ip address or domain name>:51820
PersistentKeepalive = 25

Replace <client private key> with the WireGuard private key you generated for your client, and <server public key> with the WireGuard public key you generated for your server. Replace <server ip address or domain name> with the public IP address or domain name of your AWS server.

Then save this Docker Compose file in the directory above it, like as client/docker-compose.yml:

# client/docker-compose.yml

version: '3'

services:
  nginx:
    image: nginx
    networks:
      vpn:
        ipv4_address: 10.0.1.2

  wireguard:
    image: procustodibus/wireguard
    cap_add:
    - NET_ADMIN
    networks:
      vpn:
        ipv4_address: 10.0.1.5
    ports:
    - 51820:51820/udp
    volumes:
    - ./wireguard:/etc/wireguard

networks:
  vpn:
    ipam:
      config:
      - subnet: 10.0.1.0/24

And run docker-compose up from the same directory as the Docker Compose file.


However, if you do need nginx on your AWS server (for example to preserve the real IP address of HTTP clients, or to terminate TLS, etc), skip the port-forwarding iptables rule in your AWS server's WireGuard config:

# server/wireguard/wg0.conf

# local settings for AWS server
[Interface]
PrivateKey = <server private key>
Address = 10.0.0.1/32
ListenPort = 51820

# masquerading for Internet traffic to Docker `vpn` network on personal computer
PreUp = iptables -t nat -A POSTROUTING -d 10.0.1.0/24 -j MASQUERADE

# remote settings for personal computer
[Peer]
PublicKey = <client public key>
AllowedIPs = 10.0.0.2/32, 10.0.1.0/24

Add save your reverse-proxy nginx config file on the AWS server in a directory that's a sibling of your WireGuard config, like server/reverseproxy/nginx.conf:

# server/reverseproxy/nginx.conf

events {}
http {
    server {
        listen 80;
        location / {
            proxy_pass http://10.0.1.2:80;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

Use whatever proxy directives (or other nginx config) you want in this config file (the original reverse-proxy config from your question should work fine) -- just make sure the upstream server address and port matches the address and port used by the Docker nginx service in the vpn network on your personal computer.

Then add this reverseproxy service to the Docker Compose config on your AWS server:

# server/docker-compose.yml

version: '3'

services:
  reverseproxy:
    image: nginx
    network_mode: service:wireguard
    volumes:
    - ./reverseproxy:/etc/nginx

  wireguard:
    image: procustodibus/wireguard
    cap_add:
    - NET_ADMIN
    ports:
    - 80:80
    - 51820:51820/udp
    volumes:
    - ./wireguard:/etc/wireguard

On your personal computer, use the same "client" WireGuard and Docker Compose config from the first part of this answer.

Justin Ludwig
  • 1,006
  • 7
  • 8
  • Thank you for the clear response! I tried both methods but I wasn't able to get any of them to work. Without using a proxy on the AWS I get `Empty reply from server` when running `curl localhost` on the AWS server itself and nothing when running curl from my computer. Adding the proxy I get the web page of my local machine but only when running `curl localhost` on the AWS server. – Rickj Jul 06 '22 at 18:07
  • 1) Do your EC2 security-group rules allow you to connect to port 80 on the AWS server from your computer? (try running a plain nginx container on port 80 of the AWS server to double-check) 2) When testing, make sure you always (re-)start the "client" containers (on your computer) after you (re-)start the "server" containers (on the AWS server) -- otherwise you may have to wait a couple minutes for the client to forget its old WireGuard connection state and re-connect to the server – Justin Ludwig Jul 07 '22 at 02:13