├── .gitignore ├── LICENSE ├── README.md ├── Vagrantfile ├── add-client.sh ├── bootstrap.sh ├── client-configs └── .gitignore ├── config-default.sh ├── generate-client-certificate.sh ├── interfaces.sh ├── make-config.sh ├── openvpn.sh ├── revoke-full.sh └── ubuntu.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | .DS_Store 3 | *.log 4 | config.sh 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openvpn-server-vagrant 2 | 3 | Spin up an OpenVPN Server 4 | 5 | 6 | ## Install Vagrant, VirtualBox and git 7 | 8 | http://www.vagrantup.com 9 | https://www.virtualbox.org (don't worry about setting up any VMs as the steps below will cover this) 10 | http://git-scm.com 11 | 12 | 13 | ## Set up 14 | 15 | $ git clone https://github.com/redgeoff/openvpn-server-vagrant.git 16 | $ cd openvpn-server-vagrant 17 | $ cp config-default.sh config.sh 18 | Edit config.sh and fill in your config 19 | $ vagrant up 20 | $ vagrant ssh 21 | 22 | You can then perform a sanity test with a connection from a VPN client with: 23 | 24 | $ sudo su - 25 | $ /vagrant/add-client.sh test-client 26 | $ cp ~/client-configs/files/test-client.ovpn /vagrant 27 | On the host, double click `test-client.ovpn` to load it into Tunnelblick 28 | Use Tunnelblick to connect to the VPN server 29 | 30 | # Add a route to a subnet 31 | 32 | Routes must be added to the server so that you clients know which traffic to route to the VPN Server. The following process should be repeated for each subnet in your network. 33 | 34 | Edit `/etc/openvpn/server.conf` and add something like the following, where `172.31.26.0` is your network and 255.255.255.0 is the netmask. 35 | 36 | push "route 172.31.26.0 255.255.255.0" 37 | 38 | Then restart the VPN Server: 39 | 40 | $ sudo systemctl restart openvpn@server 41 | 42 | 43 | ## Add a client 44 | 45 | The following should be repeated for each new client/user for whom you wish to grant access to your VPN. Replace client-name with a unique name. 46 | 47 | $ sudo su - 48 | $ /vagrant/add-client.sh client-name 49 | 50 | You will then find a file like the following that you should provide to the individual who will be connecting to your VPN. This ovpn file can then be used with Tunnelblick (OS X), OpenVPN (Linux, iOS, Android and Windows). 51 | 52 | ~/client-configs/files/client-name.ovpn 53 | 54 | 55 | ## Revoke client certificate 56 | 57 | If you ever need to revoke access, simply execute: 58 | 59 | $ sudo su - 60 | $ /vagrant/revoke-full.sh client-name 61 | 62 | 63 | ## Extra Info 64 | 65 | * See [Run a Free VPN Server on AWS](https://redgeoff.com/posts/free-vpn-aws-2022) 66 | * See [How To Set Up and Configure an OpenVPN Server on Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-and-configure-an-openvpn-server-on-ubuntu-20-04) 67 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | 3 | config.vm.box = "bento/ubuntu-22.04" 4 | 5 | # Use a private network so that we don't have to worry about forwarding ports 6 | config.vm.network "private_network", ip: "192.168.56.11" 7 | 8 | config.vm.provider "virtualbox" do |v| 9 | v.memory = 1024 10 | 11 | # Only allow drift of 1 sec, instead of 20 min default 12 | v.customize [ "guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 1000 ] 13 | end 14 | 15 | # Bootstrap script for configuring VM 16 | config.vm.provision :shell, path: "bootstrap.sh" 17 | 18 | end 19 | -------------------------------------------------------------------------------- /add-client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Change to script directory 4 | sd=`dirname $0` 5 | cd $sd 6 | 7 | name=$1 8 | 9 | if [ "$name" = "" ]; then 10 | echo "Usage: add-client.sh name" 11 | exit; 12 | fi 13 | 14 | # Generate a client certificate and key pair 15 | $sd/generate-client-certificate.sh $name 16 | 17 | # Make config 18 | $sd/make-config.sh $name 19 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /vagrant/ubuntu.sh 4 | 5 | /vagrant/openvpn.sh 6 | -------------------------------------------------------------------------------- /client-configs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /config-default.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | KEY_COUNTRY="US" 4 | KEY_PROVINCE="CA" 5 | KEY_CITY="SanFrancisco" 6 | KEY_ORG="Fort-Funston" 7 | KEY_EMAIL="me@myhost.mydomain" 8 | KEY_OU="MyOrganizationalUnit" 9 | 10 | PUBLIC_IP="192.168.56.11" 11 | -------------------------------------------------------------------------------- /generate-client-certificate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Change to script directory 4 | sd=`dirname $0` 5 | cd $sd 6 | sd=`pwd` 7 | 8 | name=$1 9 | 10 | if [ "$name" = "" ]; then 11 | echo "Usage: generate-client-certificate.sh name" 12 | exit; 13 | fi 14 | 15 | cd ~/openvpn-ca 16 | yes "" | ./easyrsa gen-req ${name} nopass 17 | cp pki/private/${name}.key ~/client-configs/keys/ 18 | yes "yes" | ./easyrsa sign-req client ${name} 19 | cp /root/openvpn-ca/pki/issued/${name}.crt ~/client-configs/keys/ 20 | cp /etc/openvpn/server/ta.key ~/client-configs/keys/ 21 | cp /etc/openvpn/server/ca.crt ~/client-configs/keys/ -------------------------------------------------------------------------------- /interfaces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEVICES=$(ifconfig -s | grep -v "Iface\|lo\|tun0" | cut -d " " -f 1) 4 | NUMDEVICES=$(ifconfig -s | wc -l) 5 | VPNDEVICE="" 6 | 7 | # If we have an eth0 device, use that to maintain current functionality. 8 | if ifconfig -s | grep -q eth0 9 | then 10 | export VPNDEVICE="eth0" 11 | 12 | # If we hit newer systemd/hypervisor combination, it won't be eth0, but there 13 | # will probably be only one obvious device to use: not the loop back, and not 14 | # the tunnel. Use it instead. 15 | elif [ $NUMDEVICES -eq 3 ] 16 | then 17 | # no eth0 device, but only one option, use it. 18 | export VPNDEVICE=$(ifconfig -s | tail -n 2 | cut -d " " -f 1 | grep -v "lo\|tun0") 19 | 20 | # If we end up on a server that has multiple devices and no eth0, just ask. 21 | # This is fairly common, Docker containers, wifi cards, dual nics, etc. 22 | else 23 | echo "There are multiple network devices on this server:" 24 | echo "" 25 | ifconfig -s | cut -d " " -f 1 | grep -v "Iface\|lo\|tun0" 26 | echo "" 27 | echo "Please type the name of the device you'd like to use" 28 | read VPNDEVICE 29 | 30 | # Catch bad user input 31 | while ! echo $DEVICES | grep -q "${VPNDEVICE}" 32 | do 33 | echo "${VPNDEVICE} was not found in the list of devices. Please type the device name again" 34 | read VPNDEVICE 35 | done 36 | export $VPNDEVICE 37 | 38 | fi 39 | -------------------------------------------------------------------------------- /make-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | name=$1 4 | 5 | if [ "$name" = "" ]; then 6 | echo "Usage: make-config.sh name" 7 | exit; 8 | fi 9 | 10 | KEY_DIR=~/client-configs/keys 11 | OUTPUT_DIR=~/client-configs/files 12 | BASE_CONFIG=~/client-configs/base.conf 13 | 14 | cat ${BASE_CONFIG} \ 15 | <(echo -e '') \ 16 | ${KEY_DIR}/ca.crt \ 17 | <(echo -e '\n') \ 18 | ${KEY_DIR}/${name}.crt \ 19 | <(echo -e '\n') \ 20 | ${KEY_DIR}/${name}.key \ 21 | <(echo -e '\n') \ 22 | ${KEY_DIR}/ta.key \ 23 | <(echo -e '') \ 24 | > ${OUTPUT_DIR}/${name}.ovpn 25 | 26 | # sed -i "s/group nogroup/group nobody/" ${OUTPUT_DIR}/${name}.ovpn 27 | -------------------------------------------------------------------------------- /openvpn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # For details on why certain options have been chosen, see 4 | # https://www.digitalocean.com/community/tutorials/how-to-set-up-and-configure-an-openvpn-server-on-ubuntu-20-04 5 | 6 | # Change to script directory 7 | sd=`dirname $0` 8 | cd $sd 9 | # if you ran the script from its own directory you actually just got '.' 10 | # so capture the abs path to wd now 11 | sd=`pwd` 12 | 13 | # Make sure config file exists 14 | if [ ! -f ./config.sh ]; then 15 | echo "config.sh not found!" 16 | exit; 17 | fi 18 | 19 | # Install OpenVPN and net-tools (ifconfig) 20 | apt-get -y install openvpn easy-rsa net-tools 21 | 22 | # Load config 23 | source ./config.sh 24 | source ./interfaces.sh 25 | 26 | # Set up the CA directory 27 | make-cadir ~/openvpn-ca 28 | cd ~/openvpn-ca 29 | 30 | # Update vars 31 | echo "set_var EASYRSA_REQ_COUNTRY \"${KEY_COUNTRY}\"" >> vars 32 | echo "set_var EASYRSA_REQ_PROVINCE \"${KEY_PROVINCE}\"" >> vars 33 | echo "set_var EASYRSA_REQ_CITY \"${KEY_CITY}\"" >> vars 34 | echo "set_var EASYRSA_REQ_ORG \"${KEY_ORG}\"" >> vars 35 | echo "set_var EASYRSA_REQ_EMAIL \"${KEY_EMAIL}\"" >> vars 36 | echo "set_var EASYRSA_REQ_OU \"${KEY_OU}\"" >> vars 37 | echo "set_var EASYRSA_ALGO \"ec\"" >> vars 38 | echo "set_var EASYRSA_DIGEST \"sha512\"" >> vars 39 | 40 | # Build the Certificate Authority 41 | ./easyrsa init-pki 42 | yes "" | ./easyrsa build-ca nopass 43 | 44 | # Create the server certificate 45 | yes "" | ./easyrsa gen-req server nopass 46 | cp pki/private/server.key /etc/openvpn/server/ 47 | cp pki/private/ca.key /etc/openvpn/server/ 48 | cp pki/ca.crt /etc/openvpn/server/ 49 | 50 | # Sign the certificate request 51 | yes "yes" | ./easyrsa sign-req server server 52 | yes "yes" | cp pki/issued/server.crt /etc/openvpn/server/ 53 | 54 | # Generate an extra shared secret key 55 | openvpn --genkey secret pki/ta.key 56 | cp pki/ta.key /etc/openvpn/server/ 57 | 58 | # Copy the sample config to the OpenVPN directory 59 | cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn/server/server.conf 60 | 61 | # Adjust the OpenVPN configuration 62 | sed -i "s/^;tls-auth ta.key.*$/tls-crypt ta.key 0/" /etc/openvpn/server/server.conf 63 | # sed -i "s/cipher AES-256-CBC/cipher AES-256-GCM\nauth SHA256/" /etc/openvpn/server/server.conf 64 | sed -i "s/dh dh2048.pem/;dh dh2048.pem\ndh none/" /etc/openvpn/server/server.conf 65 | # sed -i "s/;user openvpn/user openvpn/" /etc/openvpn/server/server.conf 66 | # sed -i "s/;group openvpn/group openvpn/" /etc/openvpn/server/server.conf 67 | 68 | # Allow IP forwarding 69 | sed -i "s/#net.ipv4.ip_forward/net.ipv4.ip_forward/" /etc/sysctl.conf 70 | sysctl -p 71 | 72 | # Firewall configuration 73 | sed -i "s/# rules.before/# rules.before\n# START OPENVPN RULES\n# NAT table rules\n*nat\n:POSTROUTING ACCEPT [0:0]\n-A POSTROUTING -s 10.8.0.0\/8 -o ${VPNDEVICE} -j MASQUERADE\nCOMMIT\n# END OPENVPN RULES/" /etc/ufw/before.rules 74 | sed -i "s/DEFAULT_FORWARD_POLICY=\"DROP\"/DEFAULT_FORWARD_POLICY=\"ACCEPT\"/" /etc/default/ufw 75 | ufw allow 1194/udp 76 | ufw allow OpenSSH 77 | ufw disable 78 | yes "y" | ufw enable 79 | 80 | # Start and enable the OpenVPN service 81 | systemctl -f enable openvpn-server@server.service 82 | systemctl start openvpn-server@server.service 83 | 84 | # Creating the Client Configuration Infrastructure 85 | mkdir -p ~/client-configs/keys 86 | chmod -R 700 ~/client-configs 87 | mkdir -p ~/client-configs/files 88 | cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf 89 | sed -i "s/remote my-server-1 1194/remote ${PUBLIC_IP} 1194/" ~/client-configs/base.conf 90 | # sed -i "s/;user openvpn/user openvpn/" ~/client-configs/base.conf 91 | # sed -i "s/;group openvpn/group openvpn/" ~/client-configs/base.conf 92 | sed -i "s/ca ca.crt/;ca ca.crt/" ~/client-configs/base.conf 93 | sed -i "s/cert client.crt/;cert client.crt/" ~/client-configs/base.conf 94 | sed -i "s/key client.key/;key client.key/" ~/client-configs/base.conf 95 | sed -i "s/tls-auth ta.key 1/;tls-auth ta.key 1/" ~/client-configs/base.conf 96 | # sed -i "s/cipher AES-256-CBC/cipher AES-256-GCM/" ~/client-configs/base.conf 97 | echo "auth SHA256" >> ~/client-configs/base.conf 98 | echo "key-direction 1" >> ~/client-configs/base.conf 99 | echo ";script-security 2" >> ~/client-configs/base.conf 100 | echo ";up /etc/openvpn/update-resolv-conf" >> ~/client-configs/base.conf 101 | echo ";down /etc/openvpn/update-resolv-conf" >> ~/client-configs/base.conf 102 | echo ";script-security 2" >> ~/client-configs/base.conf 103 | echo ";up /etc/openvpn/update-systemd-resolved" >> ~/client-configs/base.conf 104 | echo ";down /etc/openvpn/update-systemd-resolved" >> ~/client-configs/base.conf 105 | echo ";down-pre" >> ~/client-configs/base.conf 106 | echo ";dhcp-option DOMAIN-ROUTE ." >> ~/client-configs/base.conf 107 | -------------------------------------------------------------------------------- /revoke-full.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | name=$1 4 | 5 | if [ "$name" = "" ]; then 6 | echo "Usage: revoke-full.sh name" 7 | exit; 8 | fi 9 | 10 | cd ~/openvpn-ca 11 | 12 | yes "yes" | ./easyrsa revoke $name 13 | 14 | ./easyrsa gen-crl 15 | 16 | # Install the revocation files 17 | cp pki/crl.pem /etc/openvpn/server 18 | 19 | # Configure the server to check the client revocation list. This should only be done once 20 | if [ $(grep -R 'crl-verify crl.pem' /etc/openvpn/server/server.conf | wc -l) -eq 0 ]; then 21 | echo -e "\ncrl-verify crl.pem" >> /etc/openvpn/server/server.conf 22 | systemctl restart openvpn-server@server.service 23 | fi 24 | -------------------------------------------------------------------------------- /ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Update apt-get 4 | apt-get -y update 5 | 6 | # Update Ubuntu 7 | apt-get -y upgrade 8 | apt-get -y dist-upgrade --------------------------------------------------------------------------------