vim, git, aws and other three-letter words

Software Development Notes

RDS Access through SSH Tunnel

Using SSH tunnels, it is possible to access remote resources that are not exposed to the Internet through intermediate hosts, or to expose your local services to the Internet.

Setup

To make SSH commands shorter and easier to use, edit the ~/.ssh/config and add the configuration for the hosts you are going to connect to.

The configuration defines default SSH options, so instead of a command like this ssh ec2-user@ec2-55-222-55-55.compute-1.amazonaws.com -i ~/.ssh/my_key.pem, we can just use ssh my-remote-host.

An example config:

Host my-remote-host
HostName ec2-55-222-55-55.compute-1.amazonaws.com
StrictHostKeyChecking no
User ec2-user
IdentityFile ~/.ssh/my_key.pem

Access Remote Hidden Resource

The SSH command to access the remote hidden resource locally through the intermediate accessible host is ssh -L:

local_port=5532

accessible_host=my-remote-host

hidden_host=hidden_host.amazonaws.com
hidden_port=5432

ssh ${accessible_host} -L ${local_port}:${hidden_host}:${hidden_port}

With the command above, we connect to my-remote-host, which has access to hidden_host:hidden_port, and make the hidden resource available locally:

localhost:5532 =====> my-remote-host =====> hidden_host.amazonaws.com:5432

Expose Local Resource To the Internet

The SSH command to expose the local resource through the intermediate host is ssh -R:

remote_port=8181
local_port=8888

ssh my-remote-host -R *:${remote_port}:localhost:${local_port}

With the command above, we connect to my-remote-host and instruct it to accept connections to port 8181 and forward them to localhost:8888.

The *:8181 means that the remote host will accept connections on any network interface (by default, it will use only 127.0.0.1).

localhost:8888 <===== my-remote-host <===== my-remote-host:8181

You also need to make sure that the firewall on my-remote-host allows connections to port 8181.

Example: Access RDS Database Through the EC2 Instance

It is a good idea to make RDS databases not available from the Internet, so they can only be accessed from the EC2 instances where applications are running.

On the other hand, during development, it is convenient to have the database accessible from your local machine. It is easy to do by running the following command:

ssh my-aws-host -L 5532:my-rds-host-name.cdiofumqrcpr.us-east-1.rds.amazonaws.com:5432

Here my-aws-host is the EC2 instance that has DB access and my-rds-host-name.cdiofumqrcpr...:5532 is the RDS host name and port.

After that, you can use localhost:5532 on your local machine to connect to the remote database, for example, with psql:

psql postgresql://my_db_user@localhost:5532/my_db_name

Or dump the database with pg_dump:

pg_dump -Fc -v --dbname=postgresql://my_db_user@localhost:5532/my_db_name -f my_db_name$(date --iso-8601).pq

Note: neither command above specifies the database password. To make them work, the password can be specified in the ~/.pgpass file (so it will not be present in the shell history or visible on the screen when the command is executed).

The ~/.pgpass file looks like this:

localhost:5532:*:my_db_user_one:XXXYYY
localhost:5532:*:my_db_user_two:XXXYYY

Example: Connect to Redis on AWS

Similarly to the PostgreSQL example above, we can create an SSH tunnel to the EC2 instance that has Redis access and expose Redis locally:

ssh my-ec2-instance -L 6379:id.wssxxx.0001.appp1.cache.amazonaws.com:6379

After that, we can use redis-cli or any other tool on the local machine to connect to Redis on port 6379.

Example: Expose Local Server Through the EC2 Instance

Sometimes it can be necessary to make a locally running app available through the Internet. There are several ways to do that:

  • If you have the "real" IP address (you may need to ask your Internet provider to set up a real IP for you), you only need to make sure that your firewall allows connections to the port your app is running on
  • Use a service like ngrok, which will do the forwarding from the Internet to your local machine
  • Use an SSH tunnel to the EC2 instance (or any other machine that's accessible from the Internet)

Let's say there is an app running locally on port 8888 (localhost:8888) and we want to make it available via the EC2 instance ec2-55-222-55-55.compute-1.amazonaws.com:8181.

To use the SSH tunnel, first, make sure there is a GatewayPorts yes option in the sshd config on the server. SSH to the instance and open /etc/sshd_config:

sudo vim /etc/sshd_config

Find and change, or add, the GatewayPorts yes option and save the config. Restart sshd:

sudo service sshd restart

Second, check and change, if necessary, the security group configuration for the EC2 instance and allow access to port 8181.

Now, from your local machine, run:

ssh my-aws-host -R *:8181:localhost:8888

Now you should be able to access your local app via ec2-55-222-55-55.compute-1.amazonaws.com:8181.

Troubleshooting

If the connection fails, check the following:

  • the SSH connection and tunnel are established successfully - check the SSH output
  • sometimes, the connection is working, but the tunnel isn't; in the SSH output, you can see something like "Port forwarding is disabled to avoid man-in-the-middle attacks." and the instructions on what to do
  • you don't have anything running locally on the specified port
    • check with nc -v 127.0.0.1 5532 to see if there is a connection
    • additionally check with netstat -l | grep 5532 and lsof -i :5532

Useful searches for other issues are "ssh tunnel aws rds problem" and "ssh tunnel aws rds connection error".

References

Tags: aws ssh