Docker-openvpn: how to nginx-proxy into openvpn docker container

Created on 25 Feb 2018  路  7Comments  路  Source: kylemanna/docker-openvpn

I am trying to use DigitalOcean VPS as a openVPN server to access services (e.g. nextcloud) hosted on my home network through subdomains (e.g. nextcloud.example.com).

I have set up the following:

  • [working] kylemanna/openvpn docker on Digital Ocean VPS
  • [working] Connected my home pfSense router as VPN client to Digital Ocean VPS
  • [working] Set up the nextcloud service on my home network
  • [working] When connected to the VPN, I can ping between devices and also access the nextcloud service through the internal IP
  • [Not working] jwilder/nginx-proxy to route nextcloud.example.com through the Docker VPN tunnel to nextcloud's internal IP

I have tried to add a virtual_host file for nextcloud.example.com to nginx-proxy routing request to the openvpn port 3000, and then within the openvpn container using iptables to forward all request on port 3000 to the internal nextcloud IP.

Would really appreciate any help as I am to be honest a bit in the deep end here?

kylemanna/openvpn - iptables config forwarding

    user@Debianwebhost:~$ docker exec -it vpn bash
    bash-4.4# iptables -t nat -L
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    DNAT       tcp  --  anywhere             anywhere             tcp dpt:3000 to:192.168.0.99:80
    DNAT       udp  --  anywhere             anywhere             udp dpt:3000 to:192.168.0.99:80
    DNAT       udp  --  anywhere             anywhere             udp dpt:3000 to:192.168.0.99:80

    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination

    Chain POSTROUTING (policy ACCEPT)
    target     prot opt source               destination
    SNAT       tcp  --  anywhere             192.168.0.99         tcp dpt:http to:172.17.0.2:3000
    SNAT       udp  --  anywhere             192.168.0.99         udp dpt:http to:172.17.0.2:3000

nginx-proxy virtual host config

user@Debianwebhost:/etc/nginx/vhost.d$ cat nextcloud.example.com
server {
        server_name _; # This is just an invalid value which will never trigger on a real hostname.
        listen 80;
        access_log /var/log/nginx/access.log vhost;
        return 503;
}
# nextcloud.example.com
upstream nextcloud.example.com {
                                ## Can be connect with "bridge" network
                        # vpn
                        server 172.17.0.2:3000;
}
server {
        server_name nextcloud.example.com;
        listen 80 ;
        access_log /var/log/nginx/access.log vhost;
        location / {
                proxy_pass http://nextcloud.example.com;
        }
}

