Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Advertise on LowEndTalk.com
OpenVPN with public IPv6 for clients, port forwarding and HTTPS encapsulation
New on LowEndTalk? Please read our 'Community Rules' by clicking on it in the right menu!

OpenVPN with public IPv6 for clients, port forwarding and HTTPS encapsulation

mxp13mxp13 Member
edited June 2017 in Tutorials

Hi!

First and foremost, I wish to state that I do not have extensive knowledge in the matters of what I am sharing here. I am writing this a form of documentation of the processes I went through and decided to share it to those who are interested.

Introduction

That said, in this post, I am going to share the way I setup my OpenVPN on my VPS with IPv6 enabled which assigns public IPv6 addresses to clients connected to it. This is perfect for people who want a public IPv6 address but do not have native IPv6 on their internet connection. You will need a VPS which has an IPv6 subnet (a /64 will do just fine). It does not matter if it is 'routed' to you or you need to manually assign addresses from within the VPS control panel such as SolusVM. In my case, it is the latter. If you use a tunnel broker service such as Hurricane Electric's Tunnel Broker [1], you may refer to this guide [2] (it is an inspiration for this guide as well). As for IPv4, either public or NAT address works just fine.

In addition, since OpenVPN traffic is distinguishable from normal HTTPS traffic that setting up OpenVPN on port 443 would not be able to fool some advanced firewalls, I will also be sharing how I encapsulate the OpenVPN traffic in HTTPS using a combination of TLS/SSL tunneling application and a protocol multiplexer application so that passive firewalls (which just observe traffic as compared to an active firewall which probes the connection; refer to this relevant information [3]) would not be able to tell that the payload is the OpenVPN traffic.

Since we are using a protocol multiplexer which supports connection to multiple protocol under a single HTTPS connection and directs the traffic based on their pattern, we are going to install a simple web server as well that is running on port 443 (or any port of your choosing) so that -- for instance -- if people are curious about the connection and decide to enter the VPS IP using a browser will be connected to the web server instead of the OpenVPN server.

As a bonus, inspired by an iptables tutorial [4], we are also including a few iptables rules for the purposes of port forwarding to be used by our OpenVPN clients just in case they wish to have the ability to bind a port so the outside world may connect to them using IPv4. If you are using a NAT VPS, these ports will be made available to you by your host. As for IPv6 this is not necessary as we are assigning globally routable addresses for them.

This guide is based on my OS install which is Debian 9 (Stretch) on a KVM platform. Your mileage may vary depending on your OS and virtualisation technology. I apologise as I might be unable to help if your setup is different than what I have.

Please note that all commands need to be run as root.

