How to set up a VPN connection during builds

In case you need to connect to a private network during your builds, or if you want to restrict access to your environment to a specific IP address, we suggest configuring a VPN connection as follows.

Note that you must use one of the following executors:

 

machine [Linux] executor (Available machine images)

OpenVPN (2.x)

  • Base64-encode the OpenVPN client configuration file, and store it as an environment variable.
  • If the VPN client authentication is credentials-based (user-locked profile), you'll also need to add the username and password as environment variables (VPN_USER and VPN_PASSWORD).
version: 2.1
workflows:
  btd:
    jobs:
      - build
jobs:
  build:
    machine:
      image: ubuntu-1604:202004-01
    steps:
      - run:
          name: Install OpenVPN
          command: |
            sudo apt-get update
            sudo apt-get install openvpn -y
- run: name: Check IP before VPN connection command: | ifconfig route -n sudo netstat -anp cat /etc/resolv.conf echo "Public IP before VPN connection is $(curl checkip.amazonaws.com)"
- run: name: VPN Setup background: true command: | echo $VPN_CLIENT_CONFIG | base64 --decode > /tmp/config.ovpn

if grep auth-user-pass /tmp/config.ovpn; then
if [ -z "${VPN_USER:-}" ] || [ -z "${VPN_PASSWORD:-}" ]; then
echo "Your VPN client is configured with a user-locked profile. Make sure to set the VPN_USER and VPN_PASSWORD environment variables"
exit 1
else
printf "$VPN_USER\\n$VPN_PASSWORD" > /tmp/vpn.login
fi
fi

#IMPORTANT: Include the following line and the `--route` options to exclude the connection from CircleCI and the link-local range
phone_home="$(netstat -an | grep ':22 .*ESTABLISHED' | head -n1 | awk '{ split($5, a, ":"); print a[1] }')"
echo $phone_home

if grep auth-user-pass /tmp/config.ovpn; then sudo openvpn --config /tmp/config.ovpn --auth-user-pass /tmp/vpn.login \ --route $phone_home 255.255.255.255 net_gateway \ --route 169.254.0.0 255.255.0.0 net_gateway > /tmp/openvpn.log
else
sudo openvpn --config /tmp/config.ovpn \
--route $phone_home 255.255.255.255 net_gateway \
--route 169.254.0.0 255.255.0.0 net_gateway > /tmp/openvpn.log
fi
- run: name: Wait for the connection to be established and check
command: |
while [ $(cat /tmp/openvpn.log|grep -c "Initialization Sequence Completed") == 0 ]; do
echo "Attempting to connect..."
sleep 1;
done
echo "VPN Connected"
echo "Public IP is now $(curl checkip.amazonaws.com)" - run: name: Run commands in our infrastructure command: | # A command # Another command

  - run:
name: Disconnect from OpenVPN
command: sudo killall openvpn || true
when: always

 

OpenVPN Connect (OpenVPN 3)

version: 2.1
workflows:
  btd:
    jobs:
      - build
jobs:
  build:
    machine:
      image: ubuntu-1604:202004-01
    steps:
      - run:
          name: Install OpenVPN
          command: |
            sudo apt update && sudo apt install apt-transport-https
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
sudo apt-key add openvpn-repo-pkg-key.pub
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-xenial.list
sudo apt update && sudo apt install openvpn3
- run: name: Check IP before VPN connection command: | echo "Public IP before VPN connection is $(curl checkip.amazonaws.com)"
- run: name: VPN Setup background: true command: | echo $VPN_CLIENT_CONFIG | base64 --decode > /tmp/config.ovpn
#IMPORTANT: Include the following 3 lines to exclude the link-local range
phone_home="$(netstat -an | grep ':22 .*ESTABLISHED' | head -n1 | awk '{ split($5, a, ":"); print a[1] }')"
echo $phone_home echo -e "\nroute $phone_home 255.255.255.255 net_gateway" >> /tmp/config.ovpn
echo "route 169.254.0.0 255.255.0.0 net_gateway" >> /tmp/config.ovpn