nginx-proxy nginx.conf

    user@Debianwebhost:/etc/nginx$ cat nginx.conf

    user  nginx;
    worker_processes  auto;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;


    events {
        worker_connections  1024;
    }


    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;
        #tcp_nopush     on;

        keepalive_timeout  65;

        #gzip  on;

        include /etc/nginx/conf.d/*.conf;
    }

nginx-proxy default.conf

    user@Debianwebhost:/etc/nginx/conf.d$ cat default.conf
    # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
    # scheme used to connect to this server
    map $http_x_forwarded_proto $proxy_x_forwarded_proto {
      default $http_x_forwarded_proto;
      ''      $scheme;
    }
    # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
    # server port the client connected to
    map $http_x_forwarded_port $proxy_x_forwarded_port {
      default $http_x_forwarded_port;
      ''      $server_port;
    }
    # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
    # Connection header that may have been passed to this server
    map $http_upgrade $proxy_connection {
      default upgrade;
      '' close;
    }
    # Apply fix for very long server names
    server_names_hash_bucket_size 128;
    # Default dhparam
    ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
    # Set appropriate X-Forwarded-Ssl header
    map $scheme $proxy_x_forwarded_ssl {
      default off;
      https on;
    }
    gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    log_format vhost '$host $remote_addr - $remote_user [$time_local] '
                     '"$request" $status $body_bytes_sent '
                     '"$http_referer" "$http_user_agent"';
    access_log off;
    resolver [hidden ips, but there are 2 of them];
    # HTTP 1.1 support
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $proxy_connection;
    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 $proxy_x_forwarded_proto;
    proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
    proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
    # Mitigate httpoxy attack (see README for details)
    proxy_set_header Proxy "";
    server {
            server_name _; # This is just an invalid value which will never trigger on a real hostname.
            listen 80;
            access_log /var/log/nginx/access.log vhost;
            return 503;
    }
    # nextcloud.example.com
    upstream nextcloud.example.com {
                                    ## Can be connect with "bridge" network
                            # vpn
                            server 172.17.0.2:3000;
    }
    server {
            server_name nextcloud.example.com;
            listen 80 ;
            access_log /var/log/nginx/access.log vhost;
            location / {
                    proxy_pass http://nextcloud.example.com;
            }
    }

Most helpful comment

Just in case, I found solution several month ago. And it works fine and pretty stable.

This is how it looks like:

docker-compose.yml

version: "3"

services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    depends_on:
      - ovpn
    network_mode: "service:ovpn"
    volumes:
      - ./nginx/conf.d/:/etc/nginx/conf.d/:ro
      - ./nginx/certs/:/etc/nginx/certs/:ro
      - ./nginx/services.d/:/etc/nginx/services.d/:ro
    restart: unless-stopped

  ovpn:
    image: kylemanna/openvpn
    container_name: ovpn
    privileged: true
    ports:
      - 1194:1194/udp
      - 443:443
      - 80:80
    volumes:
      - ./ovpn-server/conf:/etc/openvpn
    devices:
      - /dev/net/tun
    cap_add:
      - NET_ADMIN
    extra_hosts:
      - server.one:10.0.0.33
      - server.two:10.0.0.34
    restart: unless-stopped

nginx/services.conf

# WebSockets support
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;

    server_name domain.com www.domain.com;

    include /etc/nginx/services.d/domain.com.conf;
}

server {
    listen 80;
    listen [::]:80;

    server_name one.domain.com *.one.domain.com;

    include /etc/nginx/services.d/one.domain.com.conf;
}

server {
    listen 80;
    listen [::]:80;

    server_name two.domain.com *.two.domain.com;

    include /etc/nginx/services.d/two.domain.com.conf;
}

ovpn-server/conf/openvpn.conf

server 10.0.0.0 255.255.255.0

verb 3
key /etc/openvpn/pki/private/sycret.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/sycret.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key

key-direction 0
keepalive 10 60
persist-key
persist-tun

proto udp
port 1194
dev tun0

status /tmp/openvpn-status.log

topology subnet

user nobody
group nogroup
comp-lzo no
comp-noadapt

client-to-client

client-config-dir ccd

### Route Configurations Below
route 10.0.0.0 255.255.255.0

ovpn-server/conf/ccd/server.one

ifconfig-push 10.0.0.33 255.255.255.0

ovpn-server/conf/ccd/server.two

ifconfig-push 10.0.0.34 255.255.255.0

All 7 comments

Hello, @Svarto , you have this issue closed, does that mean you've got a success on that? I have similar setup and want to get an access to we service hosted on VPN client using public domain.
I use haproxy container and kylemanna openvpn container. Could you help me out?

Sure, how far along are you? Have you tried what I wrote in SuperUser on the same issue: https://superuser.com/a/1302909/869159

Let me know if that works?

Actually I have something like that schema

And when I request my public domain assigned to 155.55.55.55 it follows trough the host machine to haproxy container. Haproxy service move request to the 192.168.20.10 (OpenVPN server container) and... finish.
I suppose taht I have to configure something and somehow inside OpenVPN service container which is based on kylemanna/docker-openvpn docker image. But how? Have no idea.

Btw. if I request 10.10.0.2 from within openvpn-server container everything works well. I know it because I tried to run curl -Lv 10.10.0.2 after docker exec -it openvpn-server bash and I have response from haproxy on the appliance painted on the right.

So, that is why I think that my issue is hidden inside openvpn-server container.

Just in case, the result of # iptables -t nat -L from within openvpn-server container is:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER_OUTPUT  all  --  anywhere             localhost

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER_POSTROUTING  all  --  anywhere             localhost
MASQUERADE  all  --  10.10.0.0/24         anywhere

Chain DOCKER_OUTPUT (1 references)
target     prot opt source               destination
DNAT       tcp  --  anywhere             localhost            tcp dpt:domain to:127.0.0.11:44269
DNAT       udp  --  anywhere             localhost            udp dpt:domain to:127.0.0.11:60479

Chain DOCKER_POSTROUTING (1 references)
target     prot opt source               destination
SNAT       tcp  --  localhost            anywhere             tcp spt:44269 to::53
SNAT       udp  --  localhost            anywhere             udp spt:60479 to::53

but I don't understand what does it say because of my knowledge of routing is too poor

What are your ip routes for the machine / VPS you have on the left in your diagram?

The curl -Lv 10.10.0.2 does not actually check if you can reach the haproxy, it only checks if it can reach the client inside the VPN. You have still not confirmed that your requests are routed to the proper subnet through the client.

Have you set any routes within openvpn, i.e. could you please post your server configuration file? It is found under etc/openvpn/openvpn.conf inside the docker container.

This is my docker-compose.yml

version: "3"

services:
  ovpn:
    image: kylemanna/openvpn
    container_name: ovpn
    privileged: true
    ports:
      - "1194:1194/udp"
    volumes:
      - ./ovpn-server/conf:/etc/openvpn
    devices:
      - /dev/net/tun
    cap_add:
      - NET_ADMIN
    networks:
      home-vpn:
        aliases:
          - home
    extra_hosts:
      - home:10.10.0.10
    restart: unless-stopped

  haproxy:
    image: haproxy:latest
    container_name: haproxy
    depends_on:
      - ovpn
    ports:
      - 443:443
      - 80:80
    volumes:
      - ./haproxy:/usr/local/etc/haproxy
    networks:
      home-vpn:
    restart: unless-stopped

networks:
  home-vpn:

This is haproxy.cnf

    daemon

    maxconn 2048
    tune.ssl.default-dh-param 2048
    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # Default ciphers to use on SSL-enabled listening sockets.
    # For more information, see ciphers(1SSL). This list is from:
    #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
    # An alternative list with additional directives can be obtained from
    #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3

defaults
    mode http
    timeout connect 5000ms
    timeout client 5000ms
    timeout server 5000ms
    option forwardfor
    option http-server-close

frontend home-http
    bind *:80
    reqadd X-Forwarded-Proto:\ http
    default_backend home-backend

frontend home-https
    bind *:443 ssl crt /usr/local/etc/haproxy/certs/home.domain.com.pem
    reqadd X-Forwarded-Proto:\ https
    default_backend home-backend

backend home-backend
    redirect scheme https if !{ ssl_fc }
    server home.domain.com home check

This is openvpn.conf

server 10.10.0.0 255.255.255.0

verb 3
key /etc/openvpn/pki/private/home.domain.com.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/home.domain.com.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key

key-direction 0
keepalive 10 60
persist-key
persist-tun

proto udp
port 1194
dev tun0

status /tmp/openvpn-status.log

topology subnet

user nobody
group nogroup
comp-lzo no

client-to-client

client-config-dir ccd

### Route Configurations Below
route 10.10.0.0 255.255.255.0

push "comp-lzo no"

And the home client specific configuration placed in ccd directory:

ifconfig-push 10.10.0.10 255.255.255.0

Just in case, I found solution several month ago. And it works fine and pretty stable.

This is how it looks like:

docker-compose.yml

version: "3"

services:
  nginx:
    image: nginx:alpine
    container_name: nginx
    depends_on:
      - ovpn
    network_mode: "service:ovpn"
    volumes:
      - ./nginx/conf.d/:/etc/nginx/conf.d/:ro
      - ./nginx/certs/:/etc/nginx/certs/:ro
      - ./nginx/services.d/:/etc/nginx/services.d/:ro
    restart: unless-stopped

  ovpn:
    image: kylemanna/openvpn
    container_name: ovpn
    privileged: true
    ports:
      - 1194:1194/udp
      - 443:443
      - 80:80
    volumes:
      - ./ovpn-server/conf:/etc/openvpn
    devices:
      - /dev/net/tun
    cap_add:
      - NET_ADMIN
    extra_hosts:
      - server.one:10.0.0.33
      - server.two:10.0.0.34
    restart: unless-stopped

nginx/services.conf

# WebSockets support
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;

    server_name domain.com www.domain.com;

    include /etc/nginx/services.d/domain.com.conf;
}

server {
    listen 80;
    listen [::]:80;

    server_name one.domain.com *.one.domain.com;

    include /etc/nginx/services.d/one.domain.com.conf;
}

server {
    listen 80;
    listen [::]:80;

    server_name two.domain.com *.two.domain.com;

    include /etc/nginx/services.d/two.domain.com.conf;
}

ovpn-server/conf/openvpn.conf

server 10.0.0.0 255.255.255.0

verb 3
key /etc/openvpn/pki/private/sycret.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/sycret.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key

key-direction 0
keepalive 10 60
persist-key
persist-tun

proto udp
port 1194
dev tun0

status /tmp/openvpn-status.log

topology subnet

user nobody
group nogroup
comp-lzo no
comp-noadapt

client-to-client

client-config-dir ccd

### Route Configurations Below
route 10.0.0.0 255.255.255.0

ovpn-server/conf/ccd/server.one

ifconfig-push 10.0.0.33 255.255.255.0

ovpn-server/conf/ccd/server.two

ifconfig-push 10.0.0.34 255.255.255.0

Thank you for taking the time to post the solution.
I will try it soon and give you a feedback...
Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wilbertliu picture wilbertliu  路  5Comments

lucaszanella picture lucaszanella  路  7Comments

josjaf picture josjaf  路  5Comments

Awatatah picture Awatatah  路  5Comments

patrick-mota picture patrick-mota  路  8Comments