Page 1 of 1

OpenVPN + Google Authenticator + PAM (Working)

Posted: Thu Mar 31, 2022 6:26 pm
by warren.smith
Hi Guys,

This is kind of a brain dump of the work I did to get OpenVPN working, where MFA is provided by Google Authenticator and PAM working together.
In my case, I wanted something with low-code that can be easily replicated without debugging a python script in ten years :-)

Prerequisites (these are beyond the scope of this howto):
  1. Get Debian working. In my case, that's Debian 10 on AWS EC2 (arm), but should work on all Debian 10, or 11 platforms.
  2. Get PAM working. In my case, I have PAM authenticating against WINBIND and ultimately an ADC for first factor (username + password).
  3. Get your Certificate Authority set up. In my case, I used the easy-rsa suite on a separate EC2 instance.
  4. Get google authenticator working. See https://github.com/google/google-authen ... /README.md
  5. Get OpenVPN >= 2.4.10. There's a "feature" that's included here that allows the MFA token to be passed to PAM. In my case, that meant adding the buster-backports repository and installing the openvpn/buster-backports package
I didn't want just anyone getting through PAM, so I created a new PAM service stack just for openvpn. Note that this doesn't use winbind (my production version does). Note that this is optional as the "other" service will pick up OpenVPN.

Code: Select all

cat >> /etc/pam.d/openvpn << EOM
auth    optional   pam_faildelay.so  delay=3000000
auth    [success=1 default=ignore]      pam_unix.so nullok_secure
auth    requisite                       pam_deny.so
auth    required                        pam_permit.so
auth    required pam_google_authenticator.so nullok
EOM
Then we can configure OpenVPN thusly. IMPORTANT - Note the challenge for google auth is "Verification Code", not "pin" as the docs suggest. You can either change that in pam (with authtok_prompt) to "pin".

Code: Select all

# basic tunnel configuration
port 1194
proto udp
dev tun
sndbuf 0
rcvbuf 0
keepalive 10 120
cipher AES-256-CBC
auth SHA256
link-mtu 1500
comp-lzo

# enable multi-factor authentication with google authenticator
reneg-sec 0
plugin openvpn-plugin-auth-pam.so "openvpn login USERNAME password PASSWORD 'verification code' OTP"
verify-client-cert none
username-as-common-name

# lose permissions, but read the keys before dropping permissions (persist-key)
user nobody
group nogroup
persist-key
persist-tun

# certificates and persistent ip store
ca     /etc/openvpn/ca.crt
key    /etc/openvpn/server/private.key
cert   /etc/openvpn/server/server.crt
dh     /etc/openvpn/server/dh.pem
status /etc/openvpn/status.txt
ifconfig-pool-persist /etc/openvpn/server/ipp.txt
crl-verify /etc/openvpn/server/crl.pem

# push down dns, domain and routes
topology subnet
server a.b.c.d 255.255.255.0
push "dhcp-option DNS a.b.c.e"
push "dhcp-option DNS a.b.c.f"
push "dhcp-option DOMAIN mydomain.local"
push "route 10.0.0.0 255.0.0.0"
push "block-outside-dns"
Then I can distribute a single .ovpn configuration to each user:

Code: Select all

client
dev tun
proto udp
sndbuf 0
rcvbuf 0
remote remote.mydomain.com 1194
link-mtu 1500
resolv-retry infinite
remote-cert-tls server
cipher AES-256-CBC
auth SHA256
comp-lzo
key-direction 1
verb 3

reneg-sec 0
auth-user-pass
static-challenge "MFA OTP" 1

<ca>
-----BEGIN CERTIFICATE-----
YOUR CA CERTIFICATE HERE
-----END CERTIFICATE-----
</ca>
Don't forget:
  • To enable IPV4 forwarding
  • To adjust the FORWARD chain to ALLOW
  • To remove the source/destination check in EC2 (if applicable)