Table of Contents
Connecting workloads across clouds has become a common requirement for teams that rely on hybrid environments. Organizations migrating to Civo Cloud—or expanding into it—often need a stable way to reach private AWS resources without exposing anything publicly. But this is where challenges appear quickly.
Most teams discover that cross-cloud connectivity is often expensive, complicated, and filled with vendor-specific requirements. AWS Direct Connect, proprietary firewalls, or managed VPNs introduce cost or administrative weight that small teams don’t always want to deal with.
Fortunately, there's a practical answer: a StrongSwan-powered site-to-site VPN running on a Civo VM that connects directly to an AWS VPC. This setup creates a secure tunnel where both cloud networks can communicate as if they belonged to the same private environment.
This guide walks through the entire journey—from planning the IP space and building the AWS side, to deploying StrongSwan on Civo, establishing the IPsec tunnel, and extending connectivity to additional VMs. The goal is to offer a full, ground-up walkthrough you can follow in real projects.
Let’s begin.
1. Why Cross-Cloud Private Connectivity Matters
Many organizations reach a point where hosting everything in one cloud is no longer viable. New products might be easier to run in a different provider, or teams may want to migrate workloads gradually while keeping backend systems in AWS.
In most of these scenarios, engineers face a similar challenge:
“How do we allow Civo workloads to reach AWS private resources without placing those resources on the public internet?”
A public endpoint is often unacceptable for:
- Internal APIs
- Databases
- Legacy servers
- Sensitive workloads
A site-to-site VPN solves this by linking two private networks so they act as one.
AWS supports IPsec-based VPNs through its Virtual Private Gateway (VGW). Civo Cloud, meanwhile, gives engineers complete control of their network environment, making StrongSwan a practical, cost-friendly solution.
All you need is:
- A Civo VM
- StrongSwan
- A properly planned set of CIDRs
- A few AWS VPN components
The result is a secure IPsec tunnel that routes private traffic between the two clouds.
2. Planning the Network Layout
A stable VPN configuration begins with clean network planning. The most important rule is simple:
Your AWS VPC CIDR and Civo network CIDR must not overlap.
Here’s a reference layout used throughout this guide:
Component | Example IP / CIDR | Notes |
|---|---|---|
AWS VPC | 10.10.0.0/16 | Main private network |
AWS Resource Subnet | 10.10.1.0/24 | EC2 and application resources |
AWS Instance IP | 10.10.1.10 | Example private VM |
AWS Database Subnet | 10.10.2.0/24 | Private DB subnet |
AWS Database IP | 10.10.2.106 | Example private DB |
Civo Network | 192.168.50.0/24 | Civo private network |
Civo StrongSwan VM (private) | 192.168.50.5 | Gateway VM local address |
Civo StrongSwan VM (public) | X.Y.Z.W | VM public address |
These CIDRs must stay consistent throughout AWS VPN creation, StrongSwan configuration, and static routing.
3. Preparing the AWS Side
This entire setup depends on defining the AWS components correctly. The following AWS pieces are required:
- A VPC
- Subnets
- A Virtual Private Gateway (VGW)
- A Customer Gateway (pointing to StrongSwan’s public IP)
- A Site-to-Site VPN connection
- A static route directing Civo traffic through the VPN
Step 3.1. Create the AWS VPC
In the AWS console:
VPC → Create VPC
- Name:
mumbai-main-vpc - IPv4 CIDR:
10.10.0.0/16 - Tenancy: Default
Step 3.2. Create Subnets
You’ll need at least two:
Subnet | CIDR | Purpose |
|---|---|---|
| Resource subnet |
|
| Private database subnet |
|
Note: The DB subnet must not require internet access.
Step 3.3. (Optional) Internet Gateway
Only needed if some AWS resources need outbound internet—not required for this VPN setup.
Step 3.4. Launch AWS Private Resources
Examples:
Resource Instance
- Subnet: 10.10.1.0/24
- IP: 10.10.1.10
- Security Group Rules: allow only
192.168.50.0/24
Private Database
- Subnet: 10.10.2.0/24
- IP: 10.10.2.106
- Security Group Rules: allow DB port (usually 3306, 5432, etc.) from
192.168.50.0/24
This protects the database from the public internet and allows access only through the VPN.
4. Creating AWS VPN Components
This is where AWS must be told about the Civo endpoint.
Step 4.1. Create a Customer Gateway (CGW)
Console → VPC → Customer Gateways → Create
- Name:
civo-strongswan-cgw - IP Address: public IP of your Civo VM
- Routing: Static
- BGP ASN: Any value
Step 4.2. Create a Virtual Private Gateway (VGW)
Console → VPC → Virtual Private Gateways → Create
- Name:
mumbai-vgw
Attach it to the VPC.
Step 4.3. Create the Site-to-Site VPN
Console → VPC → Site-to-Site VPN Connections → Create
- Name:
aws-civo-vpn - Target:
mumbai-vgw - Customer Gateway:
civo-strongswan-cgw - Routing: Static
- Static Route:
192.168.50.0/24(Civo CIDR)
Wait for it to enter the available state.
Step 4.4. Download the VPN Configuration File
Choose:
- Vendor: strongSwan
- Platform: Ubuntu
- IKE version: IKEv2
This file contains:
- AWS public tunnel endpoints
- Pre-shared keys
- IKE/ESP parameters
- Tunnel inside CIDRs (
169.254.x.x/30)
You’ll use these values in the StrongSwan script later.
5. Deploying StrongSwan on Civo
A single Civo VM will serve as the VPN gateway. StrongSwan supports route-based tunnels using VTI interfaces, which is compatible with AWS VGW requirements.
The script you provided is a complete, production-ready deployment tool. It:
- Installs StrongSwan
- Enables IP forwarding
- Configures Tunnel 1 and Tunnel 2
- Creates VTI interfaces
- Applies static routes
- Adds firewall rules
- Starts tunnels
- Verifies routes
- Outputs testing commands
The entire script is preserved in your original post, so instead of repeating it here, this guide focuses on how it works and what to expect after running it.
Key Variables You Must Update
Inside the script, update:
PUBLIC_IPLOCAL_SUBNETAWS_CIDRAWS_T1_PUBLIC,AWS_T2_PUBLICAWS_T1_PSK,AWS_T2_PSKT1_LOCAL_IP,T1_REMOTE_IP,T2_LOCAL_IP,T2_REMOTE_IP
These values all come directly from the AWS VPN configuration file.
Setup-vpn.sh
#!/bin/bash
##############################################
# AWS VPN TUNNEL SETUP SCRIPT (Route-Based)
#
# This script sets up a route-based IPsec VPN tunnel between
# a Civo VM and AWS VPC using strongSwan.
#
# TO USE ON A NEW VM:
# 1. Update the variables below with your values
# 2. Run: sudo bash script.sh
# 3. The script is idempotent - safe to run multiple times
#
##############################################
##############################################
# CHANGE ONLY THESE VALUES FOR NEW VM
##############################################
PUBLIC_IP="212.2.249.102" # Your Civo VM PUBLIC IP (must match AWS Customer Gateway) `leftid`
LOCAL_SUBNET="192.168.50.0/24" # Your Civo network CIDR `leftsubnet`
AWS_CIDR="10.10.0.0/16" # AWS VPC CIDR `rightsubnet`
# AWS VPN Tunnel Endpoints (from AWS VPN config file)
AWS_T1_PUBLIC="13.235.15.233" # AWS Tunnel 1 Public IP
AWS_T2_PUBLIC="65.0.248.54" # AWS Tunnel 2 Public IP
# Pre-Shared Keys (from AWS VPN config file)
AWS_T1_PSK="xxxxx"
AWS_T2_PSK="xxxxx"
# Route-based VPN tunnel IPs (from AWS VPN config file)
# These are the 169.254.x.x addresses for the tunnel interfaces
T1_LOCAL_IP="169.254.26.210/30" # Tunnel1 local IP
T1_REMOTE_IP="169.254.26.209/30" # Tunnel1 remote IP
T2_LOCAL_IP="169.254.19.106/30" # Tunnel2 local IP
T2_REMOTE_IP="169.254.19.105/30" # Tunnel2 remote IP
# Get physical interface name (usually eth0 or ens5)
PHYS_INTERFACE=$(ip route | grep default | awk '{print $5}' | head -1)
if [ -z "$PHYS_INTERFACE" ]; then
PHYS_INTERFACE="eth0" # fallback
fi
##############################################
# INSTALL STRONGSWAN
##############################################
echo " Installing strongSwan..."
sudo apt update && sudo apt install -y strongswan netfilter-persistent
##############################################
# ENABLE IP FORWARDING
##############################################
echo "Enabling IP forwarding..."
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-ipforward.conf
echo "net.ipv4.conf.all.accept_redirects=0" | sudo tee -a /etc/sysctl.d/99-ipforward.conf
echo "net.ipv4.conf.all.send_redirects=0" | sudo tee -a /etc/sysctl.d/99-ipforward.conf
sudo sysctl -p
##############################################
# CREATE /etc/ipsec.conf (ROUTE-BASED VPN)
##############################################
echo " Writing /etc/ipsec.conf..."
sudo tee /etc/ipsec.conf > /dev/null << EOF
config setup
charondebug="all"
uniqueids=yes
strictcrlpolicy=no
conn Tunnel1
type=tunnel
auto=add
keyexchange=ikev2
authby=psk
left=%any
leftid=$PUBLIC_IP
leftsubnet=$LOCAL_SUBNET
right=$AWS_T1_PUBLIC
rightsubnet=$AWS_CIDR
aggressive=no
ikelifetime=28800s
lifetime=3600s
margintime=270s
rekey=yes
rekeyfuzz=100%
fragmentation=yes
replay_window=1024
dpddelay=30s
dpdtimeout=120s
dpdaction=restart
ike=aes128-sha1-modp1024
esp=aes128-sha1-modp1024
keyingtries=%forever
mark=100
leftupdown="/etc/ipsec.d/aws-updown.sh -ln Tunnel1 -ll $T1_LOCAL_IP -lr ${T1_REMOTE_IP%/*}/30 -m 100 -r $AWS_CIDR"
conn Tunnel2
type=tunnel
auto=add
keyexchange=ikev2
authby=psk
left=%any
leftid=$PUBLIC_IP
leftsubnet=$LOCAL_SUBNET
right=$AWS_T2_PUBLIC
rightsubnet=$AWS_CIDR
aggressive=no
ikelifetime=28800s
lifetime=3600s
margintime=270s
rekey=yes
rekeyfuzz=100%
fragmentation=yes
replay_window=1024
dpddelay=30s
dpdtimeout=120s
dpdaction=restart
ike=aes128-sha1-modp1024
esp=aes128-sha1-modp1024
keyingtries=%forever
mark=200
leftupdown="/etc/ipsec.d/aws-updown.sh -ln Tunnel2 -ll $T2_LOCAL_IP -lr ${T2_REMOTE_IP%/*}/30 -m 200 -r $AWS_CIDR"
EOF
##############################################
# CREATE PSK FILE
##############################################
echo " Writing /etc/ipsec.secrets..."
sudo tee /etc/ipsec.secrets > /dev/null << EOF
$PUBLIC_IP $AWS_T1_PUBLIC : PSK "$AWS_T1_PSK"
$PUBLIC_IP $AWS_T2_PUBLIC : PSK "$AWS_T2_PSK"
EOF
##############################################
# 4.CREATE AWS UPDOWN SCRIPT
##############################################
echo " Creating AWS updown script..."
sudo mkdir -p /etc/ipsec.d
sudo tee /etc/ipsec.d/aws-updown.sh > /dev/null << 'UPSCRIPT'
#!/bin/bash
while [[ $# > 1 ]]; do
case ${1} in
-ln|--link-name)
TUNNEL_NAME="${2}"
TUNNEL_PHY_INTERFACE="${PLUTO_INTERFACE}"
shift
;;
-ll|--link-local)
TUNNEL_LOCAL_ADDRESS="${2}"
TUNNEL_LOCAL_ENDPOINT="${PLUTO_ME}"
shift
;;
-lr|--link-remote)
TUNNEL_REMOTE_ADDRESS="${2}"
TUNNEL_REMOTE_ENDPOINT="${PLUTO_PEER}"
shift
;;
-m|--mark)
TUNNEL_MARK="${2}"
shift
;;
-r|--static-route)
TUNNEL_STATIC_ROUTE="${2}"
shift
;;
*)
echo "${0}: Unknown argument \"${1}\"" >&2
;;
esac
shift
done
command_exists() {
type "$1" >&2 2>&2
}
create_interface() {
ip link add ${TUNNEL_NAME} type vti local ${TUNNEL_LOCAL_ENDPOINT} remote ${TUNNEL_REMOTE_ENDPOINT} key ${TUNNEL_MARK} 2>/dev/null || true
ip addr add ${TUNNEL_LOCAL_ADDRESS} dev ${TUNNEL_NAME} 2>/dev/null || true
ip link set ${TUNNEL_NAME} up mtu 1419
}
configure_sysctl() {
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.${TUNNEL_NAME}.rp_filter=2
sysctl -w net.ipv4.conf.${TUNNEL_NAME}.disable_policy=1
sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_xfrm=1
sysctl -w net.ipv4.conf.${TUNNEL_PHY_INTERFACE}.disable_policy=1
}
add_route() {
# Get local IP from the physical interface
PHYS_IF=$(ip route | grep default | awk '{print $5}' | head -1)
LOCAL_IP=$(ip addr show ${PHYS_IF} 2>/dev/null | grep "inet " | awk '{print $2}' | cut -d/ -f1)
if [ -z "$LOCAL_IP" ]; then
LOCAL_IP="${TUNNEL_LOCAL_ENDPOINT}"
fi
IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
for i in "${route[@]}"; do
# Remove existing route first, then add with source IP
ip route del ${i} dev ${TUNNEL_NAME} 2>/dev/null || true
ip route add ${i} dev ${TUNNEL_NAME} src ${LOCAL_IP} metric ${TUNNEL_MARK} 2>/dev/null || true
done
iptables -t mangle -C FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null || \
iptables -t mangle -A FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
iptables -t mangle -C INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || \
iptables -t mangle -A INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK}
# Mark outgoing traffic based on route
iptables -t mangle -C OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || \
iptables -t mangle -A OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK}
ip route flush table 220 2>/dev/null || true
}
cleanup() {
IFS=',' read -ra route <<< "${TUNNEL_STATIC_ROUTE}"
for i in "${route[@]}"; do
ip route del ${i} dev ${TUNNEL_NAME} metric ${TUNNEL_MARK} 2>/dev/null || true
done
iptables -t mangle -D FORWARD -o ${TUNNEL_NAME} -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 2>/dev/null || true
iptables -t mangle -D INPUT -p esp -s ${TUNNEL_REMOTE_ENDPOINT} -d ${TUNNEL_LOCAL_ENDPOINT} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || true
iptables -t mangle -D OUTPUT -o ${TUNNEL_NAME} -j MARK --set-xmark ${TUNNEL_MARK} 2>/dev/null || true
ip route flush cache 2>/dev/null || true
}
delete_interface() {
ip link set ${TUNNEL_NAME} down 2>/dev/null || true
ip link del ${TUNNEL_NAME} 2>/dev/null || true
}
case "${PLUTO_VERB}" in
up-client)
create_interface
configure_sysctl
add_route
;;
down-client)
cleanup
delete_interface
;;
esac
UPSCRIPT
sudo chmod 744 /etc/ipsec.d/aws-updown.sh
##############################################
# 4. CONFIGURE CHARON (DISABLE AUTO ROUTES)
##############################################
echo " Configuring charon.conf..."
if [ -f /etc/strongswan.d/charon.conf ]; then
sudo sed -i 's/# install_routes = yes/install_routes = no/' /etc/strongswan.d/charon.conf
if ! grep -q "install_routes = no" /etc/strongswan.d/charon.conf; then
echo "install_routes = no" | sudo tee -a /etc/strongswan.d/charon.conf
fi
fi
# Note: VTI interfaces will be created by the updown script when tunnels come up
##############################################
# FLUSH OLD STATE & RESTART VPN
##############################################
echo "♻ Restarting VPN..."
# Clean up any existing routes to AWS CIDR
sudo ip route del $AWS_CIDR via 192.168.50.5 2>/dev/null || true
sudo ip route del $AWS_CIDR dev enp1s0 2>/dev/null || true
sudo ip route del $AWS_CIDR dev $PHYS_INTERFACE 2>/dev/null || true
sudo ip route del $AWS_CIDR dev Tunnel1 2>/dev/null || true
sudo ip route del $AWS_CIDR dev Tunnel2 2>/dev/null || true
sudo ip xfrm state flush
sudo ip xfrm policy flush
sudo ipsec restart
sleep 5
# Start the tunnels
echo " Starting tunnels..."
sudo ipsec up Tunnel1
sudo ipsec up Tunnel2
sleep 3
# Routes are added by the updown script, but verify they have correct source IP
echo " Verifying routes..."
LOCAL_IP=$(ip addr show $PHYS_INTERFACE | grep "inet " | awk '{print $2}' | cut -d/ -f1)
if [ -n "$LOCAL_IP" ]; then
# Ensure routes have correct source IP (updown script should have done this, but double-check)
sudo ip route del $AWS_CIDR dev Tunnel1 2>/dev/null || true
sudo ip route del $AWS_CIDR dev Tunnel2 2>/dev/null || true
sudo ip route add $AWS_CIDR dev Tunnel1 src $LOCAL_IP metric 100 2>/dev/null || true
sudo ip route add $AWS_CIDR dev Tunnel2 src $LOCAL_IP metric 200 2>/dev/null || true
fi
echo " Tunnel status:"
sudo ipsec statusall
##############################################
# FORWARD TRAFFIC (REQUIRED FOR VM2,3,4...)
##############################################
echo "🔧 Allowing forwarded traffic..."
sudo iptables -C FORWARD -s $LOCAL_SUBNET -d $AWS_CIDR -j ACCEPT 2>/dev/null || \
sudo iptables -A FORWARD -s $LOCAL_SUBNET -d $AWS_CIDR -j ACCEPT
sudo iptables -C FORWARD -s $AWS_CIDR -d $LOCAL_SUBNET -j ACCEPT 2>/dev/null || \
sudo iptables -A FORWARD -s $AWS_CIDR -d $LOCAL_SUBNET -j ACCEPT
sudo netfilter-persistent save
##############################################
# TESTING INSTRUCTIONS FOR YOU
##############################################
echo ""
echo " TESTING COMMANDS:"
echo "-------------------------------------------"
echo "sudo ipsec statusall # Check tunnel status"
echo "ip route show # Check routes"
echo "ip addr show Tunnel1 # Check Tunnel1 interface"
echo "ip addr show Tunnel2 # Check Tunnel2 interface"
echo "sudo tcpdump -n -i any proto esp # Monitor ESP traffic"
echo "ping -c 4 10.10.2.106 # Test ping to AWS"
echo "-------------------------------------------"
echo "If you have another VM (192.168.50.10+):"
echo "sudo ip route add 10.10.0.0/16 via <STRONGSWAN_LOCAL_IP>"
echo "-------------------------------------------"
echo " If tcpdump shows ESP packets — TUNNEL IS WORKING!"
echo ""Running the Script
SSH into your Civo VM, then run:
sudo bash Setup-vpn.shThe script will:
- Install packages
- Create
/etc/ipsec.conf - Create
/etc/ipsec.secrets - Create
/etc/ipsec.d/aws-updown.sh - Restart StrongSwan
- Bring up Tunnel1 and Tunnel2
- Add routes for AWS CIDR
You should see output indicating both tunnels come up successfully.
6. Understanding What the Script Creates
Let's break down the major components the script configures.
6.1. IPsec Configuration
Two tunnels:
- Tunnel1 (mark 100)
- Tunnel2 (mark 200)
AWS supports dual-tunnel redundancy, so both should be active.
6.2. VTI Interfaces
The updown script creates:
Tunnel1Tunnel2
Each interface:
- Gets a local VTI IP (
169.254.x.x) - Has a remote endpoint
- Receives a metric for route priority
6.3. Static Routes
Routes for the full AWS VPC:
10.10.0.0/16 dev Tunnel1 metric 100
10.10.0.0/16 dev Tunnel2 metric 200
This gives failover capability.
6.4. Firewall Updates
The script adds:
- TCP MSS clamping
- MARK rules
- Forwarding rules for both directions
This prevents MTU issues and supports cross-cloud traffic forwarding.
7. Testing the VPN
Once the StrongSwan VM is configured, it’s time to verify connectivity.
Check Tunnel Status
sudo ipsec statusall
Both tunnels should show “INSTALLED”.
Check VTI Interfaces
ip addr show Tunnel1
ip addr show Tunnel2
You should see the 169.254.x.x addresses.
Check Routing Table
ip route showLook for:
10.10.0.0/16 dev Tunnel1
10.10.0.0/16 dev Tunnel2
Check for ESP Traffic
sudo tcpdump -n -i any proto espIf ESP packets appear while pinging AWS, the tunnel is functioning.
Ping an AWS Resource
ping -c 4 10.10.1.10A working ping indicates:
- The tunnel is active
- AWS security groups allow traffic
- NAT is not interfering
- Route tables are correct
8. Extending Connectivity to Other Civo VMs
Once the VPN gateway VM (StrongSwan VM) works, you can allow any VM in the Civo network to reach AWS private resources.
Each new VM only needs one route:
Step 8.1. Find the StrongSwan VM’s local IP
ip addr show | grep "192.168.50"
Step 8.2. Add a Route on the Other VM
sudo ip route add 10.10.0.0/16 via 192.168.50.5Replace 192.168.50.5 with your actual StrongSwan VM IP.
Step 8.3. Test It
ping -c 4 10.10.1.10
If ping works, the routing path is correct.
9. Making Static Routes Persistent
Routes added manually disappear after reboot. To make them persistent, use one of the options below.
Option A: Netplan (Ubuntu 18.04+)
Edit:
sudo nano /etc/netplan/50-cloud-init.yamlAdd under your network interface:
network:
version: 2
ethernets:
enp1s0:
dhcp4: true
routes:
- to: 10.10.0.0/16
via: 192.168.50.5
Apply:
sudo netplan apply
Option B: Older Ubuntu Versions
Edit:
sudo nano /etc/network/interfaces
Add:
up ip route add 10.10.0.0/16 via 192.168.50.5
Now AWS routing persists across reboots.
10. How Traffic Moves Across the VPN
Here’s the flow for a VM in the Civo network:
Civo VM (192.168.50.10)
↓
Route: 10.10.0.0/16 via 192.168.50.5
↓
StrongSwan VM (VPN Gateway)
↓
VTI Tunnel
↓
AWS Virtual Private Gateway
↓
AWS VPC Resource (10.10.x.x)
This architecture makes both clouds feel linked through a private connection.
Conclusion
Cross-cloud networking is often known for complexity, high cost, or restricted vendor options. But it doesn’t have to be that way. With a small Civo VM, StrongSwan, and a well-planned AWS VPC, you can build a powerful, private, cloud-to-cloud link without unnecessary overhead.
This tunnel gives you:
- Private access to databases
- Secure communication between backend services
- A safe migration path from AWS to Civo
- A hybrid cloud architecture that avoids public exposure
This setup is suitable for production workloads, internal applications, or teams running multi-cloud deployments for redundancy or performance reasons.
If you're looking for a straightforward method to connect AWS and Civo in a way that keeps traffic private and under your control, this StrongSwan VPN architecture is a strong option. With proper planning and the script provided earlier, you can have both clouds communicating through a secure tunnel in minutes.