AWS VPN GCP Cloud VPN IPsec IKE networking

AWS Site-to-Site VPN tunnel DOWN: diagnosing IKE Phase 1/2 negotiation failure with GCP Cloud VPN

Diagnose and fix AWS Site-to-Site VPN tunnel DOWN caused by IKE Phase 1/2 negotiation failure when connecting to GCP Cloud VPN.

·
Your AWS Site-to-Site VPN tunnel is stuck in DOWN state and GCP Cloud VPN shows "Waiting for peer" or "First handshake". Cross-cloud traffic has stopped flowing. This runbook walks through every IKE negotiation mismatch that causes this failure and how to resolve each one.

Symptoms

AWS VPN console shows tunnel status as DOWN:

Tunnel 1: DOWN
  Status message: IPSEC IS DOWN
  IKE Status: Phase 1 negotiation failed

GCP Cloud VPN console shows one of:

VPN tunnel status: Waiting for peer
VPN tunnel status: First handshake

Cloud Logging on GCP side shows repeated negotiation attempts:

IKE SA negotiation failed: no proposal chosen

Traffic between AWS VPC and GCP VPC is completely dropped. BGP session (if configured) never establishes because the underlying IPsec tunnel cannot come up.

On AWS, the VPN tunnel metrics in CloudWatch confirm:

aws cloudwatch get-metric-statistics \
  --namespace "AWS/VPN" \
  --metric-name TunnelState \
  --dimensions Name=VpnId,Value=vpn-0abc123def456 \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 300 \
  --statistics Average

Returns 0.0 consistently (tunnel down).

Cause

IKE Phase 1/2 negotiation failure between AWS and GCP typically comes from one or more of these mismatches:

  1. IKE version mismatch. AWS Site-to-Site VPN defaults to IKEv2. If the GCP Cloud VPN tunnel was created with IKEv1 (or vice versa), the initial handshake fails immediately because the IKE header format differs between versions.

  2. Pre-shared key encoding differences. Special characters in the PSK (such as !, @, #, $) may be interpreted differently by each platform. AWS stores the PSK as-is, but some GCP configurations may URL-encode or escape characters during input.

  3. Phase 2 (IPsec) SA proposals incompatible. AWS and GCP must agree on identical encryption and integrity algorithms. AWS may propose AES-256-CBC with SHA-256, while GCP defaults to AES-256-GCM (which combines encryption and integrity). If there is no overlap in proposals, Phase 2 fails even after Phase 1 succeeds.

  4. DPD (Dead Peer Detection) timeout too aggressive. If one side has a very short DPD timeout (e.g., 10 seconds on AWS) and the other has a longer interval, the aggressive side tears down the tunnel before the peer responds to the DPD probe.

  5. Rekeying window collision. Both sides initiate rekeying at the same time. When both AWS and GCP attempt to rekey simultaneously (due to similar SA lifetime values), the collision causes both to drop the existing SA without successfully negotiating a replacement.

Fix

Step 1: Retrieve current AWS VPN tunnel configuration

aws ec2 describe-vpn-connections \
  --vpn-connection-ids vpn-0abc123def456 \
  --query 'VpnConnections[0].Options.TunnelOptions' \
  --output json

Note the values for IkeVersions, Phase1EncryptionAlgorithms, Phase2EncryptionAlgorithms, DPDTimeoutSeconds, and PreSharedKey.

Step 2: Retrieve GCP Cloud VPN tunnel configuration

gcloud compute vpn-tunnels describe my-vpn-tunnel \
  --region us-central1 \
  --format="yaml(ikeVersion, sharedSecret, status, detailedStatus)"

Step 3: Align IKE versions

Both sides must use the same IKE version. Force IKEv2 on both:

On AWS, modify the VPN tunnel options:

aws ec2 modify-vpn-tunnel-options \
  --vpn-connection-id vpn-0abc123def456 \
  --vpn-tunnel-outside-ip-address 203.0.113.1 \
  --tunnel-options "IkeVersions=[{Value=ikev2}]"

On GCP, recreate the tunnel with IKEv2 (GCP does not support in-place IKE version changes):

gcloud compute vpn-tunnels delete my-vpn-tunnel --region us-central1 --quiet

gcloud compute vpn-tunnels create my-vpn-tunnel \
  --region us-central1 \
  --peer-address 203.0.113.1 \
  --shared-secret "YOUR_PSK_HERE" \
  --ike-version 2 \
  --vpn-gateway my-vpn-gateway \
  --interface 0 \
  --router my-cloud-router

Step 4: Verify pre-shared key matches exactly

Export the PSK from AWS:

aws ec2 describe-vpn-connections \
  --vpn-connection-ids vpn-0abc123def456 \
  --query 'VpnConnections[0].Options.TunnelOptions[0].PreSharedKey' \
  --output text

Compare byte-for-byte with the GCP side:

gcloud compute vpn-tunnels describe my-vpn-tunnel \
  --region us-central1 \
  --format="value(sharedSecret)"

If the keys contain special characters, replace with an alphanumeric-only PSK on both sides. AWS recommends 8-64 characters using only letters, numbers, periods, and underscores.

Update on AWS:

aws ec2 modify-vpn-tunnel-options \
  --vpn-connection-id vpn-0abc123def456 \
  --vpn-tunnel-outside-ip-address 203.0.113.1 \
  --tunnel-options "PreSharedKey=MySecurePsk2024.tunnel1"

Update on GCP (requires tunnel recreation or update):

gcloud compute vpn-tunnels update my-vpn-tunnel \
  --region us-central1 \
  --shared-secret "MySecurePsk2024.tunnel1"

Step 5: Align Phase 2 encryption algorithms

Set both sides to AES-256-GCM (recommended for performance and security):

On AWS:

aws ec2 modify-vpn-tunnel-options \
  --vpn-connection-id vpn-0abc123def456 \
  --vpn-tunnel-outside-ip-address 203.0.113.1 \
  --tunnel-options '{
    "Phase1EncryptionAlgorithms": [{"Value": "AES256"}],
    "Phase1IntegrityAlgorithms": [{"Value": "SHA2-256"}],
    "Phase1DHGroupNumbers": [{"Value": 20}],
    "Phase2EncryptionAlgorithms": [{"Value": "AES256-GCM-16"}],
    "Phase2IntegrityAlgorithms": [{"Value": "SHA2-256"}],
    "Phase2DHGroupNumbers": [{"Value": 20}]
  }'

