Skip to content

Instantly share code, notes, and snippets.

@jcconnell
Last active August 28, 2023 09:04
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jcconnell/ec3c942c818a571d97f5ceaf954a37b0 to your computer and use it in GitHub Desktop.
Save jcconnell/ec3c942c818a571d97f5ceaf954a37b0 to your computer and use it in GitHub Desktop.
Unifi Security Gateway (USG) OpenVPN server with RADIUS authentication

Last Updated: 8/30/18

Details

I wanted to run an OpenVPN server on the USG. Since it has a Radius server built in, I figured this would be a much better way to handle OpenVPN authentication. Make sure you have the Radius server enabled on your USG under Settings > Services > Radius > Server in the controller. Add OpenVpn users under Settings > Services > Radius > Server.

Thanks to the following resources in helping to configure this:

Configure easy-rsa and generate keys

SSH into your USG and run the following commands

sudo bash
curl -O http://ftp.us.debian.org/debian/pool/main/e/easy-rsa/easy-rsa_2.2.2-1~bpo70+1_all.deb
sudo dpkg -i easy-rsa_2.2.2-1~bpo70+1_all.deb

# Generate Keys
cd /usr/share/easy-rsa
. vars
./clean-all
./build-ca

# Give it a common-name like "OpenVPN CA"
./build-key-server server

# Set the common name to “server”
# Answer yes to signing the certificate and commiting it.
./build-dh

