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.68k stars 2.38k forks source link

OpenVPN with IPv6 (ipv6 NAT and public ipv6) #366

Open christianbur opened 6 years ago

christianbur commented 6 years ago

I use docker-ipv6nat to supply my vpn clients with ipv6. I know nat and ipv6 is not nice, but necessary for dockers somehow.

I use mailcow-dockerized, so I already had docker-ipv6nat in use, so I just had to do the following.

ipv6nat in the docker-compose.yml form mailcow

    ipv6nat:
      image: robbertkl/ipv6nat
      restart: always
      privileged: true
      network_mode: "host"
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
        - /lib/modules:/lib/modules:ro

my docker-compose.yml for openvpn (ipv6nat can also include in this docker-compose file).

version: '2.1'
services:
  openvpn:
      cap_add:
        - NET_ADMIN
      image: kylemanna/openvpn
      container_name: openvpn
      environment:
        - EASYRSA_KEY_SIZE=4096
      ports:
       - "91194:1194/udp"
      restart: always
      volumes:
       - ./data/:/etc/openvpn/
      sysctls:
        - net.ipv6.conf.all.disable_ipv6=0
        - net.ipv6.conf.default.forwarding=1
        - net.ipv6.conf.all.forwarding=1
      networks:
        network-openvpn:

networks:
  network-openvpn:
    enable_ipv6: true
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.27.1.0/24
        - subnet:  fdcb:37eb:3cf0:73c3::/64

when openvpn starts, the following errors are displayed in the logs, but this is not bad, since the values are set via the sysctls in the docker-compose file. (checked with: docker-compose exec openvpn sysctl -a)

openvpn    | Enabling IPv6 Forwarding
openvpn    | sysctl: error setting key 'net.ipv6.conf.all.disable_ipv6': Read-only file system
openvpn    | Failed to enable IPv6 support
openvpn    | sysctl: error setting key 'net.ipv6.conf.default.forwarding': Read-only file system
openvpn    | Failed to enable IPv6 Forwarding default
openvpn    | sysctl: error setting key 'net.ipv6.conf.all.forwarding': Read-only file system

my ovpn_genconfig command docker-compose run --rm openvpn ovpn_genconfig -e 'topology subnet' -e 'tun-ipv6' -e 'server-ipv6 fdcb:37eb:3cf0:73c3:aaaa::/112' -e 'push "route-ipv6 2000::/3"' -e 'push "redirect-gateway ipv6"' -C 'AES-256-CBC' -a 'SHA384' -z -u "udp://vpn.example.de:91194"

or add commands manual to data/openvpn.conf

tun-ipv6
server-ipv6 fdcb:37eb:3cf0:73c3:aaaa::/112
push "route-ipv6 2000::/3"
push "redirect-gateway ipv6"

fdcb:37eb:3cf0:73c3:aaaa::/112 and fdcb:37eb:3cf0:73c3::/64 are unique local address (ULA) the /112 range must be within the /64 range otherwise the nat of docker-ipv6nat does not work.

root@server:# ip6tables -t nat -L -v -n

Chain POSTROUTING (policy ACCEPT 2570 packets, 208K bytes)
 pkts bytes target     prot opt in     out     source               destination         
   26  2184 MASQUERADE  all      *      !br-95815b8003ec  fdcb:37eb:3cf0:73c3::/64  ::/0                
    0     0 MASQUERADE  udp      *      *       fdcb:37eb:3cf0:73c3::2  fdcb:37eb:3cf0:73c3::2  udp dpt:1194

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    5   590 DNAT       udp      !br-95815b8003ec *       ::/0                 ::/0                 udp dpt:91194 to:[fdcb:37eb:3cf0:73c3::2]:1194

set rout on the docker host ip -6 route add fdcb:37eb:3cf0:73c3:aaaa::/112 via $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' openvpn) Question: does anyone know an automatic way to run this command on the host always after starting the openvpn container?

