Thursday, 13 February 2014

the DIY home VPN experiment (part three - my virtualized OpenVPN server)

Part Three: My Virtualized OpenVPN Server

Overview

In this post, I'd like to share my most recent home lab project - a nested OpenVPN server. I refer to it as nested, because it is running completely within my desktop computer. See the illustration below.

The idea for this project came along when I working on setting up a VPN with my older Linksys router. Basically, I wanted to come up with a way to run a home lab VPN server without requiring any additional hardware, and I wanted to incorporate virtualization into this project. In addition, running it as a VM allows for fairly safe and controlled experimentation, without running the risk of bricking one's router with non-standard firmware.


The concept here is to forward VPN traffic received on the public interface of the Internet router (via incoming TCP port 1194) to the OpenVPN server's virtual interface. The VPN server authenticates the connection, and regulates forwarding traffic to and from various destinations on your private network, based on a defined set of rules. This could include forwarding traffic to your desktop to enable a remote sharing via VNC, RDP, SSH, or other protocol.

Even though the Internet traffic is technically flowing through the desktop, it is being forwarded in a controlled manner to the VPN server via the hypervisor. Perhaps there is, or will be a way to exploit the hypervisor for malicious use, but as I'm not aware of this at the moment chose to experiment with this design. If you have any insight, or could speak to the security implications of this configuration, I'd love to hear your feedback.

Virtual Machine Configuration

I chose to run VMware Fusion on my Mac, but you could easily use VMware Workstation, or one of the many other available hypervisor options. Please note, there are other ways to deploy OpenVPN as a virtual machine, including as an appliance; however, I chose to create a CentOS based VM for transparency and educational purposes.

  • I created a VM in Fusion called CentOS-OpenVPN-Server, and configured it as follows:
    • 1 vCPU
    • 1 GB memory


    • 10 GB vDisk (minimal resources required)
    • Network adapter (set to bridged mode so that I can assign my own IP from within the guest OS)


    • Removed any non-essential virtual hardware components (i.e. sound card, printer port).
    • Disabled any sharing between the host and guest operating system via the hypervisor.
    • Disabled time synchronization with the host, as I’ll use NTP within CentOS.


CentOS Installation

I chose to perform a bare bones installation of CentOS which reduces the required resources and the server’s attack footprint.

  • I booted the virtual machine using the CentOS installation media. In this case, I used the CentOS 6.4 DVD ISO.


  • Next, I configured my network settings. (IP address, subnet mask, gateway, DNS)

    • I used an IP Address of 192.168.2.240 /24 for the OpenVPN server.


  • I selected ‘Minimal’ for the type CentOS installation.

  • CentOS Configuration

    After the operating system was installed, I prepared the system for OpenVPN.

    • I ran yum to ensure that all of my pacakges were up-to-date and patched. (After running yum update, I was running CentOS release 6.5.)
      yum update
      
    • Next, I installed wget (used to set up the RepoForge repositories), openssh-clients, and ntp for time synchronization.
      yum install wget ntp openssh-clients
      
    • I installed the RepoForge repository. This was fairly straight forward, and I followed the instructions found here.
      For CentOS (x64), I ran the following commands:
      wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
      rpm -ivh rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
      
    • I enabled IP packet forwarding, so that traffic could routed back and forth from my external VPN clients to the desired hosts on my private network via the OpenVPN server.
      • Edited the file: /etc/sysctl.conf and set the net.ipv4.ip_forward parameter to a value of 1. (net.ipv4.ip_forward = 1)
      • Forced sysctl to reload the configuration:
        sysctl -p
        

    OpenVPN Installation and Configuration

    • Installed openvpn from the RepoForge repository.
      yum install openvpn
      
    • Configured the openvpn server.
      • Copied the relevant files from my PKI to the OpenVPN server (see part two in this series on generating a PKI), to the /etc/openvpn/pki/ directory.
        • ca.crt (cert for the trusted root certificate authority)
        • MyOpenVPNServer.crt (server certificate)
        • MyOpenVPNServer.key (server private key)
        • dh.pem (Diffie-Hellman parameters)
      • Removed the passphrase from my private key, so that OpenVPN could start up automatically at boot time.
        openssl rsa -in /etc/openvpn/pki/MyOpenVPNServer.key -out /etc/openvpn/pki/MyOpenVPNServer.key
        
      • Created my server configuration: (/etc/openvpn/server.conf)
        # local IP address, port, and protocol for OpenVPN server to listen on and use
        local 192.168.2.240
        port 1194
        proto tcp
        
        # dev tun is used to created a routed VPN
        dev tun
        
        # my PKI files
        ca /etc/openvpn/pki/ca.crt
        cert /etc/openvpn/pki/MyOpenVPNServer.crt
        key /etc/openvpn/pki/MyOpenVPNServer.key  # This file should be kept secret
        dh /etc/openvpn/pki/dh.pem
        
        # server mode
        server 10.8.0.0 255.255.255.0
        ifconfig-pool-persist ipp.txt
        
        # optional routes to push to clients
        push route 192.168.2.0 255.255.255.0
        
        keepalive 10 120
        cipher AES-128-CBC   # AES
        comp-lzo
        
        # following options enabled for non-Windows servers
        user nobody
        group nobody
        
        persist-key
        persist-tun
        status openvpn-status.log
        verb 3
        
        In this configuration, my OpenVPN server has an IP address of 192.168.2.240 on my local subnet, and will be listening on port 1194 for incoming TCP connections. I am using a routed (non-bridged) VPN, so I needed to ensure that the proper routes were in place, not only on my client, but also on any servers that my client wanted to talk to.
        When a client connects to my VPN, it receives an IP address on the 10.8.0.0/24 subnet, and the OpenVPN server presents itself to the client as 10.8.0.1. My client does not know how to reach my local subnet (192.168.2.0), therefore I pushed the following route to the client, so it knows how to handle traffic appropriately:
        push route 192.168.2.0 255.255.255.0
        

        Basically, this tells my VPN clients that traffic on the 192.168.2.0/24 subnet should be routed through the OpenVPN server; however, the devices on my local subnet also know nothing about how to send traffic back to my VPN clients which are on a different subnet. Therefore, to ensure that my hosts could route traffic back to the OpenVPN clients, I added a static route.

        For example, let’s say that my Mac has an IP address of 192.168.2.200 (on the local subnet), and that my VPN client receives an IP address of 10.8.0.6 (on the VPN subnet). My VPN client knows how to route traffic to my Mac because of the push route command above; however, my Mac does not know how to properly route traffic back.

        • Using the terminal on my Mac, I ran the following command:
          sudo route -n add 10.8.0.0/24 192.168.2.240
          

        This tells my Mac, to route traffic destined for the VPN subnet, to the OpenVPN server (192.168.2.240). (Note, this is not a persistent route and will not survive a reboot. To configure persistent routes, search for specific information relevant to your operating system.)

    • After copying the PKI files and creating my server config, I ran the following command to ensure that my files were labeled correctly.
      chcon --reference /etc/openvpn /etc/openvpn -R
      

      This command takes the SELinux context of /etc/openvpn and applies it to all of the files within that same subfolder. I did this to update the context on the PKI files that I copied over from my Mac. (Please note, this configuration will not persist through a system re-label. If you require that, please search for additional information as this post does not cover that level of detail.)

    • Enabled openvpn and ntp to start when the system boots.
      chkconfig openvpn on
      chkconfig ntpd on
      
    • Lastly, I configured port-forwarding on my Internet router, to direct TCP traffic (port 1194) from my public interface, to my OpenVPN server.
    • While I'm not planning to go through the precise steps required to configure an OpenVPN client, which vary from OS to OS, it is pretty straight forward at this point. You will need a copy of the cert for your root certificate authority (ca.crt), as well as the VPN client's certificate (client1.crt) and private key (client1.key) - both of which I created in part two of this series - and a config file for your client.
      Here is a sample VPN client config file (client_example.ovpn):
      client
      dev tun
      proto tcp
      remote  1194
      resolv-retry infinite
      nobind
      
      # Downgrade privileges after initialization (non-Windows only)
      user nobody
      group nobody
      
      # Try to preserve some state across restarts.
      persist-key
      persist-tun
      
      ca ca.crt
      cert client1.crt
      key client1.key
      
      cipher AES-128-CBC
      comp-lzo
      verb 4
      

    That’s about it. At this point, I have my OpenVPN server running as a virtual machine within VMware Fusion, and it’s ready to accept client connections. I tested my VPN connection using the OpenVPN app on my iPhone, and was able to successfully open an SSH and VNC connection to my desktop.

    Keep on virtualizing!

    the DIY home VPN experiment series:


    Be sure to follow me to receive updates when more posts in this series become available. If you like this article, please share it with others.

    References:

    OpenVPN - Installation Notes
    Using RepoForge
    OpenVPN - Server unable to open dh parameters

    2 comments:

    1. Very cool setup. You have spent a lot of time working on this project. I have a couple questions on your CentOS setup. Do you have two interfaces on your Linux server? You turned on ip forwarding, this is usually used when forwarding traffic between two interfaces. Also, did you configure iptables or leave it default? I would highly recommend tightening down your iptables. If Internet traffic is reaching the Linux server and iptables isn't configured properly, there could be ways to exploit this system very easily.

      ReplyDelete
      Replies
      1. Great questions JH!

        Using VMware Fusion, I presented one virtual NIC on my private subnet to CentOS which shows up as the eth0 interface, this had an IP address of 192.168.2.240.

        When the OpenVPN service starts up, it creates an additional interface called tun0. (as shown below)

        tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
        inet addr:10.8.0.1 P-t-P:10.8.0.2 Mask:255.255.255.255
        UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 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:100
        RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

        To answer your question, there are two interfaces and IP forwarding is enabled for passing traffic back and forth between my VPN and private subnet.

        In my experiment, I left iptables with the default settings, but only because I was already sitting behind an Internet router which also acts as a firewall. I agree with you though, and it is still a good idea to enable iptables even if you're already behind a firewall as you can never be too safe - some threats may from the inside. :-)

        Delete