# Copy the generated keys
mkdir /config/auth/keys/
cp keys/* /config/auth/keys/

Configure OpenVPN on the USG

configure
set interfaces openvpn vtun0 mode server

# Make sure to use a subnet not in use anywhere else on your USG
set interfaces openvpn vtun0 server subnet 10.72.1.0/24
set interfaces openvpn vtun0 tls ca-cert-file /config/auth/keys/ca.crt
set interfaces openvpn vtun0 tls cert-file /config/auth/keys/server.crt
set interfaces openvpn vtun0 tls key-file /config/auth/keys/server.key
set interfaces openvpn vtun0 tls dh-file /config/auth/keys/dh2048.pem
set interfaces openvpn vtun0 encryption aes128
set interfaces openvpn vtun0 openvpn-option "--keepalive 8 30"
set interfaces openvpn vtun0 openvpn-option "--comp-lzo"
set interfaces openvpn vtun0 openvpn-option "--duplicate-cn"
set interfaces openvpn vtun0 openvpn-option "--user nobody --group nogroup"
set interfaces openvpn vtun0 openvpn-option "--plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn"
set interfaces openvpn vtun0 openvpn-option "--client-cert-not-required --username-as-common-name"
set interfaces openvpn vtun0 openvpn-option "--verb 1"
set interfaces openvpn vtun0 openvpn-option "--proto udp6"
set interfaces openvpn vtun0 openvpn-option "--port 1194"
set interfaces openvpn vtun0 openvpn-option "--push redirect-gateway def1"
set interfaces openvpn vtun0 openvpn-option "--push dhcp-option DNS 8.8.8.8"
set interfaces openvpn vtun0 openvpn-option "--push dhcp-option DNS 8.8.4.4"

# Configure the firewall
set firewall name WAN_LOCAL rule 20 action accept
set firewall name WAN_LOCAL rule 20 description "Allow OpenVPN clients in"
set firewall name WAN_LOCAL rule 20 destination port 1194
set firewall name WAN_LOCAL rule 20 log disable
set firewall name WAN_LOCAL rule 20 protocol udp

# (Optional) Configure the firewall for IPv6
set firewall ipv6-name wan_local-6 rule 20 action accept
set firewall ipv6-name wan_local-6 rule 20 description "Allow OpenVPN clients in"
set firewall ipv6-name wan_local-6 rule 20 destination port 1194
set firewall ipv6-name wan_local-6 rule 20 log disable
set firewall ipv6-name wan_local-6 rule 20 protocol udp

# Forward traffic to the internet
set service nat rule 5010 description "Masquerade for WAN"
set service nat rule 5010 outbound-interface eth0
set service nat rule 5010 type masquerade

commit
save
exit

Create a .ovpn file

Give this to your clients for connection. Make sure to change the relevant details

client
float
dev tun

# EDIT THIS HOSTNAME
remote my.hostname.com 1194 udp

resolv-retry infinite
nobind
persist-key
persist-tun
auth-user-pass
cipher AES-128-CBC
comp-lzo
verb 3
<ca>
 -----BEGIN CERTIFICATE----- 
# put your certificate block here. Copy it from your /config/auth/keys/ca.crt file on your USG
-----END CERTIFICATE----- 
</ca> 

# this is an random certificate. The .ovpn file needs one, but doesn't use it, so you can leave this as is
<cert> 
-----BEGIN CERTIFICATE-----
MIIB1jCCAT+gAwIBAgIEAmLSTjANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDEwpP
cGVuVlBOIENBMB4XDTEzMDExNzAyMTExMloXDTIzMDEyMjAyMTExMlowKDEmMCQG
A1UEAxQdZnJyaWN0aW9uQGdtYWlsLmNvbV9BVVRPTE9HSU4wgZ8wDQYJKoZIhvcN
AQEBBQADgY0AMIGJAoGBALVEXIZYYu1Inmejuo4Si6Eo5AguTX5sg1pGbLkJSTR4
BXQsy6ocUnZ9py8htYkipkUUhjY7zDu+wJlUtWnVCwCYtewYfEc/+azH7+7eU6ue
T2K2IKdik1KWhdtNbaNphVvSlgdyKiuZDTCedptgWyiL50N7FMcUUMjjXYh/hftB
AgMBAAGjIDAeMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgeAMA0GCSqGSIb3
DQEBBQUAA4GBABhVzSYXHlQEPNaKGmx9hMwwnNKcHgD9cCmC9lX/KR2Y+vT/QGxK
7sYlJInb/xmpa5TUQYc1nzDs9JBps1mCtZbYNNDpYnKINAKSDsM+KOQaSYQ2FhHk
bmBZk/K96P7VntzYI5S02+hOWnvjq5Wk4gOt1+L18+R/XujuxGbwnHW2
-----END CERTIFICATE-----
</cert>

# this is an random key. The .ovpn file needs one, but doesn't use it, so you can leave this as is
<key>
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALVEXIZYYu1Inmej
uo4Si6Eo5AguTX5sg1pGbLkJSTR4BXQsy6ocUnZ9py8htYkipkUUhjY7zDu+wJlU
tWnVCwCYtewYfEc/+azH7+7eU6ueT2K2IKdik1KWhdtNbaNphVvSlgdyKiuZDTCe
dptgWyiL50N7FMcUUMjjXYh/hftBAgMBAAECgYEAsNjgOEYVRhEaUlzfzmpzhakC
SKT8AALYaAPbYO+ZVzJdh8mIbg+xuF7A9G+7z+5ZL35lrpXKnONuvmlxkK5ESwvV
Q7EOQYCZCqa8xf3li3GUBLwcwXKtOUr3AYXhdbOh2viQdisD4Ky7H6/Nd3yMc3bu
R4pErmWeHei+l6dIwAECQQDqljNxi9babmHiei6lHaznCMg5+jfAyDXgHvO/afFr
1bDQVDTDK+64kax4E9pvDZC6B/HGse9hOUGWXTjb0WZBAkEAxdAw/14iJIUcE5sz
HDy2R0RmbUQYFjrNgBCi5tnmr1Ay1zHAs1VEF+Rg5IOtCBO50I9jm4WCSwCtN6zF
FoFVAQJAUGfBJDcZIm9ZL6ZPXJrqS5oP/wdLmtFE3hfd1gr7C8oHu7BREWB6h1qu
8c1kPlI4+/qDHWaZtQpJ977mIToJwQJAMcgUHKAm/YPWLgT31tpckRDgqgzh9u4z
e1A0ft5FlMcdFFT8BuWlblHWJIwSxp6YO6lqSuBNiuyPqxw6uVAxAQJAWGxOgn2I
fGkWLLw4WMpkFHmwDVTQVwhTpmMP8rWGYEdYX+k9HeOJyVMrJKg2ZPXOPtybrw8T
PUZE7FgzVNxypQ==
-----END PRIVATE KEY-----
</key>

Configure OpenVPN to authenticate with Radius

Create the file /etc/pam_radius_auth.conf and add the following contents to it. RADIUSSERVERIP should be the IP of your USG, 10.0.1.1 in my case. Enter the shared secret you created when you enabled the Radius server.

RADIUSSERVERIP SHAREDSECRET

Create the file /etc/pam.d/openvpn and add the following contents:

auth sufficient pam_radius_auth.so debug
account sufficient pam_permit.so
session sufficient pam_permit.so

You should now be able to connect to your USG via OpenVPN using Radius authentication with the username and password you configured in the beginning.

Persist your changes

Files will be overwritten after provisions. In order to persist your changes, you'll need to add a script to the /config/scripts directory to copy your files over and schedule a task to complete this after each provision.

Copy the files pam_radius_auth.conf and openvpn from the previous steps into a new directory in /config/scripts. I used ovpn_radius_config.

Copy the following script to /config/script/postprovision.sh:

#!/bin/vbash
readonly logFile="/var/log/postprovision.log"

#restore the ssmtp configuration

cp /config/scripts/ovpn_radius_config/pam_radius_auth.conf /etc
cp /config/scripts/ovpn_radius_config/openvpn /etc/pam.d/openvpn

#the following lines remove the postprovision scheduled task
#do not modify below this line

source /opt/vyatta/etc/functions/script-template

configure > ${logFile}
delete system task-scheduler task postprovision >> ${logFile}
commit >> ${logFile}
save >> ${logFile}
#exit

#end no edit

exit

Mark your script executable: sudo chmod +x /config/scripts/postprovision.sh

Update your config:

configure
set system task-scheduler task postprovision executable path "/config/scripts/postprovision.sh"
set system task-scheduler task postprovision interval 3m
commit
save
exit

Finally, follow the instructions provided here to persist your configuration changes.

Troubleshoot

If you find you have trouble connecting, check your USG logs with show log | grep openvpn

Changelog

  • 8/30/18: Added post provision script
  • 8/29/18: First release
@Juanito87
Copy link

Hi, I'm trying to understand one thing that I'm not sure with this guide.
Copying keys from the easy-rsa install folder to the config folder, is in order to persist them after reboot right?
So each time i create a new user certificate, i need to also copy index and serial?
Thanks for the help.

@unixwizard107
Copy link

unixwizard107 commented Jun 8, 2021

This is a super guide. Thank you all.

I'm putting a Unify system into my Church and a VPN would be extremely handy for the admins of the network infrastructure remotely. At work, we use OpenVPN for some DOD/DOE contracts so I'm basically familiar with the client-side and how to set it up on macOS, Winders, and the basic Unix family.

Note I'm a 45+ yr UNIX hacker (actually wrote one of the original TCP implementations/one of the original Unix kernel developers in the 1970s, so I'm hardly a 'newbie'). But I am newish to the Unify scheme and know enough to be dangerous.

The load of easy-rsa seems to be on the USG. Note that I want to run a Unify Cloud Key2 as the controller there. Last summer when I installed Unify at home on a RPi (without a VPN), I noticed if I made a change to the USG config, the controller wiped it out when I made modifications via that SW. It was clear, they really did not want you to mix Edge commands and commands coming from the controller SW.

Q1) How do I ensure that these changes to the config files are 'known' to the controller so it includes them with any update we might do (like add/delete of users or enable/disable/change other services)?

Q2) Since you are running the OpenVPN server on the USG, it was not clear what my.hostname.com from:

EDIT THIS HOSTNAME
remote my.hostname.com 1194 udp

Needs to be. I would expect that to be the external IP address of the USG assigned by our ISP and then mapped to vpn.ourdomain.org via our DNS provider.

Client then are able to connect to vpn.ourdomain.org

Thanks for the clarifications.

@IceC00l
Copy link

IceC00l commented Aug 27, 2021

On the USG-3P with version 4.4.55 I am getting a "RADIUS server failed to respond" when I use the LAN IP of the USG. However when I revert to 127.0.0.1 it does respond, but I get a "openvpn[30843]: pam_radius_auth: packet from RADIUS server 127.0.0.1 fails verification: The shared secret is probably incorrect." Naturally the shared secret is correct and there are no strange characters. Has there been some firewall hardening on the internal LAN IP? Any heads up?

@jodo234
Copy link

jodo234 commented Mar 30, 2023

Hi :) How can I make sure that one specific user always gets the same IP address? I want to let my NAS dial in from home to the company VPN as client, so that afterwards it can be reached from the office as offsite backup location.

{
   "service":{

          "radius-server":{
                "user": {
                        "elbling-nas01": {
                                "ip-address": "192.168.111.100"
                        }
                }
          }
   }
}

I used this for L2TP VPN, this worked, but for OpenVPN it doesnt seem to use it, as the client always grabs a random IP from that subnet

Edit: fixed it with this config.gateway.json entry and it works fine now :)

{
   "interfaces":{
      "openvpn":{
         "vtun0":{
            "server":{
               "client":{
                  "nas01":{
                     "ip":"192.168.111.100"
                  }
               }
            }
         }
      }
   }
}

@krvi
Copy link

krvi commented Aug 28, 2023

Hi :) How can I make sure that one specific user always gets the same IP address? I want to let my NAS dial in from home to the company VPN as client, so that afterwards it can be reached from the office as offsite backup location.

{
   "service":{

          "radius-server":{
                "user": {
                        "elbling-nas01": {
                                "ip-address": "192.168.111.100"
                        }
                }
          }
   }
}

I used this for L2TP VPN, this worked, but for OpenVPN it doesnt seem to use it, as the client always grabs a random IP from that subnet

Edit: fixed it with this config.gateway.json entry and it works fine now :)

{
   "interfaces":{
      "openvpn":{
         "vtun0":{
            "server":{
               "client":{
                  "nas01":{
                     "ip":"192.168.111.100"
                  }
               }
            }
         }
      }
   }
}

Just what I was looking for. Great thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment