Yes!
Two provisioners are needed: One for OpenVPN server certificates, and one for OpenVPN client certificates.
First, the client provisioner. In this case we will use an OIDC provisioner so that our clients can get certificate via SSO:
cat <<EOF > openvpn-client.tpl
{
"subject": {"commonName": {{ toJson .Insecure.CR.Subject.CommonName }}},
"sans": {{ toJson .SANs }},
"keyUsage": ["digitalSignature", "keyAgreement"],
"extKeyUsage": ["clientAuth"]
}
EOF
step beta ca provisioner add "OpenVPN Client" --create \\
--type OIDC \\
--x509-default-dur 24h \\
--x509-template openvpn-client.tpl \\
--client-id 1087160488420-8qt7bavg3qesdhs6it824mhnfgcfe8il.apps.googleusercontent.com \\
--client-secret udTrOT3gzrO7W9fDPgZQLfYJ \\
--configuration-endpoint <https://accounts.google.com/.well-known/openid-configuration>
Be sure to adjust client-id
, client-secret
, and configuration-endpoint
for your environment.
Now let's get a test certificate and inspect it:
$ step ca certificate carl+openvpn@smallstep.com client.crt client.key
..
.. SSO flow happens ..
..
✔ CA: <https://your.team.ca.smallstep.com>
✔ Certificate: client.crt
✔ Private Key: client.key
$ step certificate inspect client.crt
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 288020726091407945952925267215046898938 (0xd8aec62b4f62a0092ea741ad72af74fa)
Signature Algorithm: ECDSA-SHA256
Issuer: CN=Smallstep Intermediate CA
Validity
Not Before: Nov 8 20:13:16 2021 UTC
Not After : Nov 9 20:14:16 2021 UTC
Subject: CN=carl+openvpn@smallstep.com
Subject Public Key Info:
Public Key Algorithm: ECDSA
Public-Key: (256 bit)
X:
2a:f0:6e:7e:09:a9:67:d8:e9:05:93:a2:db:82:3b:
97:73:02:8f:10:18:98:f5:11:fe:c7:b5:bd:97:b4:
7f:1e
Y:
ef:a9:b4:63:d2:b3:e2:71:e2:05:81:a7:39:c2:c4:
29:e3:1c:dc:c1:74:1b:38:fb:9c:9f:b3:48:ef:ca:
b2:df
Curve: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Agreement
X509v3 Extended Key Usage:
Client Authentication
X509v3 Subject Key Identifier:
F4:18:7C:8B:6E:9F:32:29:E5:56:3F:33:73:6C:15:C5:44:A0:D1:FA
X509v3 Authority Key Identifier:
keyid:96:4B:06:B3:08:06:3E:AD:25:78:8C:66:F3:42:14:39:D2:9A:40:47
X509v3 Subject Alternative Name:
<email:carl+openvpn@smallstep.com>
X509v3 Step Provisioner:
Type: JWK
Name: OpenVPN Client
CredentialID: T3km0338Q1EU6sUXugHKayRVvY4a20nD4yqSgbRkQOE
Signature Algorithm: ECDSA-SHA256
30:45:02:21:00:e1:ca:00:ef:cd:7b:f9:f4:3a:68:41:56:96:
56:59:b8:f9:17:e2:7f:3c:90:eb:9b:0e:49:a4:e9:a3:3d:89:
47:02:20:10:91:fa:1a:c5:7b:1d:4a:e3:93:3c:ff:3e:46:7e:
17:57:51:e1:85:e0:a3:94:82:ff:5f:f1:12:0c:b7:7c:95
Crucially, OpenVPN requires that the client certificate only have "Client Authentication" Extended Key Usage (EKU). And, similarly, the server certificate will only have "Server Authentication" EKU.
Let's create the server certificate provisioner now.
cat <<EOF > openvpn-server.tpl
{
"subject": {{ toJson .Subject }},
"sans": {{ toJson .SANs }},
"keyUsage": ["digitalSignature", "keyEncipherment", "keyAgreement"],
"extKeyUsage": ["serverAuth"]
}
EOF
step beta ca provisioner add "OpenVPN Server" --create \\
--type ACME \\
--x509-default-dur 2160h \\
--x509-template openvpn-server.tpl
? ACME is the preferred provisioner type here, but you could alternatively use JWK.
Finally, create a server certificate:
step ca certificate --provisioner="OpenVPN Server" \\
vpn.smallstep.com server.crt server.key
There's a couple of important caveats when it comes to renewal of this server certificate:
- Because the server certificate doesn't have Client Authentication enabled (TLS server certificates typically do), this certificate won't be renewable via
step ca renew
, which uses mutual TLS and authenticates the new certificate using the old one. So, you'll need a fresh certificate and private key (viastep ca certificate
, or any ACME client) when your current certificate is approaching expiry. - OpenVPN server doesn't appear to have a way to reload certificate files without restarting the server and potentially impacting client connections. To minimize the impact of that, you may want to adjust the default certificate duration to account for that.
To configure the OpenVPN side of things, here's a starting point.
⚠️ These are test configurations, not for production use.
Client Configuration:
# client
proto udp
remote vpn.smallstep.com
# authentication
tls-client
key client.key
cert client.crt
ca root_ca.crt
tls-crypt myvpn.tlsauth
cipher AES-256-GCM
verify-x509-name vpn.smallstep.com name
# network
dev tun
topology subnet
pull
Server Configuration:
# server
proto udp
local 0.0.0.0
# authentication
tls-server
key server.key
cert server.crt
ca root_ca.crt
dh dh2048.pem
remote-cert-eku "TLS Web Client Authentication"
tls-crypt myvpn.tlsauth
cipher AES-256-GCM
# network
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
myvpn.tlsauth
is a shared secret, created by runningopenvpn --genkey --secret myvpn.tlsauth
root_ca.crt
is your CA’s root certificate (fetch it withstep ca root > root_ca.crt
)dh2048.pem
is created by runningopenssl dhparam -out dh2048.pem 2048
Helpful reading as you move this configuration to production: