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
@n8barber
Copy link

n8barber commented Jul 25, 2019

Thanks, @xantari, it's good to know I have the same Radius server settings. Tunnel type and medium are per user. Please click on "user" next to "server" in the screen shot above. Attached is a screenshot of the Radius User settings from my UniFi Network app on my iPhone (it won't look like your screenshot but the content is identical).

IMG_3148

@xantari
Copy link

xantari commented Jul 28, 2019

Sorry for delay. Here is what I have. No Tunnel selections.

image

@smwoodward
Copy link

If I’m not mistaken I’ve tried several different options on the tunnel types and still got the same issues. I’m not 100% sure if I used none, and kinda think I didn’t because I also use it for WPA enterprise for my wireless. If I get a chance I’ll try to do all of this again as I haven’t persisted the changes after a reboot because I wanted it to work once before setting the persistence.

@n8barber
Copy link

n8barber commented Jul 31, 2019

Tunnel and medium type don't seem to matter. Many thanks to @xantari for all your help. You didn't give me the answer, but you definitely helped me narrow down the issue. @smwoodward, I hope this helps you too.

The issue is DO NOT USE NOTEPAD to create /etc/pam.d/openvpn

From a putty/ssh terminal, I looked at the openvpn logs with:
show log | grep openvpn

Jul 31 17:32:02 HomeUSG openvpn[9532]: PAM unable to dlopen(pam_permit.so#015): /lib/security/pam_permit.so#015: cannot open shared object file: No such file or directory
Jul 31 17:32:02 HomeUSG openvpn[9532]: PAM adding faulty module: pam_permit.so#015
Jul 31 17:32:02 HomeUSG openvpn[9532]: pam_radius_auth: unrecognized option 'debug#015'
Jul 31 17:32:02 HomeUSG openvpn[9538]: 174.207.27.57:8033 PLUGIN_CALL: plugin function PLUGIN_AUTH_USER_PASS_VERIFY failed with status 1: /usr/lib/openvpn/openvpn-plugin-auth-pam.so
Jul 31 17:32:02 HomeUSG openvpn[9538]: 174.207.27.57:8033 TLS Auth Error: Auth Username/Password verification failed for peer
Jul 31 17:32:02 HomeUSG openvpn[9538]: 174.207.27.57:8033 Peer Connection Initiated with [AF_INET]174.207.27.57:8033

I recognized a pattern of #015 everywhere there was a carriage return in the /etc/pam.d/openvpn file.
To fix it, putty/ssh into your USG and use vi to edit the text file directly in the terminal:

sudo bash
cd /etc/pad.d
vi openvpn

2019-07-31

Use your keyboard arrows to move to the end of the lines and press "delete" to remove the ^M carriage returns
Type :wq to write and quit vi (that's a colon to access vi commands, then wq, then enter)

After that, openvpn with radius starts working. Finally

Stupid Notepad

@jcconnell
Copy link
Author

Thanks to all of you for your comments here. In the next few days, I'll work to incorporate all of your notes into the original.

@n8barber
Copy link

Thanks to all of you for your comments here. In the next few days, I'll work to incorporate all of your notes into the original.

Well done putting it all together in the first place. With some of these "gotchas" removed, it'll be the ultimate reference! Thanks again!

@xantari
Copy link

xantari commented Jul 31, 2019

@n8barber Ohh yeah! I ended up using vi for everything so I was sure it would be in good linux format with LF's only.

@smwoodward
Copy link

Yeah I will admit that I did not use vi. I’m not much of a Linux guy so I would have never known that. Lol.

But hey! Since I’m not a Linux guy, I was useful in screwing it up and learning the fix for other scrip kitties like me. Lol

@smwoodward
Copy link

There also seems to be an issue with the easy-rsa deb package in the above instructions.

@conblem
Copy link

conblem commented Aug 15, 2019

@jcconnell any news on the changes :)?

@jcconnell
Copy link
Author

@conblem not yet. I'm traveling and testing the changes would risk bringing down the router for users while I'm away. Soon.

@PhillySports
Copy link

Hello.

Sorry if this is a stupid question, but beginning with the instructions at "Create a .ovpn file" section. I am unclear how this is supposed to be done? Am I supposed to create a txt file using the nano command?

Also, at some point am I actually supposed to download the OpenVPN software -- if so, how is that done?

Thanks in advance for all you help.

@shpydah
Copy link

shpydah commented Nov 18, 2019

Great guide and comments all around, thank you so much for putting this together.

I'm just now in the process of setting this up on our new USG. One question I'd love to poll everyone on is the approximate time it took everyone to finish generating DH parameters. I'm currently at more than 2 hours and it's still running. Some resources I've found estimate that a 2048 DH generation can take upwards of 12 hours or more, has anyone found this to be accurate?

@fongd
Copy link

fongd commented Dec 2, 2019

There also seems to be an issue with the easy-rsa deb package in the above instructions.

You can use http://ftp.us.debian.org/debian/pool/main/e/easy-rsa/easy-rsa_2.2.2-2_all.deb as the updated path to the EasyRSA .deb package. Otherwise just browse to http://ftp.us.debian.org/debian/pool/main/e/easy-rsa/ and download the latest 2.x release. The 3.x releases don't work with the USG.

@xatru
Copy link

xatru commented Dec 25, 2019

Hello.

Sorry if this is a stupid question, but beginning with the instructions at "Create a .ovpn file" section. I am unclear how this is supposed to be done? Am I supposed to create a txt file using the nano command?

Also, at some point am I actually supposed to download the OpenVPN software -- if so, how is that done?

Thanks in advance for all you help.

You create your .ovpn file (or copy the file) on the machine that is supposed to connect to the VPN and not on the USG. On that system you also need the VPN software. For Windows systems you find the software on https://openvpn.net/community-downloads/, for Unix systems use the internal package manager, e.g.,sudo apt install openvpnfor Ubuntu.

Using a simple text editor (nano, notepad, ...) is sufficient, but be careful with the line endings (use thedos2unixcommand if you see ^M line endings by inspecting the .opvn file withcat <filename.ovpn>)

Great guide and comments all around, thank you so much for putting this together.

I'm just now in the process of setting this up on our new USG. One question I'd love to poll everyone on is the approximate time it took everyone to finish generating DH parameters. I'm currently at more than 2 hours and it's still running. Some resources I've found estimate that a 2048 DH generation can take upwards of 12 hours or more, has anyone found this to be accurate?

I did the same mistake and generated the keys directly on the USG. For the DH key, it took really a long time (~3h) but the solution is easy: Generate the keys on a Linux/Unix machine or use the Unix subsystem for Windows and copy it to the USG via SSH/SCP.

@xatru
Copy link

xatru commented Dec 25, 2019

I did all the steps above and my VPN works quite fine now with one exception: IPv6

All the traffic from the client is tunneled via VPN to the internet if I use IPv4 addresses but it does not work for IPv6. The IPv6 traffic is still going directly form the client through the e.g., hotel hostpot. As more and more services are using IPv6 addresses this might be an issue now, but for sure it will be in the future.
As far as I was able to figure out, the option push route-ipv6 has to be set in the server config, but I wasn't able to figure out the correct settings here. Any experience on that?

@schwubbs
Copy link

First of all, thank you very much for this great guide! Such a well-written manual - not too much and not too little - doesn't come across to me very often.

So far, everything went well for me at first go: The connection is established and the complete data traffic of my OpenVPN client is routed via the USG to the WAN.

But one thing unfortunately doesn't work: My USG establishes Auto IPsec VTI connections to three other USGs. The clients from the different subnets can reach each other. OpenVPN clients can only reach the "home subnet", but unfortunately not the other subnets (it works for the remote user L2TP server).

Does anyone have any ideas how I can solve the problem? I've already played around with the routes and the firewall a bit and also searched the internet, but unfortunately I haven't found anything that causes it.

@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