GCP Cloud VPN HA automatically negotiates compatible algorithms when IKEv2 is used, but verify in logs:

gcloud logging read 'resource.type="vpn_gateway" AND textPayload:"proposal"' \
  --project my-project \
  --limit 20 \
  --format="table(timestamp, textPayload)"

Step 6: Adjust DPD timeout

Set a reasonable DPD timeout on AWS (30 seconds is a safe default):

aws ec2 modify-vpn-tunnel-options \
  --vpn-connection-id vpn-0abc123def456 \
  --vpn-tunnel-outside-ip-address 203.0.113.1 \
  --tunnel-options "DPDTimeoutSeconds=30,DPDTimeoutAction=restart"

GCP Cloud VPN uses a fixed DPD interval internally. Setting AWS to 30 seconds with restart action ensures the tunnel recovers automatically after transient network issues without being too aggressive.

Step 7: Prevent rekeying collisions

Offset SA lifetimes between sides. On AWS, set Phase 1 lifetime to 28800 seconds (8 hours) and Phase 2 to 3600 seconds (1 hour):

aws ec2 modify-vpn-tunnel-options \
  --vpn-connection-id vpn-0abc123def456 \
  --vpn-tunnel-outside-ip-address 203.0.113.1 \
  --tunnel-options '{
    "Phase1LifetimeSeconds": 28800,
    "Phase2LifetimeSeconds": 3600,
    "RekeyMarginTimeSeconds": 540,
    "RekeyFuzzPercentage": 100
  }'

The RekeyFuzzPercentage of 100 adds randomness to the rekey window, reducing the chance of simultaneous rekeying from both ends.

Step 8: Check GCP Cloud VPN logs for remaining errors

gcloud logging read '
  resource.type="vpn_gateway"
  AND severity>=WARNING
  AND timestamp>="2026-07-14T00:00:00Z"
' --project my-project --limit 50 --format="table(timestamp, severity, textPayload)"

Look for messages like “no proposal chosen”, “invalid ke payload”, or “auth failed” to identify any remaining mismatches.

Validation

Confirm both tunnels show UP on AWS:

aws ec2 describe-vpn-connections \
  --vpn-connection-ids vpn-0abc123def456 \
  --query 'VpnConnections[0].VgwTelemetry[*].{OutsideIP:OutsideIpAddress,Status:Status,StatusMessage:StatusMessage}' \
  --output table

Expected output:

------------------------------------------------------
|                  VgwTelemetry                       |
+----------------+--------+--------------------------+
|   OutsideIP    | Status |     StatusMessage        |
+----------------+--------+--------------------------+
|  203.0.113.1   |  UP    |  2 BGP ROUTES            |
|  203.0.113.2   |  UP    |  2 BGP ROUTES            |
+----------------+--------+--------------------------+

Confirm GCP tunnel status:

gcloud compute vpn-tunnels describe my-vpn-tunnel \
  --region us-central1 \
  --format="value(status, detailedStatus)"

Expected: ESTABLISHED Tunnel is up and running.

Verify BGP session is ESTABLISHED on Cloud Router:

gcloud compute routers get-status my-cloud-router \
  --region us-central1 \
  --format="yaml(result.bgpPeerStatus)"

Test end-to-end connectivity from an instance in AWS VPC to GCP VPC:

# From AWS EC2 instance
ping -c 5 10.128.0.2

# Full path test
traceroute 10.128.0.2

Monitor tunnel stability over the next hour:

aws cloudwatch get-metric-statistics \
  --namespace "AWS/VPN" \
  --metric-name TunnelState \
  --dimensions Name=VpnId,Value=vpn-0abc123def456 \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 \
  --statistics Average

All data points should return 1.0 (tunnel up).

Jerzy Kopaczewski

Need help with multi-cloud networking?

Book a free 30-minute call. We will diagnose your connectivity issue and recommend the right fix.