OCI Site-to-Site VPN: IPSec Tunnels, BGP Routing, and Redundant Connectivity

OCI Site-to-Site VPN provides encrypted IPSec connectivity between your on-premises network and OCI VCNs over the public internet. Each VPN connection consists of two tunnels for redundancy, and supports both static routing and BGP dynamic routing. BGP is the recommended approach for production environments because it provides automatic failover between tunnels and supports route propagation from on-premises to OCI without manual route table maintenance. This post covers the full setup with Terraform.

Step 1: Customer Premises Equipment and IPSec Connection

resource "oci_core_drg" "production_drg" {
  compartment_id = var.compartment_id
  display_name   = "production-drg"
}

resource "oci_core_drg_attachment" "vcn_to_drg" {
  drg_id   = oci_core_drg.production_drg.id
  vcn_id   = var.vcn_id
  display_name = "production-vcn-to-drg"
}

resource "oci_core_cpe" "on_premises_router" {
  compartment_id = var.compartment_id
  display_name   = "on-premises-firewall"
  ip_address     = var.on_premises_public_ip

  # Use a CPE device shape that matches your hardware for configuration templates
  cpe_device_shape_id = "CISCO_ASA"
}

resource "oci_core_ipsec" "site_to_site_vpn" {
  compartment_id = var.compartment_id
  display_name   = "production-site-to-site-vpn"
  cpe_id         = oci_core_cpe.on_premises_router.id
  drg_id         = oci_core_drg.production_drg.id

  # BGP dynamic routing - recommended over static routes
  static_routes = []

  cpe_local_identifier      = var.on_premises_public_ip
  cpe_local_identifier_type = "IP_ADDRESS"

  defined_tags = { "Operations.ManagedBy" = "terraform", "Operations.Environment" = "production" }
}

output "ipsec_connection_id" { value = oci_core_ipsec.site_to_site_vpn.id }

Step 2: Tunnel Configuration with BGP

resource "oci_core_ipsec_connection_tunnel_management" "tunnel_1" {
  ipsec_id  = oci_core_ipsec.site_to_site_vpn.id
  tunnel_id = data.oci_core_ipsec_connection_tunnels.tunnels.ip_sec_connection_tunnels[0].id

  display_name = "vpn-tunnel-1-bgp"
  routing      = "BGP"

  bgp_session_info {
    customer_bgp_asn      = var.on_premises_bgp_asn
    customer_interface_ip = "10.10.10.1/30"
    oracle_interface_ip   = "10.10.10.2/30"
  }

  phase_one_details {
    is_custom_phase_one_config = true
    authentication_algorithm   = "SHA2_384"
    encryption_algorithm       = "AES_256_CBC"
    diffie_hellman_group       = "GROUP20"
    lifetime_in_seconds        = 28800
  }

  phase_two_details {
    is_custom_phase_two_config  = true
    authentication_algorithm    = "HMAC_SHA2_256_128"
    encryption_algorithm        = "AES_256_GCM"
    lifetime_in_seconds         = 3600
    is_pfs_enabled              = true
    pfs_dh_group                = "GROUP20"
  }

  shared_secret = var.vpn_tunnel_1_psk
  ike_version   = "V2"
}

resource "oci_core_ipsec_connection_tunnel_management" "tunnel_2" {
  ipsec_id  = oci_core_ipsec.site_to_site_vpn.id
  tunnel_id = data.oci_core_ipsec_connection_tunnels.tunnels.ip_sec_connection_tunnels[1].id

  display_name = "vpn-tunnel-2-bgp-backup"
  routing      = "BGP"

  bgp_session_info {
    customer_bgp_asn      = var.on_premises_bgp_asn
    customer_interface_ip = "10.10.20.1/30"
    oracle_interface_ip   = "10.10.20.2/30"
  }

  phase_one_details {
    is_custom_phase_one_config = true
    authentication_algorithm   = "SHA2_384"
    encryption_algorithm       = "AES_256_CBC"
    diffie_hellman_group       = "GROUP20"
    lifetime_in_seconds        = 28800
  }

  phase_two_details {
    is_custom_phase_two_config = true
    authentication_algorithm   = "HMAC_SHA2_256_128"
    encryption_algorithm       = "AES_256_GCM"
    lifetime_in_seconds        = 3600
    is_pfs_enabled             = true
    pfs_dh_group               = "GROUP20"
  }

  shared_secret = var.vpn_tunnel_2_psk
  ike_version   = "V2"
}

Step 3: Tunnel Status Alarm

resource "oci_monitoring_alarm" "vpn_tunnel_down" {
  compartment_id        = var.compartment_id
  display_name          = "vpn-tunnel-status-down"
  is_enabled            = true
  metric_compartment_id = var.compartment_id
  namespace             = "oci_vpn"
  query                 = "TunnelState[5m]{ipsecId = 'IPSEC_OCID'}.mean() < 1"
  severity              = "CRITICAL"
  pending_duration      = "PT5M"
  destinations          = [var.ops_notification_topic_id]
  body                  = "One or more VPN tunnels are down. Check on-premises CPE configuration and BGP session status. If both tunnels are down, on-premises connectivity to OCI is completely lost."
}

Operational Notes

OCI provisions two IPSec tunnel headends in different fault domains for every connection. Your on-premises CPE should maintain active BGP sessions to both tunnels. Configure your CPE to prefer one tunnel by adjusting BGP local preference or MED attributes, and use the other as a hot standby. If the primary tunnel goes down, BGP failover to the standby tunnel typically completes in under 30 seconds, depending on your BGP hold timer configuration.

Use IKEv2 with AES-256-GCM in Phase 2 for maximum security and performance. IKEv1 is supported but has a longer negotiation sequence and does not support AEAD ciphers. Generate separate pre-shared keys for each tunnel. A compromised PSK only affects one tunnel, and the key rotation process interrupts traffic only on the tunnel being rekeyed while the other tunnel continues passing traffic.

Regards,
Osama

#OCI #OracleCloud #SiteToSiteVPN #IPSec #BGP #Terraform #IaC #TechBlog #Oracle #PlatformEngineering #NetworkSecurity #HybridCloud #OracleCloudInfrastructure #DRG #VPN #IKEv2 #CPE #NetworkConnectivity #HighAvailability #CloudNetworking

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.