The application and script we will be using are:

  • OpenVPN [5] (using Nyr's script [6])
  • sslh [7]
  • stunnel [8]
  • nginx [9]

Steps

  1. Add a subnet from our /64 (for example a /112) for our OpenVPN server. This will be dependent upon two things:

    [1a] If you have a 'routed' /64 (that is, the provider routed a subnet directly to your single /64 address), then you do not have to do anything on this stage.

    [1b] If your /64 is assigned to a control panel such as SolusVM (as with the case of many providers), then you will need to manually add the addresses from within the panel. You will need to add three types of address (let us assume our /64 subnet is 2001:0db8:85a3:1ab2::/64):

    • Main VPS address: This one you can just assign any address from your /64 as it will be used as the primary IPv6 address of your VPS. Example in our scenario: 2001:0db8:85a3:1ab2::1

    • Main OpenVPN server address: In our case, we choose a /112 subnet and assigned a primary IP to be used as the main OpenVPN server address. For example, as the subnet for our VPS is 2001:0db8:85a3:1ab2::/64, we are going to assign 2001:0db8:85a3:1ab2:0:0:2b1a::/112 for OpenVPN. With that, we choose 2001:0db8:85a3:1ab2:0:0:2b1a:1 as the main OpenVPN server address, so we add that IP to SolusVM.

    • OpenVPN clients addresses: By default, OpenVPN assigns addresses from :1000 (example: 2001:0db8:85a3:1ab2:0:0:2b1a:1000). This one depends on your preference. If you have five clients, then add :1000 until :1004. Please note that IPv6 uses hexadecimal addressing, so if you add 11 clients, the 10th client would be :1009 and the 11th client would be :100a (followed until :100f, then :1010, and so on). Note also the subnet. In our case, it is a /112, so we need to add ::2b1a:1000, ::2b1a:1001, and so on in the control panel.

    For this guide, we are going to assume we have the following IP information:

        VPS IPv4 IP: 8.8.8.100
        VPS IPv4 NM: 255.255.255.0
        VPS IPv4 GW: 8.8.8.1
    
        VPS IPv6 IP: 2001:0db8:85a3:1ab2::1
        VPS IPv6 NM: /48
        VPS IPv6 GW: 2001:0db8:85a3::1
    
        OVPN IPv4 IP: 10.8.0.1 (server)
                      10.8.0.2 and above (clients)
        OVPN IPv4 NM: /24
    
        OVPN IPv6 IP: 2001:0db8:85a3:1ab2:0:0:2b1a:1 (server)
                      2001:0db8:85a3:1ab2:0:0:2b1a:1000 and above (clients)
        OVPN IPv6 NM: /112
    

    Please refer to the information provided by your host regarding gateway and netmask settings, especially for IPv6.

  2. Apply the IP information to our /etc/network/interfaces file. Please take note the interface name (check using "ip addr" command; in my case it is "enp0s3") I prefer to rewrite the file using this template:

    source /etc/network/interfaces.d/*
    
    auto lo
    iface lo inet loopback
    iface lo inet6 loopback
    
    auto enp0s3
    iface enp0s3 inet static
            address 8.8.8.100
            netmask 255.255.255.0
            gateway 8.8.8.1
    iface enp0s3 inet6 static
            address 2001:0db8:85a3:1ab2::1
            netmask 48
            gateway 2001:0db8:85a3::1
    
  3. Edit your /etc/resolv.conf to include both IPv4 and IPv6 DNS addresses. In my case it is Google DNS:

    nameserver 8.8.8.8
    nameserver 8.8.4.4
    nameserver 2001:4860:4860::8888
    nameserver 2001:4860:4860::8844
    
  4. Install OpenVPN using Nyr's script [6]. During setup, choose TCP and not UDP. You may choose any port (I suggest the default port) but if you use NAT VPS, please use the provided port by your host and take note of it.

  5. Add these lines to the end of /etc/openvpn/server.conf file (note the OpenVPN /112 subnet and the VPS IPv4 address):

    server-ipv6 2001:0db8:85a3:1ab2:0:0:2b1a::/112
    push "redirect-gateway-ipv6 def1 bypass-dhcp-ipv6"
    push "route-ipv6 2000::/3"
    push "route 8.8.8.100 255.255.255.255 net_gateway"
    

    You may need to push an IPv6 route to your /64 (I do not apply it but some tutorials include this, so if you have IPv6 problems later on, you might need to add it):

    push "route-ipv6 2001:0db8:85a3:1ab2::/64"
    

    If you need to push IPv6 DNS, add:

    push "dhcp-option DNS6 2001:4860:4860::8888"
    push "dhcp-option DNS6 2001:4860:4860::8844"
    

    If you wish for the clients to be able to connect each other through the internal OpenVPN network, add:

    client-to-client
    

    Restart the OpenVPN server using "systemctl restart [email protected]".

  6. Add these lines to the end /etc/sysctl.conf file:

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

    Then, run "sysctl -p".

  7. In Debian 9, /etc/rc.local is no longer supported (replaced by systemd). As Nyr's script is still using /etc/rc.local to implement some iptables rules and we are also going to use it to include our own iptables rules, we need to enable it. Following the guide on [10], we can enable it as a systemd service.

    First, create /etc/systemd/system/rc-local.service file with this:

    [Unit]
    Description=/etc/rc.local Compatibility
    ConditionPathExists=/etc/rc.local
    
    [Service]
    Type=forking
    ExecStart=/etc/rc.local start
    TimeoutSec=0
    StandardOutput=tty
    RemainAfterExit=yes
    SysVStartPriority=99
    
    [Install]
    WantedBy=multi-user.target
    

    Run "systemctl enable rc-local.service" and "systemctl start rc-local.service".

  8. As our OpenVPN server assigns global addresses to clients, we need to instruct our VPS to add NDP proxy to the OpenVPN IPv6 addresses (refer to [11]). Create a file in your home directory (/root) called openvpn-rules.sh containing this:

    #!/bin/bash
    
    echo -e "ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1 dev enp0s3"
    
    for i in {0..5}; do
        echo -e "ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:$(printf %x $(($i+0x1000))) dev enp0s3"
    done
    

    Change {0..5} accordingly to suit your needs. For example, we have six IPv6 addresses assigned to clients.

    If you have a 'routed' subnet, then you may skip this step (no need for NDP).

  9. To allow port forwarding for your clients, add these lines in the same file (/root/openvpn-rules.sh):

    for i in {0..5}; do
        echo -e "iptables -t nat -A PREROUTING -p tcp --dport $((10000+$i+2)) -j DNAT --to 10.8.0.$(($i+2)):$((10000+$i+2))"
        echo -e "iptables -A FORWARD -d 10.8.0.$(($i+2)) -p tcp --dport $((10000+$i+2)) -j ACCEPT"
    done
    

    Please adjust {0..5} accordingly to align with the NDP proxy lines. In the case above, our base port is 1000. I had offset the number by 2, so it will align the internal IPv4 address of the clients. So, the server will assign the ports as such:

    10.8.0.2 -> 10002
    10.8.0.3 -> 10003
    [...]
    
  10. The commands above, when made executable and executed, will not run as I had made it to only echo the output (just a preference; you may edit it so it runs directly).

    That said, we need to run openvpn-rules.sh and output it to a text file which we will later add to /etc/rc.local file.

    # chmod +x /root/openvpn-rules.sh
    # /root/openvpn-rules.sh > /root/openvpn-rules_output.txt
    

    Verify the contents of the openvpn-rules_output.txt as such:

    # cat /root/openvpn-rules_output.txt
    
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1000 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1001 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1002 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1003 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1004 dev enp0s3
    ip -6 neigh add proxy 2001:0db8:85a3:1ab2:0:0:2b1a:1005 dev enp0s3
    iptables -t nat -A PREROUTING -p tcp --dport 10002 -j DNAT --to 10.8.0.2:10002
    iptables -A FORWARD -d 10.8.0.2 -p tcp --dport 10002 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp --dport 10003 -j DNAT --to 10.8.0.3:10003
    iptables -A FORWARD -d 10.8.0.3 -p tcp --dport 10003 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp --dport 10004 -j DNAT --to 10.8.0.4:10004
    iptables -A FORWARD -d 10.8.0.4 -p tcp --dport 10004 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp --dport 10005 -j DNAT --to 10.8.0.5:10005
    iptables -A FORWARD -d 10.8.0.5 -p tcp --dport 10005 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp --dport 10006 -j DNAT --to 10.8.0.6:10006
    iptables -A FORWARD -d 10.8.0.6 -p tcp --dport 10006 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp --dport 10007 -j DNAT --to 10.8.0.7:10007
    iptables -A FORWARD -d 10.8.0.7 -p tcp --dport 10007 -j ACCEPT
    
  11. Run the text file from /etc/rc.local file. Add this line in /etc/rc.local before "exit 0".

    bash /root/openvpn-rules_output.txt
    
  12. I suggest that you reboot your VPS and test your connection to the OpenVPN with your desktop and/or phone using the generated client file (transfer the .ovpn file to your device). Ensure that everything works well, including the port forwarding. Use applications such as netcat [12] to test it (one tutorial can be found here [13]).

    Check whether port forwarding is setup correctly by using netcat and verifying it with external sites like this [14].

    If everything works, we can go on to the next phase, which is to setup HTTPS encapsulation.

  13. Install stunnel4 and sslh ("apt-get install stunnel4 sslh"). Choose "inetd" when presented with the configuration of sslh.

  14. Generate a self-signed certificate [15]. You may use services such as Let's Encrypt but I find it easier to just use a self-signed certificate. In this instance, we are generating a 2048-bits certificate with 10 years validity. Change according to your needs.

    # openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3650 -nodes
    # cat key.pem cert.pem > fullcert.pem
    # cp fullcert.pem /etc/ssl/private/.
    # chmod 600 /etc/ssl/private/fullcert.pem
    
  15. Configure stunnel4 and sslh on the VPS.

    Create /etc/stunnel/stunnel.conf file containing this:

    [sslh]
    accept = 443
    cert = /etc/ssl/private/fullcert.pem
    exec = /usr/sbin/sslh
    execArgs = sslh -t 5s -i --http 127.0.0.1:80 --openvpn 127.0.0.1:1194
    

    Edit /etc/default/stunnel4 file and change "ENABLED=0" to "ENABLED=1".

    Run "systemctl restart stunnel4.service".

    Reboot just to be on the safe side.

  16. Install stunnel4 on your computer or phone. Setup the configuration file (create /etc/stunnel/stunnel.conf file if you are on Linux; edit it from within the stunnel application if you are on Windows):

    [sslh]
    client = yes
    accept = 8443
    connect = 8.8.8.100:443
    

    "accept = 8443" can be any port number that is available on your computer or phone.

    Reload the configuration file by running "systemctl restart stunnel4.service" on Linux or choose the menu "Reload configuration file" on Windows.

    If you use Android, you can use the SSLDroid [16] application with the following configuration:

    Tunnel name: (Anything)
    Local port: 8443
    Remote host: 8.8.8.100
    Remote port: 443
    
  17. Create a copy of the .ovpn file with the necessary changes to connect to the OpenVPN server through stunnel.

    Change "remote 8.8.8.100 1194" line in our copy of client .ovpn file to "remote 127.0.0.1 8443".

  18. We may now connect to the OpenVPN server using the second profile if we wish to connect through 443 and the first profile for a direct connection. It is recommended to use direct connection whenever possible so that we do not add unnecessary overhead by having two forms of encryption.

    Check IPv4 and IPv6 connectivity with sites like [17], [18] and [19].

    Please note if you use Windows, you may need to configure Windows Firewall to allow ICMP ping (I just disable Windows Firewall; I know it is bad but I am okay with it). Refer to this guide [20]. Follow similar steps if you use other firewalls.

    Test reachability of clients's IPv6s address by going to ping sites like this [21].

Well, that is about it! This guide is written in one go, so I apologise for any mistakes and oversight I may have made.

Good luck and enjoy your OpenVPN connection!


References:

[1] https://tunnelbroker.net/
[2] https://lasse-it.dk/2015/08/how-to-setting-up-openvpn-with-individual-public-ipv6s-for-clients/
[3] https://serverfault.com/a/681497
[4] https://www.systutorials.com/816/port-forwarding-using-iptables/
[5] https://openvpn.net/
[6] https://github.com/Nyr/openvpn-install
[7] http://www.rutschle.net/tech/sslh.shtml
[8] https://www.stunnel.org/
[9] https://nginx.org/en/
[10] https://www.linuxbabe.com/linux-server/how-to-enable-etcrc-local-with-systemd
[11] https://unix.stackexchange.com/a/136819
[12] http://nc110.sourceforge.net/
[13] http://www.binarytides.com/netcat-tutorial-for-beginners/
[14] http://www.canyouseeme.org/
[15] https://stackoverflow.com/a/10176685
[16] https://play.google.com/store/apps/details?id=hu.blint.ssldroid&hl=en
[17] http://ipv6-test.com/
[18] http://test-ipv6.com/
[19] http://ds.testmyipv6.com/
[20] https://technet.microsoft.com/en-us/library/cc749323(v=ws.10).aspx
[21] http://centralops.net/co/

Comments

  • Wow great work, thanks!

    Thanked by 1mxp13
  • mxp13mxp13 Member
    edited June 2017

    Edit: Some revisions that have been incorporated into the main post. Did not realise I was still able to edit it.

  • mxp13mxp13 Member
    edited June 2017

    Edit: Forgotten about the "thank" button. Chose that instead. But still, thank you, @muratai. :)

  • Really nice work! Was thinking about adding 6to4 to my OpenVPN server, thanks!

    Thanked by 1mxp13
  • Nice tutorial, but how the hell did you get this by the cloudflare protection that always kicks in when people share these kinds of steps

    Thanked by 1mxp13

    I like my uptime down low and my servers all hacked. Can see me droppin' twenty-fours with a router in the rack.
    Ya like ya Switch-Ports hot and ya servers all hacked. If ya pings real high and ya networks pitch black.

  • mxp13mxp13 Member
    edited June 2017

    Thank you everyone. :)

    @bugrakoc: Yes, getting 6to4 to work was one of my primary motivations in executing this setup. This VPN is being used by me and my family, and since some of them do not have IPv6 and setting up HE's tunnel is tricky on their part because they do not have static IPv4 to link to, I figured this alternative setup will work just as well instead. And on top of that, we have a working VPN so our family can connect securely over the internet. And that is cool!

    Since posting this guide, I have been adjusting my setup with a proper dual-stack configuration, so the OpenVPN server is accessible by both IPv4 and IPv6. Suits everyone!

    It is as simple as editing a few files. Assuming we are using Nyr's script, the steps are:

    1. Edit /etc/openvpn/server.conf file:

      [1a]
      Change

      proto tcp
      

      to

      proto tcp6
      
    2. Edit /etc/openvpn/client-common.txt (for generating future client files) and your already generated .ovpn files:

      [2a]
      Remove "proto tcp".

      [2b]
      Change

      remote 8.8.8.100 1194
      

      to

      remote 8.8.8.100 1194 tcp4
      

      [2c]
      Add this new line under it

      remote 2001:0db8:85a3:1ab2:0:0:2b1a:1 1194 tcp6
      

      If you use HTTPS encapsulation, then apply (3), (4) and (5).

    3. Edit /etc/stunnel/stunnel.conf on the VPS:

      [3a]
      Change

      accept = 443
      

      to

      accept = :::443
      

      &

      [3b]
      Change

      execArgs = sslh -t 5s -i --http 127.0.0.1:80 --openvpn 127.0.0.1:1194
      

      to

      execArgs = sslh -t 5s -i --http localhost:80 --openvpn localhost:1194
      
    4. Replace the contents of stunnel.conf on the client side with:

      [sslh-ipv6]
      client = yes
      accept = ::1:8443
      connect = 2001:0db8:85a3:1ab2:0:0:2b1a:1:443
      
      [sslh-ipv4]
      client = yes
      accept = 127.0.0.1:8443
      connect = 8.8.8.100:443
      
    5. Edit the second copy of the .ovpn files (the one we use to connect through stunnel):

      [5a]
      Change

      proto tcp
      

      to

      proto tcp6
      

      &

      [5b]
      Change

      remote 127.0.0.1 8443
      

      to

      remote localhost 8443
      

      or specify it separately by the respective protocols.

      remote localhost 8443 tcp6
      remote localhost 8443 tcp4
      

      This particular change is probably unncessary given that localhost points to both IPv6 and IPv4, but maybe it is beneficial to specify it separately. Try with just the original change first ("remote localhost 8443").

    @teamacc: It was quite tricky to get past that. I had to install the NoScript extension on my Firefox browser. First, I clicked "Temporarily allow lowendtalk.com", then navigate until I get to the page where I can post (or edit) the thread. Then I hit the "Forbid lowendtalk.com" so it would then reload the page with JavaScript turned off. I then pasted the text (I had to use an offline Markdown editor to get the formatting going) onto the now-static page. Click Post/submit, then a "broken" CloudFlare will greet us. In that page, temporary allow again so CF will reload well. Get past it, then you will have your text posted on the site.

    Thanked by 2aboanas93 bugrakoc
  • seanhoseanho Member

    Great write-up, @mxp13. Getting around CloudFlare sounds like a feat worthy of its own tutorial!

    Thanked by 1mxp13
  • mxp13mxp13 Member

    Thank you, @seanho. Encouraged by your comment, I have just posted a simple guide on how I managed to do so. :)

Sign In or Register to comment.