Today I needed to access Google Calendar API. Google Calendar uses OAuth 2.0 for authentication. OAuth 2.0 works by redirecting the user to the OAuth server to be authenticated. After authentication the user is redirected back to the website. This means that it does not work with localhost as localhost is not accessible from the internet, which is a bit of a bummer if you are developing on a local machine. As I do not want to open my development machine up to the outside world I needed a different approach. I decided to setup a secure tunnel between my machine and a server that is accessible from internet. I did this using SSH and Apache Server.
TL;DR
A reverse tunnel is when network traffic is forwarded from one computer to another computer. In this case we are going to forward traffic that connects to a server from internet to a local machine.
What are we going to do?
- Setup a secure virtual host in Apache Server using reverse proxy
- Create a SLL/TLS certificate using Let’s Encrypt
- Setup a reverse SSH tunnel on the client
Prerequisites
This post expects the following prerequisites:
Server
- A server that is accessible from internet
- Apache Server up and running
- SSH Server up and running with password login disabled (certificate login)
Client
- SSH client up and running that can connect to the server using a certificate
DNS and domain
- A domain or subdomain that points to the server
Setup a secure virtual host in Apache Server using reverse proxy
Part of the magic starts with the server accepting a secure connection from internet and forwarding this connection to the local machine through the SSH tunnel. We will use Apache to handle the connection from internet. Setup is pretty straight forward using the reverse proxy module of Apache.
Create a virtual host
Create a virtual host file.
$ sudo nano /etc/apache2/sites-available/google-api.mydomain.com.conf
And add the following contents to it.
<VirtualHost *:80>
ServerName google-api.mydomain.com
ProxyPass / http://127.0.0.1:8888/
ProxyPassReverse / http://127.0.0.1:8888/
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
The interesting lines her are ProxyPass
and ProxyPassReverse
. This maps the context root ‘/’ to the back-end server. This is just a fancy way of saying the https://google-api.mydomain.com
is redirected to http://127.0.0.1:8888
. In our case the back-end server is 127.0.0.1:8080
, which is port 8080
of the server. In this case it will be the SSH Server forwarding port 8080
 to the connected SSH client running on the local machine. More on that in a minute.
What do ProxyPass
and ProxyPassReverse
actually do and why do you need them both?
ProxyPass redirects a request from the Apache Server to another server. For example it redirects from https://my-domain.com/my-page
to http://localhost:8080/my-page
.
The response from the server can contain HTML, Javascript and CSS with links that point to the servers location (http://localhost:8080
). If this response is sent as is back to the browser it will not work, as the browser will try to access the links on the users local machine.
This is were ProxyPassReverse
comes in. ProxyPassReverse
will rewrite all links so that they point to the Apache Server. In other words it will rewrite (replace) http://localhost:8080
with https://mydomain.com
. The browser will then send requests back to the Apache Server which will redirect to the other server.
Add a SSL/TLS certificate
This virtual host uses the unsecured HTTP port. Not very good. Lets change it to the secure HTTPS port. For this to work we need to setup a certificate. We will use the free SSL/TLS certificate service of Let’s Encrypt.
Install Let’s Encrypt Certbot with the following commands.
$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-apache
Now generate and install the certificate.
$ sudo certbot --apache -d google-api.mydomain.com
Certbot will ask if you want to redirect HTTP to HTTPS. Choose YES!
Test the virtual host
Now we need to enable the Apache virtual host.
$ sudo a2ensite google-api.mydomain.com
Check if the configuration is valid.
$ sudo apache2ctl configtest
If it is valid then we can restart Apache to activate the virtual host.
$ sudo systemctl reload apache2
Cool now the server is accessible to the outside world over internet. Time to setup the SSH tunnel.
Setup a reverse SSH tunnel on the client
Execute the following command on the local machine from a command shell to connect to the SSH Server and forward network traffic from server port 8888 to client port 8080.
Spoiler alert: the following command might not work (see next section).
ssh -vR 8888:localhost:8080 -l nidkil google-api.mydomain.com
The options mean the following:
- -R: tells the tunnel to answer on the remote side and forward to the client. In other words reverse direction from the server to the client.
- -l: specifies the user to login as on the remote machine.
- -v: Verbose output.
Force SSH to use a specific private key
The previous command will only work if there is a config file in the users .ssh directory that tells SSH which private key to use for the connection. If it does not exist you will get a login error.
Lets create the config file.
$ notepad ~/.ssh/config
Add the following contents to the config file.
Host google-api.mydomain.com
IdentityFile ~/.ssh/your-private.ssh.key
The host must match the server name you use to connect to the server. In this case google-api.mydomain.com
.
Alternatively you can use the -i
command line option to pass the private key as an command line argument.
$ ssh -i ~/.ssh/your-private.ssh.key -vR 8888:localhost:8080 -l nidkil google-api.mydomain.com
Test
Okay now all the piece are in place it is time to test if it works. Open the following URL in your browser and if all goes well it should display the web page.
Note: make sure the local web or application server is running on the local machine.
https://google-api.mydomain.com
How cool is that?
Setup certificates for automatic renewal
The Let’s Encrypt certificates are only valid for 90 days. The need to be renewed before this period ends. This can be done with the Certbot tool and Crontab. Open up the Crontab file.
$ sudo crontab -e
Add the following line to the Crontab file.
0 2 * * 0 certbot renew && systemctl restart apache2
This is the syntax Crontab uses to specify day, date and time followed by the command to be run at that interval.

So this means that Certbot auto renew will run every week at 2 o’clock Sunday morning.
As we are adding the Certbot auto renew to the root’s Crontab there is no need to add sudo to the commands. It would also not work as sudo requires you to manually enter the password.
Debug SSH Server
When you run into problems with SSH it can help to view the SSH logs to figure out what the problem is. To check the SSH log file use the following command
$ sudo tail -f /var/log/auth.log
The server logs are your best friend when troubleshooting. It may be necessary to turn up the log level temporarily to get more information.
Important: Don’t forget to set it back to normal after things are fixed to avoid privacy problems or excessively use of disk space.
Open the SSH config file.
$ sudo nano /etc/ssh/sshd_config
Look for the following line.
LogLevel INFO
And change it to: VERBOSE.
LogLevel VERBOSE
And restart the SSH Server to activate the change.
$ sudo service ssh restart
Debug SSH client
To debug on the client side all you need to do is run the client with the -v
option, which will show verbose output. Change this to -vvv
to get even more verbose debugging information.
I hope you enjoyed this and found it useful. Happy hacking!