Link Search Menu Expand Document

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

  1. 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
    
  2. 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.

  3. Add an entry to /etc/hosts pointing the API Gateway domain to your local machine. Requests directly to https://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
    
  4. 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

  1. 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
    
  2. 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.

  3. 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;

    Screenshot of configuration in Firefox network settings to use a SOCKS5 proxy auto-config file

    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"
    
  4. 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.