For numerous reasons, some servers are protected with a VPN, but it shouldn't hold you back from using a CI/CD. In this guide (aka my first one), I'll describe how to connect to your VPN inside your pipeline. Let's get started!
File Structure
It is always suggested to put your pipeline files inside one folder, to keep things clean. This is a file structure for CircleCI using Forticlient VPN.
├── .circleci
└── config.yml
└── forti-vpn.sh
└── forticlient-sslvpn_4.4.2333-1_amd64.deb
I suggest you put most of your files needed, inside the .circleci folder, as this can keep the structure clean and put all your pipeline files hidden away from your code.
The 'root' of your repo inside the machine is:
/home/$CIRCLE_PROJECT_REPONAME/
I'll dive into the two Forticlient files later in this post.
VPN Setup
It's actually easier than you might think. The build tool (CircleCI, Jenkins, Azure, Github Workflow,..) actually just utilizes by default docker images (read more). Usually, the build automation tool has some agents (Linux containers) already running. But since we need some special TUN/TAP drivers, we'll need to spin up a Linux VM ourselves.
Don't worry, you don't need anything special for it, just 1 line of code at the top of your config.yml
.
version: 2.1
jobs:
build:
machine:
image: ubuntu-1604:201903-01
The machine line tells the tool to spin up a specific Linux machine, because in the standard ones, the TUN/TAP driver are disabled by default.
VPN Clients
FortiClient
Environment variables
Add your FortiClient host (with port), username and password as environment variables in the UI of CircleCI
- FORTI_HOST hostname:port
- FORTI_USER forticlient username
- FORTI_PASSWORD forticlient password
Install FortiClient
At the time of writing, I wasn't able to install forticlient inside a cmd with a curl command or otherwise, so I'm just passing the installation file inside the .circleci
folder. You can download the file from here. On Azure, the following cmd did work:
sudo wget https://hadler.me/files/forticlient-sslvpn_4.4.2333-1_amd64.deb
Now we have the installer, but we still need to create a step which installs the tool inside our Linux.
- run:
name: Install FortiClient
command: |
mkdir -p /home/$CIRCLE_PROJECT_REPONAME/vpn/tmp
cd /home/$CIRCLE_PROJECT_REPONAME/vpn
sudo apt-get update
echo "Install Expect"
sudo apt-get install expect
echo "Install PPP"
sudo apt-get install ppp expect
echo "Install FortiClient"
sudo cp ~/.circleci/forticlient-sslvpn_4.4.2333-1_amd64.deb .
sudo dpkg -x forticlient-sslvpn_4.4.2333-1_amd64.deb /home/$CIRCLE_PROJECT_REPONAME/vpn
sudo truncate -s 0 opt/forticlient-sslvpn/64bit/helper/License.txt
sudo ./opt/forticlient-sslvpn/64bit/helper/setup >/dev/null < <(echo y)
*you don't need to change anything in this script, the $CIRCLE_PROJECT_REPONAME is a circleci env var, that automatically grabs your repo name.
Connect to the VPN
You only need to provide your credentials as environment variables. Create this file and paste in the following script (or you can get this file)
Then, add a step to connect to the VPN using the script.
sudo wget https://gist.githubusercontent.com/buelenw/0887937a8cffaca9c9ec84c8b4c47a06/raw/a8575708e860b33f5f51ae834ee64558d1a57d14/forti-vpn.sh
sudo sed -i s/"<FORTI_HOST>"/$FORTI_HOST/ forti-vpn.sh
sudo sed -i s/"<FORTI_USERNAME>"/$FORTI_USER/ forti-vpn.sh
sudo sed -i s/"<FORTI_PASSWORD>"/$FORTI_PASSWORD/ forti-vpn.sh
sudo chown root:root forti-vpn.sh
sudo chmod 600 forti-vpn.sh
sudo chmod +x forti-vpn.sh
nohup sudo ./forti-vpn.sh &
sleep 10
HOST=$SERVER
ip=${HOST%%:*}
ping -c 1 $ip ; echo $?
*the sed command will place your credentials into the login script
the sleep is needed for to VPN connection to establish
After the sleep there's a check which pings the SERVER and returns 1 if the server is unreachable. This will automatically stop the CircleCI workflow.
Make sure to kill the VPN connection at the end of your job
sudo pkill forti
This guide is working with Forti-client, but I'm pretty sure it's the same solution for all other VPN-clients (like Cisco-anyconnect and OpenVPN. It has been tested on both CircleCi & Azure DevOps.
References
https://discuss.circleci.com/t/vpn-connection-from-build-machine/4033
https://confluence.jaytaala.com/display/TKB/Continuous+and+automated+VPN+connection+with+FortiClient+%28CLI+only%29+using+bash+and+expect+scripting
https://serverfault.com/questions/412220/fortinet-ssl-vpn-client-setup-without-gui-on-linux-centos/484988#484988?newreg=9f12134c83234e27bb2ec21784900b36
https://gist.github.com/PieterScheffers/20583b65ef171cf94db16e6c659498f3
Top comments (2)
Hi, Thanks for a good tutorial! You are saying that this has been tested on Azure DevOps. Do you have any code example?
I´m trying to follow your steps but doing this in Azure DevOps instead and I´m struggling a little bit:) I´m getting the error message: The certificate for the SSLVPN server is invalid.
Thanks!
Sorry, I've just seen this comment now. Still having issues? I'm not using this any longer, so I can't provide a working example. Any issues I can help you with?
For the invalid error, did you try to connect to the vpn on your own machine with the certificate?