kylemanna / docker-openvpn

🔒 OpenVPN server in a Docker container complete with an EasyRSA PKI CA
https://hub.docker.com/r/kylemanna/openvpn/
MIT License
8.7k stars 2.39k forks source link

Other docker-container in VPN-network #182

Open BirgerK opened 7 years ago

BirgerK commented 7 years ago

Hi there,

I'm using this Image for a long time and I like it! And now I want to add some services which are only accessible inside of the VPN.

Those services shall run as docker-container on the same host like the VPN-container does. The problem: I don't want to route the Docker-IP from every VPN-client inside of the VPN-network. This is not practicable because I got on my VPN-client also a docker-service running which shall not be restricted.

My question: How do I get a docker-container inside of the VPN-network, so it get's (like my VPN-client) a IP from the VPN-network 192.168.254.0?

ofc I have to use the param -c on genconfig but first the container must be reachable in this IP-network.

Thank you for your help!
Birger

BirgerK commented 7 years ago

Would this be a smooth solution? :

I guess this would be ok.

BirgerK commented 7 years ago

Well, it sounds like a great idea, but I don't get it working.

My setup: I got this Image running as a container named my-ovpn. my-ovpn is connected to a docker-network named great-private-stuff. great-private-stuff is f.e. the subnet 172.19.0.0/16, so it's a "normal" docker-network. There's also another container connected to the docker-network great-private-stuff which is running f.e. an nginx and has the ip f.e. 172.19.0.3.

My ovpn-container is started with --privileged --cap=NET_ADMIN. I created the openvpn-config by ovpn-genconfig -u udp://example.com -p "route 172.19.0.0 255.255.0.0" If I connect my client to this ovpn-server, I can see in my routes that the subnet 172.19.0.0/16 will now be routed through the ovpn-tunnel. I can see with traceroute 172.19.0.3 (which is the nginx) that the package halts at 192.168.255.1, which is the tun0-device on serverside. Same thing if I try to traceroute 172.19.0.1 (which is the docker-network-gateway).

But: If my ovpn-container is not connected to the docker-network great-private-stuff, then I can ping 172.19.0.1 but not the nginx connected to it.

What's running wrong?

Thank you, BirgerK

BirgerK commented 7 years ago

TL;DR: How do you route to a container which is connected with your vpn-container by a docker-network?

toBeLikeMike commented 7 years ago

Are you using docker-compose? As far as I understood [1] you could set up a different docker network for the services that should be using your vpn and others that don't. That way you maybe could use the options in Issues 110 or 109 to get the vpn container and the others you want to restrict to work together in one network (option -c and all)

[1] https://docs.docker.com/compose/networking/

BirgerK commented 7 years ago

I prepared a docker-compose-example for you:

version: '2'
services:
  openvpn:
    cap_add:
     - NET_ADMIN
    image: kylemanna/openvpn
    ports:
     - "1194:1194/udp"
    volumes:
     - /tmp/openvpn/conf:/etc/openvpn
    networks:
     public:
       ipv4_address: 172.100.0.10
     private:
       ipv4_address: 172.200.0.10
  webserver:
    image: httpd
    networks:
      public:
        ipv4_address: 172.100.0.20
      private:
        ipv4_address: 172.200.0.20

networks:
  public:
    ipam:
      config:
        - subnet: 172.100.0.0/16
  private:
    ipam:
      config:
        - subnet: 172.200.0.0/16

I create my ovpn-config by:

mkdir -p /tmp/openvpn/conf && docker-compose run --rm openvpn ovpn_genconfig -u udp://<your-local-ip> -p "route 172.200.0.0 255.255.0.0" -p "route 172.100.0.0 255.255.0.0" -c

And run it (locally) by: docker-compose up -d

Now you can ping from your ovpn-container the webserver on his both ips 172.100.0.20 and 172.200.0.20. -> The docker-network works internally.

If I do d exec openvpn_1 ifconfig I get:

eth0      Link encap:Ethernet  HWaddr 02:42:AC:C8:00:0A
          inet addr:172.200.0.10  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fec8:a%32738/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:64:00:0A
          inet addr:172.100.0.10  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:acff:fe64:a%32738/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:648 (648.0 B)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1%32738/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:192.168.255.1  P-t-P:192.168.255.2  Mask:255.255.255.255
          inet6 addr: fe80::6b94:1b2b:e735:1eaa%32738/64 Scope:Link
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          RX bytes:0 (0.0 B)  TX bytes:144 (144.0 B)

There you see that eth0 is connected to subnet 172.200.0.0/16.

This was docker-side.


Now I connect by my openvpn-client to the created and configured ovpn-docker-server. From there I can ping the webserver on IP 172.200.0.20 but NOT on the IP 172.100.0.20.

We can see that the subnet, which is connected to eth0, is reachable but no other docker-networks. At ovpn_run you can see that eth0 is configured for NAT'ing. ofc you can change the interface for NAT'ing but I want to use NAT on all eth*-interfaces.


You know what I mean?

farfeduc commented 7 years ago

@BirgerK Did you activate the client_to_client option in your config ?

I did some research and I think you might have some hints on how to proceed here : http://blog.bobbyallen.me/2016/02/07/enabling-openvpn-clients-to-access-to-the-lan/

I didn't try any of it and I don't now much about openvpn but I am quite interested if you could tell me if you succeeded.

farfeduc commented 7 years ago

