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.
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:
-
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.
-
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. -
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.
-
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.
-
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).
Need help with multi-cloud networking?
Book a free 30-minute call. We will diagnose your connectivity issue and recommend the right fix.