OpenVPN + SSO via OAUTH2
Tags: openvpn, oidc, oauth, keycloak
Published: 2024-02-09 21:09:18

Update: Check latest up2date docs: https://github.com/jkroepke/openvpn-auth-oauth2/wiki

# Introduction

We all love free Open Source software and OpenVPN is one of the most open source VPN solutions around the world.

While OpenVPN works great on Small Business, issuing VPN certificates on a large enterprise environment can be an issue. Many companies are buying VPN appliances with Self-Service Portal to on board OpenVPN users.

In history, a lot of solutions based on OpenVPN plugins or PAM modules together with OpenVPN PAM module. Another solution was OpenVPN LDAP module, but the experience for an MFA was terrible.

After long reason, I figure out that OpenVPN 2.5 or higher supports deferred authentication. With deferred authentication method. OpenVPN can tell the users’ client to open a browser page. This pattern can be used to initialize SSO flows and fully delegates the authentication to an external programs.

Using the clients’ browser to initiate the authentication has a lot of benefits. Looking to Azure AD or Keycloak, modern MFA technics a side from OTP can be used for a secure authentication, e.g. FIDO2 (WebAuth) or Microsoft Authenticator (push). Additional restrictions can be applied through an IDP like Keycloak for example restrict the client IP address or allow connections only from MDM devices (Intune; Conditional Access).

---

# Setup OpenVPN

[openvpn-auth-oauth2]https://github.com/jkroepke/openvpn-auth-oauth2() just adds an additional authentication layer on OpenVPN. You can still use client certificates if you wish or omit them, if you no longer want to depend on them.

openvpn-auth-oauth2 is a dedicated binary which start a HTTP server and connect to an OpenVPN management interface. You have to enable to management interface on your OpenVPN server first.

```
# /etc/openvpn/password.txt is a password file where the password must be on first line
management /run/openvpn/server.sock unix /etc/openvpn/password.txt
management-client-auth
```

**Keep in mind, never expose the management interface to the public internet!** You have to create as password.txt near to your OpenVPN configuration file. Put a strong password into the first line and save the file.

Next, you have to create a standard OAUTH2 application on the IdP on your choice. As example, we will use Entra ID:

# Create a App Registration in Entra ID

1. Login as admin into your tenant
2. Open [App registrations](https://aad.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) in Azure AD portal
3. Click `new registration`
4. Pick a name, chose a “Supported account types”-option. Leave the default value, if you are not sure.
5. For redirect uri, choice Web and enter the public endpoint of `openvpn-auth-oauth2`, for example `https://openvpn-auth-oauth2.example.com/oauth2/callback`. Behind the URL, the `openvpn-auth-oauth2` binary should be accessible.
6. Click Register.
7. Copy the tenant-id and client-id. You need the both as configuration option for `openvpn-auth-oauth2`.
8. After creation, select `Certificates & secrets` on the left side.
9. Select the tab `Client secrets` and create a new client secret.
10. Copy the client-secret. You need it as configuration option for `openvpn-auth-oauth2`.
11. Then, select Token configuration on the left side.
12. Add optional claim
13. On the right panel, select ID as token type
14. Select `ipaddr` from the list of claims.
15. Select Add.

# Setup openvpn-auth-oauth2

After setup OpenVPN and you OAUTH2 App/Client, all prerequirements are met for openvpn-auth-oauth2.

Download the binary from [GitHub Releases](https://github.com/jkroepke/openvpn-auth-oauth2/releases/latest) and store the binary under `/usr/local/bin/openvpn-auth-oauth2`. To run the binary, setup a systemd service.

```ini
[Unit]
Description=OpenVPN authenticator
Documentation=https://github.com/jkroepke/openvpn-auth-oauth2
Wants=network-online.target openvpn.service
After=network-online.target openvpn.service openvpn@.service openvpn-server@.service
PartOf=openvpn.service openvpn@.service openvpn-server@.service

[Service]
DynamicUser=true
Group=nogroup
ExecStart=/usr/local/bin/openvpn-auth-oauth2
EnvironmentFile=/etc/sysconfig/openvpn-auth-oauth2
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ConfigurationDirectory=openvpn-auth-oauth2
ReadWritePaths=-/run/openvpn -/run/openvpn-server
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoExecPaths=/
ExecPaths=/usr/local/bin/openvpn-auth-oauth2
Restart=always
RestartSec=5s
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

# Configure

If you run the binary as systemd service, you can use the `/etc/sysconfig/openvpn-auth-oauth2` file toset up the environment variables. If you are preferring Docker containers, use the known tools to declare environment variables

```ini
CONFIG_OAUTH2_ISSUER=https://login.microsoftonline.com/<tenant-id>/v2.0
CONFIG_OAUTH2_CLIENT_ID=
CONFIG_OAUTH2_CLIENT_SECRET=
CONFIG_HTTP_SECRET=  # Define a random value with 16 or 24 chacters
CONFIG_HTTP_BASEURL=https://  # Define the public http endpoint here.
```

After configuration, you can start the service with `systemctl enable --now openvpn-auth-ouath2` and you are done.