now the vpn clients have ipv6, but only with nat (on http://ipv6-test.com/ the ipv6 address of the docker host is displayed).

Without nat it would be nicer, but it is easier and you don't have the problem that the vpn clients with ipv6 bypass the tunnel (because they establish an ipv6 connection directly without vpn). when your vpn clients has an dualstack conneciton (ipv4 and ipv6), then test it on http://ipv6-test.com/

christianbur commented 6 years ago

when i finished the first post, i simply tested it with public ipv6 addresses.

my vserver (Ubuntu 16.04.4 LTS) has a /64, normally you don't subnet this IP range further, otherwise functions like SLAAC won't work anymore. For OpenVPN this is not relevant so the range can be smaller, I mean /112 is the smallest usable ipv6 range in OpenVPN.

my /64 ipv6 range: 2a03:4000:6:aaaa::0/64 my vserver ipv6 address 2a03:4000:6:aaaa::1/64

i split the /64 ip range in a samller /111 ip range. The /111 ipv6 range consists of two /112

2a03:4000:6:aaaa::100:0/111 = 2a03:4000:6:aaaa::100:0/112 + 2a03:4000:6:aaaa::101:0/112

2a03:4000:6:aaaa::100:0/112  -> for the openvpn server
2a03:4000:6:aaaa::101:0/112  -> for the openvpn clients

the following settings must be set on the vserver, check with sysctl -a

        net.ipv6.conf.default.forwarding=1
        net.ipv6.conf.all.forwarding=1
        net.ipv6.conf.all.proxy_ndp = 1

my docker-compose.yml


version: '2.1'
services:
  openvpn:
      cap_add:
        - NET_ADMIN
      image: kylemanna/openvpn
      container_name: openvpn
      environment:
        - EASYRSA_KEY_SIZE=4096
      ports:
       - "51194:1194/udp"
      restart: always
      volumes:
       - ./data/:/etc/openvpn/
      sysctls:
        - net.ipv6.conf.all.disable_ipv6=0
        - net.ipv6.conf.default.forwarding=1
        - net.ipv6.conf.all.forwarding=1
      networks:
        network-openvpn:

networks:
  network-openvpn:
    enable_ipv6: true
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.27.1.0/24
        - subnet: 2a03:4000:6:aaaa::100:0/111

add commands manual to data/openvpn.conf or use the "docker-compose run --rm openvpn ovpn_genconfig" command (see above).

tun-ipv6
server-ipv6 2a03:4000:6:aaaa::101:0/112
push "route-ipv6 2000::/3"
push "redirect-gateway ipv6"

add route to client ipv6 range ip -6 route add 2a03:4000:6:aaaa::101:0/112 via $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' openvpn)

Now the vpn client ipv6 addresses must be made known to the router of the provider with NDP. The solution is not yet optimal, if necessary this has to be done with a small script for the whole /112 network. (eth0 = external vserver interface)

2a03:4000:6:aaaa::101:1000 -> vpn client 1
2a03:4000:6:aaaa::101:1001 -> vpn client 2
/sbin/ip -6 neigh add proxy 2a03:4000:6:aaaa::101:1000 dev eth0
/sbin/ip -6 neigh add proxy 2a03:4000:6:aaaa::101:1001 dev eth0

But ATTENTION, now the openvpn server and the vpn client have a public ipv6 address without firewall, so all open ports should be accessible directly from the internet. At the moment I use the ULA ipv6 area (fdcb:37eb:3cf0:73c3::/64)and not the public ipv6 area (2a03:4000:6:aaaa::100:0/111) for the Openvpn server.

my docker-compose.yml

      ...
      config:
        - subnet: 172.27.1.0/24
        - subnet:  fdcb:37eb:3cf0:73c3::/64
christianbur commented 6 years ago

this makes the routes easier to set, assuming eth0 is as external interface and it has only one global ipv6 address.

export GLOBAL_IPV6_PREFIX=$(ip addr show dev eth0 | sed -e's/^.*inet6 \([^ ]*\)::.* global .*$/\1/;t;d')
ip -6 route add ${GLOBAL_IPV6_PREFIX}::101:0/112 via $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.GlobalIPv6Address}}{{end}}' openvpn)

/sbin/ip -6 neigh add proxy ${GLOBAL_IPV6_PREFIX}::101:1000 dev eth0
/sbin/ip -6 neigh add proxy ${GLOBAL_IPV6_PREFIX}::101:1001 dev eth0
sothy commented 6 years ago

Hello, when I run this command, version: '2.1'

services: openvpn: image: kylemanna/openvpn:latest cap_add:

networks: network-openvpn: enable_ipv6: true driver: bridge ipam: driver: default config:

Docker got error when starting. Error is
iptables v1.6.1: invalid mask 64' specified Tryiptables -h' or 'iptables --help' for more information. iptables v1.6.1: invalid mask 64' specified Tryiptables -h' or 'iptables --help' for more information. iptables v1.6.1: invalid mask `64' specified

ANy clue?

johansmitsnl commented 5 years ago

Can it also work with a docker nat like this:

version: '2.1'

# Services
services:
  ipv6nat:
    restart: always
    image: robbertkl/ipv6nat
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /lib/modules:/lib/modules:ro
    privileged: true
    network_mode: "host"

  openvpn:
    restart: always
    image: kylemanna/openvpn:latest
    hostname: vpn
    domainname: smitsmail.net
    container_name: openvpn
    ports:
      - "1194:1194/udp"
    volumes:
      - ./openvpn/data:/etc/openvpn
    cap_add:
      - NET_ADMIN
    sysctls:
     - net.ipv6.conf.all.disable_ipv6=0
     - net.ipv6.conf.default.forwarding=1
     - net.ipv6.conf.all.forwarding=1
    networks:
     - vpn_net

networks:
  vpn_net:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.0.0.0/24
        - subnet: fd00:aaaa:cccc::/48

openvpn.conf:

### IPv6 settings
tun-ipv6
push tun-ipv6
ifconfig-ipv6 fd00:aaaa:cccc:1::2 fd00:aaaa:cccc:1::3
server-ipv6 fd00:aaaa:cccc:1::/64
push "route-ipv6 2000::/3"

I can ping the gateway ping fd00:aaaa:cccc:1::1 from the client. I can ping back from the container but I have to define the interface: ping -I tun0 fd00:aaaa:cccc:1::1000. But I can't route from the client to other IPv6 addresses. But from the container I can access and ping all IPv6 addresses. It seems that it is not correctly routed?