Accessing a website through an ssh tunnel

Introduction

SSH tunnelling has been around for years and there are plenty of discussions and how-to’s on the internet that explain the process, the mechanism and anything else about it. This is my small and humble explanation and demonstration.

Lets say you have a Linux ( or any *nix) server and you want to access a service via a website that also resides on that server. The problem is you don’t want to expose that monitoring website on the  internet or on the intranet. You don’t want it to be scanned or seen by anyone or anything.
The simple answer is to run the website on the internal loopback address at 127.0.0.1. Then you have to view that website. There are two possible ways to accomplish this. One is to use the local console, the other is to use an ssh tunnel.

Console solution.

If you have a server that is hosted and it provides a console (usually on a separate network) you could boot up the server into GUI mode, log into the console, start up a local copy of Firefox or your web browser of choice,
point it to http://127.0.01/website and there you are set happily monitoring your site.
The problem with this is all the resources necessary to accomplish this are extreme. And on a slow line it could be cumbersome.

Another way, that I think is a little related to the console method  is to run X11forwarding or possibly a console utility like VNC. But these all have the same resource problems and they may open up even more security problems.

SSH Tunnel Solution

Set up your websites on the server so that they are bound to 127.0.0.1 and any port you want. We’ll choose 8080 for illustrative purposes. I’ll use the built in nginx status page as an example. Here is a good explanation of setting up the nginx status page: Enable Nginx status page .
Now the only thing that can see anything running on 127.0.0.1 has to reside on the server itself, unless you use an ssh tunnel.

Setting up the website.

Lets say your running nginix and you set up the following file called localhost.conf. Apache has similar statements and syntax. Check your manual for specifics.

The localhost website has the following properties:

  1. The server only listens on 127.0.0.1:8080
  2. The root location is only allowed from 127.0.0.1 (suspenders for the belt)
  3. the status page and everything else is only allowed from 127.0.0.1
server {
    listen       127.0.0.1:8080;
    server_name  localhost;

    access_log  /var/log/nginx/localhost.access.log  main;

    root   /usr/share/nginx/html;
    index  index.php index.html index.htm;
    try_files $uri $uri/ /index.php$is_args$args;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ =404;
        allow 127.0.0.1;
        deny all;
    }

    # This is the status page.
    location /nginx_status {
          stub_status on;
          access_log   off;
          allow 127.0.0.1;
          deny all;
    }

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    location ~ /\.ht {
        deny  all;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    # Pass all .php files onto a php-fpm/php-fcgi server.
    # location ~ \.php$ {
    location ~ [^/]\.php(/|$)
    {
        allow 127.0.0.1;
        deny all;

        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) {
                return 404;
        }
        include        /etc/nginx/fastcgi_params;
        #  #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
        fastcgi_pass   php_upstream; # this uses the upstream module
        fastcgi_intercept_errors on;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    }
}

Now the only thing that can access this site (the nginx status page)  is something coming from 127.0.0.1. That’s, not very useful unless you have a way to access it remotely.

Set up the ssh tunnel

Method 1

Update: Wed Dec 24 13:16:03 EST 2014
Check out the following link at DigitalOcean for ssh tunnelling:

This method does not require any of the port forwarding rules in the router or DSL modem. It is simple to set up and the on I would recommend.

How To Configure Custom Connection Options for your SSH Client

It that link you will find informaiton ion setting up the client side, per user, ssh config file and this will remove the need to log in and run the ssh command listed in method 2. Instead every time you log in the forwarding  will automatically be set up. I used the local_to_remote entry only and it worked fine. I also used a CNAME ( a DNS alias or host alias ). This way I log in with that host name and any subsequent logins to the server using other names do not generate “port already in use” conflicts.

# This will allow us to use port 8080 on the local machine
# in order to access example.com at port 80 from the remote machine
Host local_to_remote-host-name-that-you-will-use-to-login
    #LocalForward 8080 example.com:80
    LocalForward 9001 localhost:8080  # This reflects the ssh command used above
# This will allow us to offer access to internal.com at port 443
# to the remote machine through port 7777 on the other side
Host remote_to_local
    RemoteForward 7777 internal.com:443

Method 2

This method is here for completeness and reference.

You must be able to accept incoming ssh connections. To this you will need to have the following.
Consult the documentation of your devices on how to do this.

  1. An ssh server running
  2. A rule in your router  that allows ssh to be forwarded to your workstation
  3. A rule in  your cable/DSL modem that allows ssh to be forwarded to your workstation or router.

log into the remote server from your workstation as a normal user via ssh. Your workstation has to be running an ssh server. ssh usually runs on port 22 but it can be configured to run on any port you choose. Execute the following on the remote server. You can use any unused port, it does not have to be 9001. Use the “netstat -an | grep LIST”  command to discover what ports are being used.

$ ssh -4 -p 22 -v -R 9001:localhost:8080 ip-address-of-your-workstation.

now on your workstation access the website with the following URL:

http://localhost:9001/nginx_status

You should see the statistics for the remote server. The advantages are

  1. Your entire session is encrypted using ssh, no need for SSL.
  2. Your websites cannot be seen by outside scanners.
  3. This keeps you safe from unknown vulnerabilities from remote attacks since the website can not be accessed remotely.
  4. Only people with ssh access have access to the local websites.

Nothing is full proof but this does raise the bar quite a bit while allowing you access to local resources available via HTTP.
Of course this can be applied to other protocols as well, not just HTTP.


Leave a Reply

Your email address will not be published. Required fields are marked *