About AWS Client VPN
AWS Client VPN is a managed client-based VPN service provided by AWS. With Client VPN, it is possible to access AWS resources from any location using an OpenVPN-based VPN client.
Recently AWS added ability to use SAML IdP for user authorization and authentication (see image).
However, SAML federation requires you to use proprietary AWS VPN Client which is available only for Windows and macOS. Moreover, the client is a closed source (and likely violates GPL) and very limited.
In this post, I will show how it works under the hood and how to connect to it using the native OpenVPN binary.
What is wrong with “native AWS client”
- Available only for the macOS and Windows. OpenVPN itself supports pretty every platform which can establish VPN connections.
- Closed source. In combination with root access, it adds security risks.
- VERY limited. No hooks, no timeouts, no log export, nothing at all. Just “import profile” and “Connect”. If things are not working – you have to search for the logs in the
- Client whitelisting only very few OpenVPN options. If you will try to add any non-whitelisted option to the config – the client will fail to start. It is including inactivity timeout settings, scripting, etc.
- No documentation about the ability to create customized packages with pre-loaded config
Hopefully, AWS would address some of these limitations in the future. Also, I would suggest making it OSS, as the very likely current client is violating GPL.
How “native client” works
I was using Wireshark and LLDB tools to find out how the client really works. How user flow looks like:
- On the first run, you are importing a profile to the AWS VPN Client. Client detecting
auth-federatekeyword in it and saving config in the
auth-federatekeyword is removed at this stage.
- The user running the AWS VPN Client and using the “connect” menu to connect.
- AWS VPN Client opening a web browser and redirects to the SAML IdP page. After the authorization browser shows “Authentication details received, processing details. You may close this window at any time.” message.
- The client connects to the gateway and traffic starts to go via VPN.
Now, let’s take a look at what is going on internally.
- The wrapper on Mono starting OpenVPN binary (part of the package) and starts the HTTP server at
- Using the OpenVPN management interface it is asking to connect to the provided gateway with the username
ACS::35001. This (of course) fails with an authentication failure, but as failure reason VPN server sends SAML redirect URL.
- Wrapper using this URL and opening it in the browser. If SAML flow succeeds – IdP will redirect the browser with POST data to the
http://127.0.0.1:35001/. HTTP POST data contains
SAMLResponsefield. On this URL mono wrapper capturing it.
- Mono wrapper asks OpenVPN to establish connection second time, but now with N/A as username and
SAMLResponse+ some session data as a password.
- AWS VPN server validates them, and if they are looking valid (e.g. signed by corresponding IdP, etc) start the session.
How to connect with OSS OpenVPN to the AWS Client VPN using SAML
I decided to emulate this flow. I started with writing small HTTP server on golang, which listens on
127.0.0.1:35001 and saving
SAMLResponse POST form field to the file. The next step was to write a shell wrapper which emulates the activity of the Mono wrapper in the AWS Client. I decided not to use the management interface but to run the OpenVPN binary directly.
Surprisingly I been able to get the connection up, but only with
acvc-openvpn binary from the
AWS VPN Client.app package. So I decided to build OpenVPN myself to debug why it is not working with OSS binary. After some experiments reason was found:
- Password length in the OSS OpenVPN is up to 128 bytes. SAML response is ~11Kb. I extended this size but got another problem related to the TLS error.
- After all, I found that the password block is not fitting into
TLS_CHANNEL_BUF_SIZElimit in the OpenVPN, so I had to extend it as well.
My patch is available here. At this point, I was able to connect and use a VPN. Also, it clearly shows that OpenVPN source code was modified, so AWS have to publish it, according to GPL requirements.
So far my PoC can connect to the VPN Server. After connect, it is working the same way as AWS client. I already tested both TCP and UDP setup, with 443 and 1194 ports. Some things to do (if I will have some time)
- Make golang wrapper smarter and replace shell wrapper entirely
- Think how to integrate this with tunnelblick or other OSS UI for the OpenVPN
As usual – patches and contributions are welcome, repository URL is github.com/samm-git/aws-vpn-client.