Integrated Enterprise OpenVPN Configuration
I've scoured the Internet high and low attempting to locate a definitive source of how to configure openvpn in a manner that is secure, and most importantly, is 100% integrated with Active Directory. I am way too lazy to have multiple systems to take care of. I want:
- OpenVPN Client with no customization (besides config file, of course)
- Tight Integration with Active Directory
- Multiple Factors of Authentication (MS Certificate Store + Password)
- Super-Easy to Manage
Here's what I've come up with. Is this solution secure and does it meet the needs above?
At this point, I'm going to assume there is an active directory server somewhere that the openvpn server can connect to and that the client machine is joined to the domain.
I'm also going to assume you have an AD CA deployed.
It all starts with a trip through the age-old process of auto-enrollment. I'm not going to get in to the specifics here, as this note describes the process well. The only note here is that I auto-enrolled machine certificates, not user certificates.
Once the GPO is rolled out, all end-users will have an unexportable, private machine certificate that is signed by the the domain CA.
Now, can we marry the AD auto-enrolled certificates with OpenVPN? I think so.
On the AD CA, export the CA public certificate. In my case, I named it the same as the machine name with a .crt at the end.
These examples assume an ubuntu-based operating system. Adjust the packages and ca-certificates step for your distribution.
# install our requisite packages apt-get -y install openvpn openvpn-auth-ldap # install our ca key as root ca mkdir /usr/share/ca-certificates/extra/ cat > /usr/share/ca-certificates/extra/contoso-CONAWSDC01-CA.crt << EOM -----BEGIN CERTIFICATE----- MIIDBjCCAe6gAwIBAgIQf0VK+i3wuppE8eWGBn525TANBgkqhkiG9w0BAQsFADAc ... KYTE7h9qTnJ4EQ== -----END CERTIFICATE----- EOM # enable this root ca dpkg-reconfigure ca-certificates # go to /etc/openvpn for the remainder of this exercise cd /etc/openvpn # create our private key openssl genrsa -des3 -out server.key 2048 # create a csr for the domain controller to sign. you *should* have the correct CN, but it's not required openssl req -key server.key -out server.csr # then, over on the CA, we need to sign this key (note the webserver cheat) # certreq.exe -submit -attrib "CertificateTemplate:WebServer" .\bopawsvpn02.txt # create ta.key, which is used by the tls-auth pragma to prevent DOS attacks openvpn --genkey --secret /etc/openvpn/ta.key # symlink to our ca key ln -s /usr/share/ca-certificates/extra/bop-BOPAWSDC01-CA.crt ca.crt # enable ip forwarding sysctl -w net.ipv4.ip_forward=1 # ( update /etc/sysctl.conf ) # If you are hosting the OpenVPN server on an Amazon Web Services (AWS) EC2 instance make sure "Source/Destination Checking" is disabled on the instance's Elastic Network Interface (enabled by default)
I started with the basic road warrior configuration and tweaked it for my needs. Mostly, I added dns settings and routes.
I was unable to get chroot to work well due to dependencies on other modules for LDAP authentication.
port 1194 proto udp dev tun sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem tls-auth ta.key 0 topology subnet server 10.22.0.0 255.255.0.0 ifconfig-pool-persist ipp.txt push "dhcp-option DNS 10.20.1.4" push "dhcp-option DOMAIN contoso.com" push "route 10.0.0.0 255.255.0.0" push "route 192.168.1.0 255.255.255.0" keepalive 10 120 cipher AES-128-CBC comp-lzo user nobody group nogroup persist-key persist-tun status openvpn-status.log verb 3 plugin /usr/lib/openvpn/openvpn-auth-ldap.so /etc/openvpn/ldap.conf
In order to configure LDAP, you're going to need service account to bind with before we can ask for authentication. I created an account with a long password with very minimal privileges.
<LDAP> # LDAP server URL URL "ldap://10.20.1.4" # Bind DN (If your LDAP server doesn't support anonymous binds) # BindDN uid=Manager,ou=People,dc=example,dc=com BindDN "CN=auth,OU=Service Accounts,DC=contoso,DC=com" # Bind Password Password areallyreallyreallylongpassword # Network timeout (in seconds) Timeout 15 # Enable Start TLS TLSEnable no # Follow LDAP Referrals (anonymously) FollowReferrals yes # TLS CA Certificate File TLSCACertFile /usr/local/etc/ssl/ca.pem # TLS CA Certificate Directory TLSCACertDir /etc/ssl/certs # Client Certificate and key # If TLS client authentication is required #TLSCertFile /usr/local/etc/ssl/client-cert.pem #TLSKeyFile /usr/local/etc/ssl/client-key.pem # Cipher Suite # The defaults are usually fine here # TLSCipherSuite ALL:!ADH:@STRENGTH </LDAP> <Authorization> # Base DN BaseDN "OU=local,DC=contoso,DC=com" # User Search Filter #SearchFilter "(&(uid=%u)(accountStatus=active))" SearchFilter "(sAMAccountName=%u)" # Require Group Membership RequireGroup false # Add non-group members to a PF table (disabled) #PFTable ips_vpn_users </Authorization>
I wanted a single configuration that I would send to all of my users that worked without embedding an identifier or certificate in the .ovpn file. After some experimentation and a less-than-well-documented feature of the cryptoapi, I found this cheat. By specifying my domain in the SUBJ call to cryptoapi, OpenVPN will find the first certificate with that name, which is my machine's cert.
I did have to embed the CA certificate, as I couldn't figure out how to pull that from the certificate store too. But that's public information, so I'm not too worried.
client dev tun proto udp sndbuf 0 rcvbuf 0 remote vpn.contoso.com 1194 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server cipher AES-128-CBC comp-lzo key-direction 1 verb 3 auth-user-pass setenv opt block-outside-dns # block all non-vpn dns queries # redirect-gateway def1 # route *all* traffic through vpn # this is my domain fqdn. allows one config for multiple connections cryptoapicert "SUBJ:.contoso.com" <ca> -----BEGIN CERTIFICATE----- MIIDBjCCAe6gAwIBAgIQf0VK+i3wuppE8eWGBn525TANBgkqhkiG9w0BAQsFADAc ... KYTE7h9qTnJ4EQ== -----END CERTIFICATE----- </ca>
Am I missing any obvious security holes?