Accessing VPC resources
This document outlines a couple of options for connecting to AWS resources deployed within an VPC which are inaccessible from the public internet.
Throughout this document, connecting to an API Gateway deployed within a VPC is used as the example, but the process should be the same for connecting to any resource within the VPC.
Prerequisites
- Both options require the Session Manager plugin for the AWS CLI.
- Both options assume that you have a running EC2 instance inside of your VPC.
Option 1: Use an SSH tunnel via SSM
-
Use SSM to create a tunnel from a port on your local machine (9000 in this example) to port 22 on the EC2 instance.
aws ssm start-session --target <instance id> \ --document-name AWS-StartPortForwardingSession \ --parameters portNumber=22,localPortNumber=9000
-
Create an SSH tunnel to forward a local port to the private API via the SSM tunel. This examples forwards connections from
localhost:8000
to port 443 on the API endpoint:ssh -p 9000 -N \ -L 8000:<api id>.execute-api.us-east-1.amazonaws.com:443 \ -i ~/.ssh/id_rsa [email protected]
If this is your first time using public/private key authentication to connect to the EC2 instance, you may need to add your key pair to the machine.
-
Add an entry to
/etc/hosts
pointing the API Gateway domain to your local machine. Requests directly tohttps://localhost:8000
will fail because you're not using a domain that matches the SSL certificate for the real API Gateway domain.127.0.0.1 <api-id>.execute-api.us-east-1.amazonaws.com
-
Access your API at
https://<api-id>.execute-api.us-east-1.amazonaws.com:8000
Option 2: Use a SOCKS5 proxy with SSM
Background
SOCKS5 proxies use dynamic port forwarding:
Dynamic Port Forwarding can handle connections from multiple ports. It analyzes the traffic to determine the proper destination for the given connection. For example, a browser configured to use it as a SOCKS proxy can then access HTTP, HTTPS, FTP, etc. over the same connection
A single dynamic port forward can allow access to multiple remote destinations, even across multiple protocols, which is more efficient than a separate local port forward per destination.
Furthermore, a dynamic port forward automatically figures out which remote port to request for the destination based on the protocol. For example, a request for https://<api-id>.execute-api.us-east-1.amazonaws.com
over a dynamic port forward will automatically request port 443 (the default for HTTPS) for that domain over the proxy and it does not need to be specified. So your request for the domain from your local machine looks the same as it would if the domain was public.
Setting up and using a SOCKS5 proxy
-
Use SSM to create a tunnel from a port on your local machine (9000 in this example) to port 22 on the EC2 instance.
aws ssm start-session --target <instance id> \ --document-name AWS-StartPortForwardingSession \ --parameters portNumber=22,localPortNumber=9000
-
Use an SSH tunnel to create a dynamic port forward (in this example port 1234) from your machine to the EC2 instance:
ssh -p 9000 -D 1234 -N -i ~/.ssh/id_rsa [email protected]
If this is your first time using public/private key authentication to connect to the EC2 instance, you may need to add your key pair to the machine.
-
Configure your browser to use the SOCKS5 proxy:
a. For Firefox, create a proxy auto-config file:
function FindProxyForURL(url, host) { if (dnsDomainIs(host, "<api-id>.execute-api.us-east-1.amazonaws.com")) return "SOCKS5 localhost:1234"; // by default use no proxy return "DIRECT"; }
Then, update the Firefox network settings to use your proxy auto-config;
b. For Chrome, launch a new instance that is aware of the SOCKS5 proxy:
$ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \ --user-data-dir="$HOME/proxy-profile" \ --proxy-server="socks5://localhost:1234"
-
Access your API at
https://<api-id>.execute-api.us-east-1.amazonaws.com
. Again, note that no special port needs to be specified for the request.
Tips & tricks
Useful shell functions
You can use local shell functions to make connecting to SSM easier. These functions would go in your .bashrc
, .zshrc
, or equivalent file.
These functions both assume that your EC2 instance has a tag of Deployment
and that you have set the value of the tag as DEPLOYMENT
in your shell. But you can easily tweak these scripts to match whatever tags you have on your instance.
function get-ssm-host () {
aws ec2 describe-instances \
--filters "Name=tag:Deployment,Values=$DEPLOYMENT" "Name=instance-state-name,Values=running" \
| jq -r '.Reservations[0].Instances[0].InstanceId'
}
function ssm-start() {
aws ssm start-session \
--target `DEPLOYMENT=$DEPLOYMENT get-ssm-host` \
--document-name AWS-StartPortForwardingSession \
--parameters portNumber=22,localPortNumber=9000
}
Once you add these functions and re-load your shell, you can run ssm-start
to start an SSM tunnel.
Using SSH config
Another option to create your SSH tunnels is to add a named host to your ~/.ssh/config
:
Host ssm-proxy
Hostname 127.0.0.1
User ec2-user
LocalForward 8000 <api id>.execute-api.us-east-1.amazonaws.com:443
DynamicForward 1234
IdentityFile ~/.ssh/id_rsa
Port 9000
Once you've added this config, then all you need to run is ssh ssm-proxy
to create your SSH tunnel. Notice that this SSH config can contain both local port forwarding and dynamic port forwarding.