#This will start the connection
sudo openvpn3 session-start --config /tmp/config.ovpn > /tmp/openvpn.log
- run: name: Wait for the connection to be established and check
command: |
while [ $(sudo openvpn3 sessions-list|grep -c "Client connected") == 0 ]; do
echo "Attempting to connect..."
sleep 1;
done
echo "VPN Connected"

sudo openvpn3 sessions-list
echo "Public IP is now $(curl checkip.amazonaws.com)"
- run: name: Run commands in our infrastructure command: | # A command # Another command

  - run:
name: Disconnect from OpenVPN
command: |
SESSION_PATH=$(sudo openvpn3 sessions-list | grep Path | awk -F': ' '{print $2}')
echo $SESSION_PATH
sudo openvpn3 session-manage --session-path $SESSION_PATH --disconnect
when: always

 

L2TP

To set up an L2TP VPN connection, we recommend referring to this guide.

We suggest storing VPN_SERVER_IPVPN_IPSEC_PSKVPN_USER and VPN_PASSWORD as environment variables. Ideally, you might want to base64-encode VPN_IPSEC_PSK before storing it; you'll need to decode it during the build.

To retrieve the default gateway IP address during the build, you can use either of the following:

  • default_gw_IP=$(netstat -r | grep default | awk '{ print $2 }')
  • default_gw_IP=$(ip route | grep default | awk '{ print $3 }')

 

macos executor (Supported Xcode versions)

  • Base64-encode the OpenVPN client configuration file, and store it as an environment variable.
  • If the VPN client authentication is credentials-based (user-locked profile), you'll also need to add the username and password as environment variables (VPN_USER and VPN_PASSWORD).
version: 2.1
workflows:
  btd:
    jobs:
      - build
jobs:
  build:
    macos:
xcode: "12.2.0" steps: - run: name: Install OpenVPN command: | brew install openvpn
- run: name: Check IP before VPN connection command: | ifconfig echo "Public IP before VPN connection is $(curl checkip.amazonaws.com)"
- run: name: VPN Setup command: | echo $VPN_CONFIG | base64 --decode | tee /tmp/config.ovpn 1>/dev/null

if grep auth-user-pass /tmp/config.ovpn; then
if [ -z "${VPN_USER:-}" ] || [ -z "${VPN_PASSWORD:-}" ]; then
echo "Your VPN client is configured with a user-locked profile. Make sure to set the VPN_USER and VPN_PASSWORD environment variables"
exit 1
else
printf "$VPN_USER\\n$VPN_PASSWORD" > /tmp/vpn.login
sed -i config.bak 's|^auth-user-pass.*|auth-user-pass /tmp/vpn\.login|' /tmp/config.ovpn
fi
fi

touch /tmp/openvpn.log

#IMPORTANT: Include the following 3 lines to exclude the link-local range
phone_home="$(netstat -an | grep '\.2222\s.*ESTABLISHED' | head -n1 | awk '{ split($5, a, "."); print a[1] "." a[2] "." a[3] "." a[4] }')"
echo $phone_home echo -e "\nroute $phone_home 255.255.255.255 net_gateway" | tee -a /tmp/config.ovpn
echo "route 169.254.0.0 255.255.0.0 net_gateway" | tee -a /tmp/config.ovpn

cat \<< EOF | sudo tee /Library/LaunchDaemons/org.openvpn.plist 1>/dev/null
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.openvpn</string>
<key>Program</key>
<string>/usr/local/sbin/openvpn</string>
<key>ProgramArguments</key>
<array>
<string>--config</string>
<string>/tmp/config.ovpn</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>TimeOut</key>
<integer>90</integer>
<key>StandardErrorPath</key>
<string>/tmp/openvpn.log</string>
<key>StandardOutPath</key>
<string>/tmp/openvpn.log</string>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
EOF
ifconfig
echo "Public IP before VPN connection is > $(curl http://checkip.amazonaws.com)"

