2018-01-11

SSH Tunnels (How to Access AWS RDS Locally Without Exposing it to Internet)

Using SSH tunnels, it is possible to access remote resources that are not exposed to the Internet through the intermediate hosts or 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.

The configuration defines default ssh options, so instead of the 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 the my-remote-host that has access to the 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 the my-remote-host and instruct it to accept connections to the 8181 port and forward them to the localhost:8888.

The *:8181 that remote host will forward connections to 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 firewall on my-remote-host allows connections to the 8181 port.

Example: Access RDS Database Through the EC2 Instance

It is 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 the development, it is convenient to have the database accessible from your local machine. It is easy to do it, 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...:5432 is the RDS host name and port.

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

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

Or dump the database with pg_dump:

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

Note: both commands above don't specify the database password, to make it 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 looks like this:

localhost:5432:*:my_db_user_one:XXXYYY
localhost:5432:*: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 6379 port.

Example: Expose Local Server Through the EC2 Instance

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

Let's say there is an app running locally on 8888 port (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, open the /etc/sshd_config:

sudo vim /etc/sshd_config

Find and change or add GatewayPorts yes option, 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 the 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:

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

References

The Black Magic Of SSH / SSH Can Do That?

How to make requests from an external server to localhost

Connect to MongoDB on aws Server From Another Server

profile for Boris Serebrov on Stack Exchange, a network of free, community-driven Q&A sites