I-ALiRT - vpn tunnel - #1334
Conversation
|
I think we won't need this: d) The following networks/prefixes requiring access to NOAA resources will be routed to NOAA N-Wave via static routing by LASP: if we use BGP |
|
@laspsandoval This is a great fix for security purposes, but the system (LASP's IMAP? AWS infrastructure) still won't be covered by an SSP. Is Rachel going to add the AWS infrastructure to LASP's institutional SSP? |
There was a problem hiding this comment.
Pull request overview
Adds I-ALiRT Site-to-Site VPN infrastructure for NOAA N-Wave connectivity and wires it into the SDS CDK deployment.
Changes:
- Adds a Virtual Private Gateway and public subnet route propagation.
- Adds a new I-ALiRT VPN construct for NOAA customer gateways and VPN connections.
- Retrieves VPN PSK/router IP configuration and adds a placeholder for NOAA ECS access rules.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
sds_data_manager/utils/stackbuilder.py |
Retrieves NOAA VPN configuration and instantiates the VPN construct. |
sds_data_manager/constructs/networking_construct.py |
Adds VGW creation, VPC attachment, and route propagation. |
sds_data_manager/constructs/ialirt_vpn_construct.py |
Defines NOAA customer gateways, VPN connections, and tunnel crypto settings. |
sds_data_manager/constructs/ialirt_processing_construct.py |
Adds a TODO placeholder for NOAA security group ingress. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Outdated. |
8d8ef84 to
b534578
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
sds_data_manager/utils/stackbuilder.py:450
- The NOAA VPN resources are created unconditionally in build_sds(). This will break deployments in accounts/environments where the required SSM parameters and Secrets Manager secret don't exist (CloudFormation will fail resolving them), and it also forces creation of a Transit Gateway/VPN in every environment. Consider gating this whole block behind an explicit account_config flag (e.g. enable_noaa_vpn) and/or limiting it to the intended account(s).
# Retrieve the NOAA VPN pre-shared key from Secrets Manager.
# Store the PSK under the key "psk" in a secret named
# "/ialirt/noaa/noaa-vpn-psk" before deploying this stack.
noaa_vpn_psk = (
secretsmanager.Secret.from_secret_name_v2(
ialirt_stack, "NoaaVpnPsk", "/ialirt/noaa/noaa-vpn-psk"
)
.secret_value_from_json("psk")
.unsafe_unwrap()
)
sds_data_manager/constructs/ialirt_vpn_construct.py:75
- New infrastructure construct IalirtVpnConstruct introduces non-trivial CDK/CloudFormation resources (CustomerGateways, VPNConnections, tunnel option specs) but there are no corresponding aws_cdk.assertions-based unit tests (tests/infrastructure has this pattern for other constructs). Adding a template assertion test would help prevent accidental changes to tunnel parameters and resource counts.
tunnel = ec2.CfnVPNConnection.VpnTunnelOptionsSpecificationProperty(
pre_shared_key=psk,
ike_versions=[
ec2.CfnVPNConnection.IKEVersionsRequestListValueProperty(value="ikev2")
],
phase1_encryption_algorithms=[
ec2.CfnVPNConnection.Phase1EncryptionAlgorithmsRequestListValueProperty(
value="AES256"
)
],
lacoak21
left a comment
There was a problem hiding this comment.
LGTM! I tried tracking the flow down and it looks good. This was on a high level though so the testing will be key. Great work!!
…nd hands it to the vpc
83478cb
into
IMAP-Science-Operations-Center:dev
What we provide to NOAA:
What NOAA provides to us:
Neither side "provides" - needs to go back to NOAA as a platform limitation:
Notes:
Why we used Transit Gateway:
A Virtual Private Gateway (VGW) is the "default" way to terminate a Site-to-Site VPN. It attaches directly to a VPC and injects routes into that VPC's subnet route tables. A VGW cannot route decrypted VPN traffic to a NAT Gateway.
Why that's a problem here: I-ALiRT's actual endpoint is an EC2 instance in an Auto Scaling Group. Its private IP isn't stable. If the ASG replaces the instance, the private IP changes. What is stable is its Elastic IP, which is reached by going out through a NAT Gateway. So NOAA's decrypted traffic needs to land in the VPC and then get handed to the NAT Gateway so it can reach that Elastic IP - exactly the hop a VGW can't do.
A Transit Gateway can do that hop. A TGW attaches to the VPC and its traffic follows whatever the subnet's own route table says - including a NAT Gateway route. So terminating the VPN on a TGW instead of a VGW means the decrypted NOAA traffic gets funneled through the NAT Gateway, out to the Elastic IP, and hits the instance the same way everyone else's traffic does.
- Step 1: NOAA sends data
The packet has:
- Step 2: NOAA's border router encrypts the packet
The router has a rule: Traffic destined for the I-ALiRT EIP goes through the VPN tunnel
The router wraps the original packet in an encrypted IPSec envelope. The outer envelope has:
- Step 3: Travels across public internet
- Step 4: AWS Transit Gateway decrypts the packet
AWS decrypts the envelope using the pre-shared key (Secrets Manager, /ialirt/noaa/noaa-vpn-psk).
The outer envelope is discarded. The original inner packet is restored:
- Step 5: Transit Gateway routes the packet into the VPC
TGW route table sends it through the VPC attachment, into a private subnet. Packet is unchanged.
- Step 6: Private subnet routes the packet to the NAT Gateway
Default route (0.0.0.0/0) sends it there, since the EIP isn't inside the VPC's own address range. The NAT Gateway rewrites the source IP to its own EIP (NOAA's original source IP is gone at
this point):
- Step 7: NAT Gateway hands off to Internet Gateway, which translates EIP → private IP
- Step 8: Security group check
SG sees source = NAT Gateway's own EIP (not NOAA's real IP), checked against the allow-list.
- Step 9: Container receives packet
Testing
To validate the NOAA N-Wave Site-to-Site VPN connection ahead of the real NOAA onboarding, I stood up an EC2 instance running strongSwan as a stand-in for NOAA's border router, configured
with the IKEv2/AES-256-CBC/SHA2-256/DH group14 parameters specified in the ICD, and deployed the IalirtStack CDK infrastructure — which routes the NOAA VPN connection through a Transit
Gateway to a NAT Gateway and I-ALiRT's stable Elastic IP in a sandbox AWS account for testing. This architecture was tested end-to-end on the WASH site's Tunnel 1, with a TCP connection successfully established from the strongSwan test host through the IPsec tunnel, BGP-derived routing, NAT Gateway, and security group to I-ALiRT's Elastic IP, confirming actual data packets traverse the full path.
Detailed reproducible instructions
Prerequisites: A running EC2 acting as the test "NOAA router" with strongSwan + FRR already installed (we reused an existing one: dev account).
Confirm the stack deployed the VPN resources
aws cloudformation describe-stack-resources --stack-name IalirtStack
--profile lcs --region us-west-2
Look for AWS::EC2::VPNConnection, AWS::EC2::CustomerGateway, AWS::EC2::TransitGateway* entries.
Look up the tunnel endpoint you're bringing up (e.g. WASH)
aws ec2 describe-vpn-connections --vpn-connection-ids
--region us-west-2 --profile lcs
--query 'VpnConnections[].Options.TunnelOptions[].{OutsideIp:OutsideIpAddress,InsideCidr:TunnelInsideCidr}'
--output json
Take Tunnel 1's OutsideIp and InsideCidr.
Point strongSwan at that tunnel
On the test EC2, edit /etc/swanctl/conf.d/noaa-aws.conf: set remote_addrs, the remote { id = ... } block, and the secrets.ike-tunnel1.id-2 entry all to the OutsideIp from step 2.
Point FRR's BGP config at the AWS-side inside IP
Edit /etc/frr/frr.conf: router bgp <NOAA ASN, e.g. 64583>, neighbor <AWS-side inside IP, e.g. x.x.x.x/30's .1> remote-as 64512 (AWS's amazon_side_asn), and a network statement for whatever
prefix you want to test-advertise.
Bring the tunnel up
sudo swanctl --load-all
sudo swanctl --initiate --child aws-tunnel1
Verify BGP came up
sudo vtysh -c "show bgp summary"
Expect ESTABLISHED against the AWS-side inside IP.
Confirm from the AWS side
aws ec2 describe-vpn-connections --vpn-connection-ids
--region us-west-2 --profile lcs --query 'VpnConnections[].VgwTelemetry' --output json
Expect Status: UP and a nonzero AcceptedRouteCount on that tunnel's OutsideIpAddress.
Find I-ALiRT's current Elastic IP (dynamically Lambda-assigned per instance, not static — always look it up fresh)
aws ec2 describe-instances
--filters "Name=tag:aws:cloudformation:stack-name,Values=IalirtStack" "Name=instance-state-name,Values=running"
--region us-west-2 --profile lcs
--query 'Reservations[].Instances[].{InstanceId:InstanceId,PublicIp:PublicIpAddress}' --output json
Send test traffic, explicitly sourced from the test EC2's private IP
echo "test" | nc -v -s 7563 -i 1
Expect "Connection ... succeeded!".