#This will start the connection
sudo launchctl load /Library/LaunchDaemons/org.openvpn.plist
sudo launchctl start org.openvpn
- run: name: Wait for the connection to be established and check
command: |
while [ $(cat /tmp/openvpn.log|grep -c "Initialization Sequence Completed") == 0 ]; do
echo "Attempting to connect..."
sleep 1;
done
echo "VPN Connected"

sudo launchctl list | grep openvpn
echo "Public IP is now $(curl checkip.amazonaws.com)"
- run: name: Run commands in our infrastructure command: | # A command # Another command

  - run:
name: Disconnect from OpenVPN
command: sudo launchctl stop org.openvpn
when: always

 

windows executor (Windows executor images)

  • Base64-encode the OpenVPN client configuration file, and store it as an environment variable.
  • If the VPN client authentication is credentials-based (user-locked profile), you'll also need to add the username and password as environment variables (VPN_USER and VPN_PASSWORD).
version: 2.1

orbs:
  win: circleci/windows@2.2.0
  
workflows:
  btd:
    jobs:
      - build
jobs:
  build:
    executor:
      name: win/default
      shell: bash.exe
      
    steps:
      - run:
          name: Install OpenVPN
          shell: powershell.exe
          command: |
            Invoke-WebRequest -Uri "https://swupdate.openvpn.org/community/releases/OpenVPN-2.5.2-I601-amd64.msi" -OutFile "OpenVPN-2.5.2-I601-amd64.msi"
            msiexec /i OpenVPN-2.5.2-I601-amd64.msi /qn
            
      - run:
          name: Check IP before VPN connection
          command: echo "Public IP before VPN connection is $(curl checkip.amazonaws.com)"
          
      - run:
          name: VPN Setup
          command: |
echo $VPN_CONFIG | base64 --decode > /C/PROGRA~1/OpenVPN/config/config.ovpn if grep auth-user-pass "/C/PROGRA~1/OpenVPN/config/config.ovpn"; then
if [ -z "${VPN_USER:-}" ] || [ -z "${VPN_PASSWORD:-}" ]; then
echo "Your VPN client is configured with a user-locked profile. Make sure to set the VPN_USER and VPN_PASSWORD environment variables"
exit 1
else
printf "$VPN_USER\\n$VPN_PASSWORD" > /C/PROGRA~1/OpenVPN/config/vpn.login
sed -i 's|^auth-user-pass.*|auth-user-pass vpn\.login|' /C/PROGRA~1/OpenVPN/config/config.ovpn
fi
fi phone_home=$(netstat -an | grep ':22 .*ESTABLISHED' | head -n1 | awk '{ split($3, a, ":"); print a[1] }') echo -e "\nroute $phone_home 255.255.255.255 net_gateway" | tee -a "/C/PROGRA~1/OpenVPN/config/config.ovpn"
echo "route 169.254.0.0 255.255.0.0 net_gateway" | tee -a "/C/PROGRA~1/OpenVPN/config/config.ovpn"

#Create and start the OpenVPN service
sc.exe create "OpenVPN" binPath= "C:\PROGRA~1\OpenVPN\bin\openvpnserv.exe"
net start "OpenVPN" - run: name: Wait for the connection to be established and check command: | while [ $(cat /C/PROGRA~1/OpenVPN/log/config.log|grep -c "Initialization Sequence Completed") == 0 ]; do echo "Attempting to connect..." sleep 1; done echo "VPN Connected" echo "Public IP is now $(curl checkip.amazonaws.com)" - run: name: Run commands in our infrastructure command: | # A command # Another command - run: name: Disconnect from OpenVPN command: net stop "OpenVPN" when: always
Was this article helpful?
10 out of 12 found this helpful

Comments

0 comments

Article is closed for comments.