Oracle Cloud Infrastructure (OCI) does not support the use of Domain Name System (DNS) for configuring the Customer Premises Equipment (CPE) as the VPN tunnel endpoint. This tutorial aims to enable small businesses that do not have access to a static public IP address to use the IPSec service even with a dynamic address. The strategy involves deploying a script capable of automatically generating a new OCI tunnel whenever the public IP of the Customer Premises Equipment (CPE) changes.
Objective:
Create a new Oracle Cloud Infrastructure Site-to-Site VPN tunnel with the new updated CPE IP address.
Prerequisites:
- A dynamically update DNS record that point to your public CPE IP ADDRESS
- A Linux virtual machine (VM).
- OCI CLI installed and configured
- Generate an API key (link)
- Install
'sshpass'
into the Linux VM. In this tutorial, this will be used to send commands to the MikroTik router.
1 – To achieve a stable VPN IPSEC connection with Oracle OCI that survive to a CPE IP ADDRESS update you have to setup a dynamically update DNS name. There are several public DNS providers that support this feature. Choose your favorite and configure it (ex: https://www.cloudns.net/)
After you have registered and configured a public dynamic DNS record, you can set up your linux VM.
2 – Once the VM is up and running, install and configure the OCI CLI to operate on your OCI tenancy from this machine.
3 – Install the Sshpass application in case you need to use it for update your private router settings (sudo apt-get install Sshpass).
4 – Create the .sh
file (ex: nano VPN_update.sh) and copy the following script in the file and make it executable. Remember to modify the script with your data, the compartment ID, DRG ID and DNS name that points to your CPE. (You can find the oci path executing this command into your terminal: ‘which oci’).
#!/bin/bash
#OCI variables
export compartment_id=ocid1.compartment.oc1… #your OCI compartment ID
export drg_id=ocid1.drg.oc1.eu-frankfurt-1… #your DRG ID
export DNS_cpe=YOUR_DYNAMIC_DNS_RECORD_POINT_TO_YOUR_CPE #(example: your-domain.com)
#Mikrotik variables
export static_routes=192.168.1.0/24 #(YOUR PRIVATE SUBNET)
export router_ip=192.168.1.1 #(YOUR ROUTER PRIVATE IP ADDRESS)
export router_user=USER #(YOUR ROUTER USER)
export router_password=PWD #(YOUR ROUTER PASSWORD)
#oci command path (to find the path of oci command type 'which oci' into the terminal)
export oci_path=PATH #(YOUR oci COMMAND PATH. example: /home/ubuntu/bin/oci)
#
#
#
#
#
export ip_address=$(ping -c 1 $DNS_cpe | gawk -F'[()]' '/PING/{print $2}')
check_out=$($oci_path network ip-sec-connection list --compartment-id $compartment_id --all --query "data[?\"cpe-local-identifier\"=='$ip_address']" | sed 's|[[]]||g')
if [[ -n $check_out ]]
then
printf -- "%s\n" "IP of the CPE is not changed - EXIT"
exit
else
printf -- "%s\n" "create new IPSEC tunnel"
cpe_id=$($oci_path network cpe create --compartment-id $compartment_id --ip-address $ip_address --query data.id --raw-output)
$oci_path network cpe update --cpe-id $cpe_id
ipsc_id=$($oci_path network ip-sec-connection create --compartment-id $compartment_id --cpe-id $cpe_id --drg-id $drg_id --static-routes '["$static_routes"]' --query data.id --raw-output)
$oci_path network ip-sec-connection update --ipsc-id $ipsc_id
tunnel_id1=$($oci_path network ip-sec-tunnel list --ipsc-id=$ipsc_id --all --query 'data[0].id' --raw-output)
tunnel_id2=$($oci_path network ip-sec-tunnel list --ipsc-id=$ipsc_id --all --query 'data[1].id' --raw-output)
ip_sec_psk1=$($oci_path network ip-sec-psk get --ipsc-id=$ipsc_id --tunnel-id=$tunnel_id1 | grep -oP '(?<="shared-secret": ").*(?=")')
ip_sec_psk2=$($oci_path network ip-sec-psk get --ipsc-id=$ipsc_id --tunnel-id=$tunnel_id2 | grep -oP '(?<="shared-secret": ").*(?=")')
vpn_ip1=$($oci_path network ip-sec-tunnel list --ipsc-id=$ipsc_id --all --query 'data[0]."vpn-ip"' --raw-output)
vpn_ip2=$($oci_path network ip-sec-tunnel list --ipsc-id=$ipsc_id --all --query 'data[1]."vpn-ip"' --raw-output)
echo OCI TUNNEL 1 IP and SHARED SECRET
echo $vpn_ip1
echo $ip_sec_psk1
echo OCI TUNNEL 2 IP and SHARED SECRET
echo $vpn_ip2
echo $ip_sec_psk2
#Mikrotik router update section. (Execute the sshpass command before run this script). Avoid and delete these two rows if you don't need it.
sshpass -p '$router_password' ssh $router_user@$router_ip "/ip ipsec/ identity/ set number=0 secret=$ip_sec_psk1"
sshpass -p '$router_password' ssh $router_user@$router_ip "/ip ipsec/ peer/ set number=0 address=$vpn_ip1"
Make it executable:
chmod +x VPN_update.sh
The last part of the script (The Mikrotik router section) is intended just if in you have a Mikrotik router. Otherwise you have to implement the commands to update the ORACLE OCI tunnel endpoint IP ADDRESS and the shared secret into your router.
The script can create the tunnels but cannot terminate the old ones. Sometimes you have to log in into the OCI web console and terminate the unused old VPN tunnels or implement this function into it.
5 – Add the script to the crontab
if you want automatically execute it.
Crontab -e
Add the frequency and the script path into the last line of the crontab. The following command is intended to be executed every 15 minutes.
*/15 * * * * YOUR_SCRIPT_PATH/VPN_update.sh
Done! Next time your CPE IP address changes, the OCI tunnel will be recreated with the new IP address and shared secret within a maximum of 15 minutes.
Published on Oracle Help Center
Related Links
Hi,
great approach. Sadly, I just get a “syntax error: unexpected end of file” when I run the script after making all the apropiate changes 🙁
Hi, try to comment the script commands per sections to exclude and identify which part generate the error.
You can use as well the command ‘echo’ to check the progress of the script until you find the command/part that does not works.
Or maybe you forget to close a quote or something similar. Check this link:
https://linuxconfig.org/bash-script-unexpected-end-of-file-error#:~:text=An%20Unexpected%20end%20of%20file,to%20execute%20your%20Bash%20script.
Good luck!
Hi,
thank you. This was however not the issue.
At the end of the script there is a “fi” missing to close the if clause. With an added if the error disappeared.
I also found another issue, but this is because of my ISP. The line “export ip_address=$(ping -c 1 $DNS_cpe | gawk -F'[()]’ ‘/PING/{print $2}’)” fills the variable $ip_address with an fqdn, not the IP.
I changed it to use nslookup, and now it correctly fills the IP (and IPv4 only, as otherwise I would have both v4 and v6 in the variable, breaking the script).
Changed line: export ip_address=$(nslookup $DNS_cpe | awk ‘/^Address: / && $2 !~ /:/{ print $2 }’)
Now it at least correctly recognizes that the IP did not change. Looking forward to the next change… 😀
Thanks for the idea in any case, really helpful!
Thank you very much for your feedback!
Have a nice day 🙂