To access the other eth* interfaces you might need to create a bridge and add them to the bridge.

This might be the solution according to my research.

BirgerK commented 7 years ago

@farfeduc and then configure the NAT'ing on the created bridge? Would this work? (Never added a bridge before)

EDIT: and yes, I'm already using the client-to-client-option. :) For now I got no solution for this problem.

farfeduc commented 7 years ago

@BirgerK I tried to apply this : http://powtos.fr/255-openvpn-bridge/ but I didn't succeed. May be you might have an idea.

I made a docker image based on this one with bridge-utils : https://github.com/farfeduc/docker-openvpn-bridge in order to create the bridge.

In my image, I work on the 192.168.111.0/24 subnet and my goal is to make my other docker containers on the same network as my openvpn container to be accessed.

But by changing the openvpn.conf I wasn't able to connect to the vpn anymore. I'm trying again today to recreate all of that and maybe do some debug.

As I understood, creating a bridge and switching to a TAP device should make the openvpn server's lan available. So in theory, adding : server-bridge 192.168.111.254 255.255.255.0 192.168.111.100 192.168.111.200 to the openvpn.conf should help gain access to the docker network as if it were the "lan"

olegstepura commented 5 years ago

@BirgerK thanks for hinting. The dirty but working solution is to override function setupIptablesAndRouting in ovpn_env.sh:

setupIptablesAndRouting {
    iptables -t nat -C POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE || {
      iptables -t nat -A POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE
    }
    # olegstepura: add eth1 NAT rules as hinted by https://github.com/kylemanna/docker-openvpn/issues/182#issuecomment-269363050
    # this allows to connect to docker internal network which is eth1
    iptables -t nat -C POSTROUTING -s $OVPN_SERVER -o eth1 -j MASQUERADE || {
      iptables -t nat -A POSTROUTING -s $OVPN_SERVER -o eth1 -j MASQUERADE
    }
    for i in "${OVPN_ROUTES[@]}"; do
        iptables -t nat -C POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE || {
          iptables -t nat -A POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE
        }
    done
}
iedmrc commented 5 years ago

@BirgerK thanks for hinting. The dirty but working solution is to override function setupIptablesAndRouting in ovpn_env.sh:

setupIptablesAndRouting {
    iptables -t nat -C POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE || {
      iptables -t nat -A POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE
    }
    # olegstepura: add eth1 NAT rules as hinted by https://github.com/kylemanna/docker-openvpn/issues/182#issuecomment-269363050
    # this allows to connect to docker internal network which is eth1
    iptables -t nat -C POSTROUTING -s $OVPN_SERVER -o eth1 -j MASQUERADE || {
      iptables -t nat -A POSTROUTING -s $OVPN_SERVER -o eth1 -j MASQUERADE
    }
    for i in "${OVPN_ROUTES[@]}"; do
        iptables -t nat -C POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE || {
          iptables -t nat -A POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE
        }
    done
}

thanks for the solution! I think this solution should be extended for per interface and get merged with source code by maintainers.

vdo commented 5 years ago

@farfeduc I have a similar issue.

I want the clients to get an IP of the defined subnet in the docker-compose , but access it without doing any NATing on the OpenVPN container, just like a 'bridge' to that network.

Is there a way to accomplish that without switching to a TAP device?

Sylvain-Delafoy commented 5 years ago

@iedmrc and @olegstepura: according to man iptables there is a better way put eth+ in OVPN_NATDEVICE that way, all ethx devices get configured by the already existring function. no changes in a bash script required.

hexxone commented 2 years ago

@iedmrc and @olegstepura: according to man iptables there is a better way put eth+ in OVPN_NATDEVICE that way, all ethx devices get configured by the already existring function. no changes in a bash script required.

This works beautifully, thank you! I have searched 10hrs+ for a working and simple solution... and was about to create my own Dockerfile with custom scripts... because I'll probably want to reach more than only "eth0" and "eth1" in the future...

In my case, I have dev-systems on an internal docker-network I want to reach via VPN only (for debugging purposes).

10.10.0.4 is where a pihole lies, for forwarding dns queries to docker (127.0.0.11) and resolving internal dns names. The pihole is also member of all other networks I want to reach. Note: to resolve the hostname of containers you actually have to use <container>.<network>.

10.10.10.0/24 is a docker-network which containers I want to access. This was previously not possible. I could ping the Gateway (10.10.10.2) and resolve the IP of my container (via 10.10.0.4) but not ping. Now with OVPN_NATDEVICE=eth+ it works:

docker-compose run --rm openvpn -e OVPN_NATDEVICE=eth+ openvpn ovpn_genconfig \
-N -c \
-n 10.10.0.4 \ 
-u udp://vpn.domain.com \
-p "route 10.10.0.0 255.255.255.0" \
-p "route 10.10.10.0 255.255.255.0"
nicolas-f commented 1 year ago

An alternative method without manually touching iptables is to use the same network as the openvpn container in your service:

version: '2'
services:
  openvpn:
    cap_add:
     - NET_ADMIN
    image: kylemanna/openvpn
    ports:
     - "1194:1194/udp"
     - "80:8080/tcp" # for service webserver
    volumes:
     - /tmp/openvpn/conf:/etc/openvpn
  webserver:
    image: httpd
    network_mode: "service:openvpn"

The downside is that webserver and openvpn will share the same network interfaces (same ip). The good thing is there is no other configuration to do.