diff --git a/requirements.txt b/requirements.txt
index 9b6b228..38646fb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -38,6 +38,7 @@ htmlmin==0.1.12;                python_version >= '2.7'
 Flask-HTMLmin==1.3.2;           python_version >= '2.7'
 SQLAlchemy>=1.2.5;              python_version >= '2.7'
 Flask-Security>=3.0.0;          python_version >= '2.7'
+sshtunnel>=0.1.3;               python_version >= '2.7'
 
 ###############################################################
 # Modules specifically required for Python2.7 or lesser version
diff --git a/web/config.py b/web/config.py
index 3d8f6f7..93a1d2d 100644
--- a/web/config.py
+++ b/web/config.py
@@ -378,3 +378,11 @@ try:
     from config_local import *
 except ImportError:
     pass
+
+##########################################################################
+# SSH Tunneling supports only for Python 2.7 and 3.4+
+##########################################################################
+SUPPORT_SSH_TUNNEL = False
+if (sys.version_info[0] == 2 and sys.version_info[1] >= 7) or \
+    (sys.version_info[0] == 3 and sys.version_info[1] >= 4):
+    SUPPORT_SSH_TUNNEL = True
diff --git a/web/migrations/versions/a68b374fe373_.py b/web/migrations/versions/a68b374fe373_.py
new file mode 100644
index 0000000..5e94bb1
--- /dev/null
+++ b/web/migrations/versions/a68b374fe373_.py
@@ -0,0 +1,41 @@
+
+"""Added columns for SSH tunneling
+
+Revision ID: a68b374fe373
+Revises: 50aad68f99c2
+Create Date: 2018-04-05 13:59:57.588355
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from pgadmin.model import db
+
+# revision identifiers, used by Alembic.
+revision = 'a68b374fe373'
+down_revision = '50aad68f99c2'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN use_ssh_tunnel INTEGER DEFAULT 0'
+    )
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN tunnel_host TEXT'
+    )
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN tunnel_port TEXT'
+    )
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN tunnel_username TEXT'
+    )
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN tunnel_authentication INTEGER DEFAULT 0'
+    )
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN tunnel_identity_file TEXT'
+    )
+
+def downgrade():
+    pass
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 9df9dc9..3741d9d 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -617,7 +617,8 @@ def utils():
             editor_insert_pair_brackets=insert_pair_brackets,
             editor_indent_with_tabs=editor_indent_with_tabs,
             app_name=config.APP_NAME,
-            pg_libpq_version=pg_libpq_version
+            pg_libpq_version=pg_libpq_version,
+            support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL
         ),
         200, {'Content-Type': 'application/x-javascript'})
 
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 01c72e5..e615f9e 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -479,7 +479,13 @@ class ServerNode(PGChildNodeView):
             'sslcompression': 'sslcompression',
             'bgcolor': 'bgcolor',
             'fgcolor': 'fgcolor',
-            'service': 'service'
+            'service': 'service',
+            'use_ssh_tunnel': 'use_ssh_tunnel',
+            'tunnel_host': 'tunnel_host',
+            'tunnel_port': 'tunnel_port',
+            'tunnel_username': 'tunnel_username',
+            'tunnel_authentication': 'tunnel_authentication',
+            'tunnel_identity_file': 'tunnel_identity_file',
         }
 
         disp_lbl = {
@@ -665,7 +671,18 @@ class ServerNode(PGChildNodeView):
                 'sslcrl': server.sslcrl if is_ssl else None,
                 'sslcompression': True if is_ssl and server.sslcompression
                 else False,
-                'service': server.service if server.service else None
+                'service': server.service if server.service else None,
+                'use_ssh_tunnel': server.use_ssh_tunnel if server.use_ssh_tunnel
+                else 0,
+                'tunnel_host': server.tunnel_host if server.tunnel_host
+                else None,
+                'tunnel_port': server.tunnel_port if server.tunnel_port else 22,
+                'tunnel_username': server.tunnel_username
+                    if server.tunnel_username else None,
+                'tunnel_identity_file': server.tunnel_identity_file
+                    if server.tunnel_identity_file else None,
+                'tunnel_authentication': server.tunnel_authentication
+                    if server.tunnel_authentication else 0
             }
         )
 
@@ -736,7 +753,13 @@ class ServerNode(PGChildNodeView):
                 sslcompression=1 if is_ssl and data['sslcompression'] else 0,
                 bgcolor=data.get('bgcolor', None),
                 fgcolor=data.get('fgcolor', None),
-                service=data.get('service', None)
+                service=data.get('service', None),
+                use_ssh_tunnel=data.get('use_ssh_tunnel', 0),
+                tunnel_host=data.get('tunnel_host', None),
+                tunnel_port=data.get('tunnel_port', 22),
+                tunnel_username=data.get('tunnel_username', None),
+                tunnel_authentication=data.get('tunnel_authentication', 0),
+                tunnel_identity_file=data.get('tunnel_identity_file', None)
             )
             db.session.add(server)
             db.session.commit()
@@ -754,6 +777,7 @@ class ServerNode(PGChildNodeView):
                 have_password = False
                 password = None
                 passfile = None
+                tunnel_password = None
                 if 'password' in data and data["password"] != '':
                     # login with password
                     have_password = True
@@ -764,9 +788,14 @@ class ServerNode(PGChildNodeView):
                     setattr(server, 'passfile', passfile)
                     db.session.commit()
 
+                if 'tunnel_password' in data and data["tunnel_password"] != '':
+                    tunnel_password = data['tunnel_password']
+                    tunnel_password = encrypt(tunnel_password, current_user.password)
+
                 status, errmsg = conn.connect(
                     password=password,
                     passfile=passfile,
+                    tunnel_password=tunnel_password,
                     server_types=ServerType.types()
                 )
                 if hasattr(str, 'decode') and errmsg is not None:
@@ -877,10 +906,10 @@ class ServerNode(PGChildNodeView):
         res = conn.connected()
 
         if res:
-            from pgadmin.utils.exception import ConnectionLost
+            from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
             try:
                 conn.execute_scalar('SELECT 1')
-            except ConnectionLost:
+            except (ConnectionLost, SSHTunnelConnectionLost):
                 res = False
 
         return make_json_response(data={'connected': res})
@@ -924,28 +953,37 @@ class ServerNode(PGChildNodeView):
 
         password = None
         passfile = None
+        tunnel_password = None
         save_password = False
 
         # Connect the Server
         manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
         conn = manager.connection()
 
+        # If server using SSH Tunnel
+        if server.use_ssh_tunnel:
+            if 'tunnel_password' not in data:
+                return self.get_response_for_password(server, 428)
+            else:
+                tunnel_password = data['tunnel_password'] if 'tunnel_password' \
+                                                             in data else None
+                # Encrypt the password before saving with user's login
+                # password key.
+                try:
+                    tunnel_password = encrypt(tunnel_password, user.password) \
+                        if tunnel_password is not None else \
+                        server.tunnel_password
+                except Exception as e:
+                    current_app.logger.exception(e)
+                    return internal_server_error(errormsg=e.message)
+
         if 'password' not in data:
             conn_passwd = getattr(conn, 'password', None)
             if conn_passwd is None and server.password is None and \
                     server.passfile is None and server.service is None:
                 # Return the password template in case password is not
                 # provided, or password has not been saved earlier.
-                return make_json_response(
-                    success=0,
-                    status=428,
-                    result=render_template(
-                        'servers/password.html',
-                        server_label=server.name,
-                        username=server.username,
-                        _=gettext
-                    )
-                )
+                return self.get_response_for_password(server, 428)
             elif server.passfile and server.passfile != '':
                 passfile = server.passfile
             else:
@@ -969,22 +1007,13 @@ class ServerNode(PGChildNodeView):
             status, errmsg = conn.connect(
                 password=password,
                 passfile=passfile,
+                tunnel_password=tunnel_password,
                 server_types=ServerType.types()
             )
         except Exception as e:
             current_app.logger.exception(e)
-
-            return make_json_response(
-                success=0,
-                status=401,
-                result=render_template(
-                    'servers/password.html',
-                    server_label=server.name,
-                    username=server.username,
-                    errmsg=getattr(e, 'message', str(e)),
-                    _=gettext
-                )
-            )
+            return self.get_response_for_password(server, 401,
+                                                  getattr(e, 'message', str(e)))
 
         if not status:
             if hasattr(str, 'decode'):
@@ -995,17 +1024,7 @@ class ServerNode(PGChildNodeView):
                 .format(server.id, server.name, errmsg)
             )
 
-            return make_json_response(
-                success=0,
-                status=401,
-                result=render_template(
-                    'servers/password.html',
-                    server_label=server.name,
-                    username=server.username,
-                    errmsg=errmsg,
-                    _=gettext
-                )
-            )
+            return self.get_response_for_password(server, 401, errmsg)
         else:
             if save_password and config.ALLOW_SAVE_PASSWORD:
                 try:
@@ -1376,5 +1395,34 @@ class ServerNode(PGChildNodeView):
             )
             return internal_server_error(errormsg=str(e))
 
+    def get_response_for_password(self, server, status, errmsg=None):
+        if server.use_ssh_tunnel:
+            return make_json_response(
+                success=0,
+                status=status,
+                result=render_template(
+                    'servers/tunnel_password.html',
+                    server_label=server.name,
+                    username=server.username,
+                    tunnel_username=server.tunnel_username,
+                    tunnel_host=server.tunnel_host,
+                    tunnel_identity_file=server.tunnel_identity_file,
+                    errmsg=errmsg,
+                    _=gettext
+                )
+            )
+        else:
+            return make_json_response(
+                success=0,
+                status=status,
+                result=render_template(
+                    'servers/password.html',
+                    server_label=server.name,
+                    username=server.username,
+                    errmsg=errmsg,
+                    _=gettext
+                )
+            )
+
 
 ServerNode.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index 375b0b6..9bee2e6 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -669,6 +669,13 @@ define('pgadmin.node.server', [
           sslrootcert: undefined,
           sslcrl: undefined,
           service: undefined,
+          use_ssh_tunnel: 0,
+          tunnel_host: undefined,
+          tunnel_port: 22,
+          tunnel_username: undefined,
+          tunnel_identity_file: undefined,
+          tunnel_password: undefined,
+          tunnel_authentication: 0,
         },
         // Default values!
         initialize: function(attrs, args) {
@@ -695,8 +702,7 @@ define('pgadmin.node.server', [
         },{
           id: 'connected', label: gettext('Connected?'), type: 'switch',
           mode: ['properties'], group: gettext('Connection'), 'options': {
-            'onText':  gettext('True'), 'offText':  gettext('False'), 'onColor':  'success',
-            'offColor': 'danger', 'size': 'small',
+            'onText':  gettext('True'), 'offText':  gettext('False'), 'size': 'small',
           },
         },{
           id: 'version', label: gettext('Version'), type: 'text', group: null,
@@ -735,11 +741,29 @@ define('pgadmin.node.server', [
         },{
           id: 'save_password', controlLabel: gettext('Save password?'),
           type: 'checkbox', group: gettext('Connection'), mode: ['create'],
-          deps: ['connect_now'], visible: function(m) {
+          deps: ['connect_now', 'use_ssh_tunnel'], visible: function(m) {
             return m.get('connect_now') && m.isNew();
           },
-          disabled: function() {
-            return !current_user.allow_save_password;
+          disabled: function(m) {
+            if (!current_user.allow_save_password)
+              return true;
+
+            if (m.get('use_ssh_tunnel')) {
+              if (m.get('save_password')) {
+                Alertify.alert(
+                  gettext('Stored Password'),
+                  gettext('Database passwords cannot be stored when using SSH tunnelling. The \'Save password\' option has been turned off.')
+                );
+              }
+
+              setTimeout(function() {
+                m.set('save_password', false);
+              }, 10);
+
+              return true;
+            }
+
+            return false;
           },
         },{
           id: 'role', label: gettext('Role'), type: 'text', group: gettext('Connection'),
@@ -782,8 +806,7 @@ define('pgadmin.node.server', [
         },{
           id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
           mode: ['edit', 'create'], group: gettext('SSL'),
-          'options': { 'onText':   gettext('True'), 'offText':  gettext('False'),
-            'onColor':  'success', 'offColor': 'danger', 'size': 'small'},
+          'options': {'size': 'small'},
           deps: ['sslmode'], disabled: 'isSSL',
         },{
           id: 'sslcert', label: gettext('Client certificate'), type: 'text',
@@ -820,13 +843,77 @@ define('pgadmin.node.server', [
         },{
           id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
           mode: ['properties'], group: gettext('SSL'),
-          'options': { 'onText':  gettext('True'), 'offText':  gettext('False'),
-            'onColor':  'success', 'offColor': 'danger', 'size': 'small'},
+          'options': {'size': 'small'},
           deps: ['sslmode'], visible: function(m) {
             var sslmode = m.get('sslmode');
             return _.indexOf(SSL_MODES, sslmode) != -1;
           },
         },{
+          id: 'use_ssh_tunnel', label: gettext('Use SSH tunneling'), type: 'switch',
+          mode: ['properties', 'edit', 'create'], group: gettext('SSH Tunnel'),
+          'options': {'size': 'small'},
+          disabled: function(m) {
+            if (!pgAdmin.Browser.utils.support_ssh_tunnel) {
+              setTimeout(function() {
+                m.set('use_ssh_tunnel', 0);
+              }, 10);
+
+              return true;
+            }
+
+            return m.get('connected');
+          },
+        },{
+          id: 'tunnel_host', label: gettext('Tunnel host'), type: 'text', group: gettext('SSH Tunnel'),
+          mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'],
+          disabled: function(m) {
+            return !m.get('use_ssh_tunnel');
+          },
+        },{
+          id: 'tunnel_port', label: gettext('Tunnel port'), type: 'int', group: gettext('SSH Tunnel'),
+          mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'], max: 65535,
+          disabled: function(m) {
+            return !m.get('use_ssh_tunnel');
+          },
+        },{
+          id: 'tunnel_username', label: gettext('Username'), type: 'text', group: gettext('SSH Tunnel'),
+          mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'],
+          disabled: function(m) {
+            return !m.get('use_ssh_tunnel');
+          },
+        },{
+          id: 'tunnel_authentication', label: gettext('Authentication'), type: 'switch',
+          mode: ['properties', 'edit', 'create'], group: gettext('SSH Tunnel'),
+          'options': {'onText':  gettext('Identity file'),
+            'offText':  gettext('Password'), 'size': 'small'},
+          deps: ['use_ssh_tunnel'],
+          disabled: function(m) {
+            return !m.get('use_ssh_tunnel');
+          },
+        }, {
+          id: 'tunnel_identity_file', label: gettext('Identity file'), type: 'text',
+          group: gettext('SSH Tunnel'), mode: ['edit', 'create'],
+          control: Backform.FileControl, dialog_type: 'select_file', supp_types: ['*'],
+          deps: ['tunnel_authentication', 'use_ssh_tunnel'],
+          disabled: function(m) {
+            if (!m.get('tunnel_authentication') || !m.get('use_ssh_tunnel')) {
+              setTimeout(function() {
+                m.set('tunnel_identity_file', '');
+              }, 10);
+            }
+            return !m.get('tunnel_authentication');
+          },
+        },{
+          id: 'tunnel_identity_file', label: gettext('Identity file'), type: 'text',
+          group: gettext('SSH Tunnel'), mode: ['properties'],
+        },{
+          id: 'tunnel_password', label: gettext('Password/Passphrase'), type: 'password',
+          group: gettext('SSH Tunnel'), control: 'input', mode: ['create'],
+          deps: ['use_ssh_tunnel'],
+          disabled: function(m) {
+            return !m.get('use_ssh_tunnel');
+          },
+        }, {
           id: 'hostaddr', label: gettext('Host address'), type: 'text', group: gettext('Advanced'),
           mode: ['properties', 'edit', 'create'], disabled: 'isConnected',
         },{
diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html b/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html
new file mode 100644
index 0000000..ed0b68b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/templates/servers/tunnel_password.html
@@ -0,0 +1,28 @@
+<form name="frmPassword" id="frmPassword" style="height: 100%; width: 100%" onsubmit="return false;">
+    <div>{% if errmsg %}
+        <div class="highlight has-error">
+            <div class='control-label'>{{ errmsg }}</div>
+        </div>
+        {% endif %}
+        {% if tunnel_identity_file %}
+        <div><b>{{ _('SSH Tunnel password for the identity file \'{0}\' to connect the server "{1}"').format(tunnel_identity_file, tunnel_host) }}</b></div>
+        {% else %}
+        <div><b>{{ _('SSH Tunnel password for the user \'{0}\' to connect the server "{1}"').format(tunnel_username, tunnel_host) }}</b></div>
+        {% endif %}
+        <div style="padding: 5px; height: 1px;"></div>
+        <div style="width: 100%">
+            <span style="width: 97%;display: inline-block;">
+                <input style="width:100%" id="tunnel_password" class="form-control" name="tunnel_password" type="password">
+            </span>
+        </div>
+        <div style="padding: 5px; height: 1px;"></div>
+        <div><b>{{ _('Database server password for the user \'{0}\' to connect the server "{1}"').format(username, server_label) }}</b></div>
+        <div style="padding: 5px; height: 1px;"></div>
+        <div style="width: 100%">
+            <span style="width: 97%;display: inline-block;">
+                <input style="width:100%" id="password" class="form-control" name="password" type="password">
+            </span>
+        </div>
+        <div style="padding: 5px; height: 1px;"></div>
+    </div>
+</form>
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
new file mode 100644
index 0000000..cca9322
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
@@ -0,0 +1,62 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+
+
+class ServersWithSSHTunnelAddTestCase(BaseTestGenerator):
+    """ This class will add the servers under default server group. """
+
+    scenarios = [
+        (
+            'Add server using SSH tunnel with password', dict(
+                url='/browser/server/obj/',
+                with_password=True
+            )
+        ),
+        (
+            'Add server using SSH tunnel with identity file', dict(
+                url='/browser/server/obj/',
+                with_password=False
+            )
+        ),
+    ]
+
+    def setUp(self):
+        pass
+
+    def runTest(self):
+        """ This function will add the server under default server group."""
+        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
+        # Add service name in the config
+        self.server['use_ssh_tunnel'] = 1
+        self.server['tunnel_host'] = '127.0.0.1'
+        self.server['tunnel_port'] = 22
+        self.server['tunnel_username'] = 'user'
+        if self.with_password:
+            self.server['tunnel_authentication'] = 0
+        else:
+            self.server['tunnel_authentication'] = 1
+            self.server['tunnel_identity_file'] = 'pkey_rsa'
+
+        response = self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+        self.assertEquals(response.status_code, 200)
+        response_data = json.loads(response.data.decode('utf-8'))
+        self.server_id = response_data['node']['_id']
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js
index be3b712..79cf69a 100644
--- a/web/pgadmin/browser/templates/browser/js/utils.js
+++ b/web/pgadmin/browser/templates/browser/js/utils.js
@@ -26,6 +26,7 @@ define('pgadmin.browser.utils',
     is_indent_with_tabs: '{{ editor_indent_with_tabs }}' == 'True',
     app_name: '{{ app_name }}',
     pg_libpq_version: {{pg_libpq_version|e}},
+    support_ssh_tunnel: '{{ support_ssh_tunnel }}' == 'True',
 
     counter: {total: 0, loaded: 0},
     registerScripts: function (ctx) {
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 11bc9f0..784bc1e 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -145,6 +145,23 @@ class Server(db.Model):
     bgcolor = db.Column(db.Text(10), nullable=True)
     fgcolor = db.Column(db.Text(10), nullable=True)
     service = db.Column(db.Text(), nullable=True)
+    use_ssh_tunnel = db.Column(
+        db.Integer(),
+        db.CheckConstraint('use_ssh_tunnel >= 0 AND use_ssh_tunnel <= 1'),
+        nullable=False
+    )
+    tunnel_host = db.Column(db.String(128), nullable=True)
+    tunnel_port = db.Column(
+        db.Integer(),
+        db.CheckConstraint('port <= 65534'),
+        nullable=True)
+    tunnel_username = db.Column(db.String(64), nullable=True)
+    tunnel_authentication = db.Column(
+        db.Integer(),
+        db.CheckConstraint('tunnel_authentication >= 0 AND tunnel_authentication <= 1'),
+        nullable=False
+    )
+    tunnel_identity_file = db.Column(db.String(64), nullable=True)
 
 
 class ModulePreference(db.Model):
diff --git a/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
index f1627aa..c790eeb 100644
--- a/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
+++ b/web/pgadmin/static/js/browser/server_groups/servers/model_validation.js
@@ -43,6 +43,15 @@ export class ModelValidation {
     this.checkForEmpty('username', gettext('Username must be specified.'));
     this.checkForEmpty('port', gettext('Port must be specified.'));
 
+    if (this.model.get('use_ssh_tunnel')) {
+      this.checkForEmpty('tunnel_host', gettext('SSH Tunnel host must be specified.'));
+      this.checkForEmpty('tunnel_port', gettext('SSH Tunnel port must be specified.'));
+      this.checkForEmpty('tunnel_username', gettext('SSH Tunnel username must be specified.'));
+      if (this.model.get('tunnel_authentication')) {
+        this.checkForEmpty('tunnel_identity_file', gettext('SSH Tunnel identity file must be specified.'));
+      }
+    }
+
     this.model.errorModel.set(this.err);
 
     if (_.size(this.err)) {
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
index 9b18804..d3c3bf9 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -27,7 +27,7 @@ from config import PG_DEFAULT_DRIVER
 from pgadmin.utils.preferences import Preferences
 from pgadmin.model import Server
 from pgadmin.utils.driver import get_driver
-from pgadmin.utils.exception import ConnectionLost
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
 from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
     get_query_tool_keyboard_shortcuts, get_text_representation_of_shortcut
 
@@ -135,7 +135,7 @@ def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id):
                                   auto_reconnect=False,
                                   use_binary_placeholder=True,
                                   array_to_string=True)
-    except ConnectionLost as e:
+    except (ConnectionLost, SSHTunnelConnectionLost) as e:
         raise
     except Exception as e:
         app.logger.error(e)
@@ -363,7 +363,7 @@ def initialize_query_tool(sgid, sid, did=None):
                                   array_to_string=True)
         if connect:
             conn.connect()
-    except ConnectionLost as e:
+    except (ConnectionLost, SSHTunnelConnectionLost) as e:
         raise
     except Exception as e:
         app.logger.error(e)
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index fae527c..c72505a 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -34,7 +34,7 @@ from pgadmin.utils.ajax import make_json_response, bad_request, \
     success_return, internal_server_error, unauthorized
 from pgadmin.utils.driver import get_driver
 from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.exception import ConnectionLost
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
 from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
 from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
     RegisterQueryToolPreferences
@@ -166,7 +166,7 @@ def check_transaction_status(trans_id):
             use_binary_placeholder=True,
             array_to_string=True
         )
-    except ConnectionLost as e:
+    except (ConnectionLost, SSHTunnelConnectionLost) as e:
         raise
     except Exception as e:
         current_app.logger.error(e)
@@ -212,7 +212,7 @@ def start_view_data(trans_id):
         manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
             trans_obj.sid)
         default_conn = manager.connection(did=trans_obj.did)
-    except ConnectionLost as e:
+    except (ConnectionLost, SSHTunnelConnectionLost) as e:
         raise
     except Exception as e:
         current_app.logger.error(e)
@@ -261,7 +261,7 @@ def start_view_data(trans_id):
         # Execute sql asynchronously
         try:
             status, result = conn.execute_async(sql)
-        except ConnectionLost as e:
+        except (ConnectionLost, SSHTunnelConnectionLost) as e:
             raise
     else:
         status = False
diff --git a/web/pgadmin/tools/sqleditor/utils/start_running_query.py b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
index 6c09067..8c598ff 100644
--- a/web/pgadmin/tools/sqleditor/utils/start_running_query.py
+++ b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
@@ -25,7 +25,7 @@ from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
     update_session_grid_transaction
 from pgadmin.utils.ajax import make_json_response, internal_server_error
 from pgadmin.utils.driver import get_driver
-from pgadmin.utils.exception import ConnectionLost
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
 
 
 class StartRunningQuery:
@@ -61,7 +61,7 @@ class StartRunningQuery:
                                           auto_reconnect=False,
                                           use_binary_placeholder=True,
                                           array_to_string=True)
-            except ConnectionLost:
+            except (ConnectionLost, SSHTunnelConnectionLost):
                 raise
             except Exception as e:
                 self.logger.error(e)
@@ -127,7 +127,7 @@ class StartRunningQuery:
         # and formatted_error is True.
         try:
             status, result = conn.execute_async(sql)
-        except ConnectionLost:
+        except (ConnectionLost, SSHTunnelConnectionLost):
             raise
 
         # If the transaction aborted for some reason and
diff --git a/web/pgadmin/utils/driver/psycopg2/connection.py b/web/pgadmin/utils/driver/psycopg2/connection.py
index 8a05ba8..e86dcfe 100644
--- a/web/pgadmin/utils/driver/psycopg2/connection.py
+++ b/web/pgadmin/utils/driver/psycopg2/connection.py
@@ -215,6 +215,17 @@ class Connection(BaseConnection):
 
         encpass = kwargs['password'] if 'password' in kwargs else None
         passfile = kwargs['passfile'] if 'passfile' in kwargs else None
+        tunnel_password = kwargs['tunnel_password'] if 'tunnel_password' in kwargs else None
+
+        # Check SSH Tunnel needs to be created
+        if mgr.use_ssh_tunnel == 1 and tunnel_password is not None:
+            status, error = mgr.create_ssh_tunnel(tunnel_password)
+            if not status:
+                return False, error
+
+        # Check SSH Tunnel is alive or not.
+        if mgr.use_ssh_tunnel == 1:
+            mgr.check_ssh_tunnel_alive()
 
         if encpass is None:
             encpass = self.password or getattr(mgr, 'password', None)
@@ -240,6 +251,7 @@ class Connection(BaseConnection):
                     password = password.decode()
 
             except Exception as e:
+                mgr.stop_ssh_tunnel()
                 current_app.logger.exception(e)
                 return False, \
                     _(
@@ -268,9 +280,9 @@ class Connection(BaseConnection):
                 config.APP_NAME, conn_id)
 
             pg_conn = psycopg2.connect(
-                host=mgr.host,
-                hostaddr=mgr.hostaddr,
-                port=mgr.port,
+                host=mgr.local_bind_host if mgr.use_ssh_tunnel else mgr.host,
+                hostaddr=mgr.local_bind_host if mgr.use_ssh_tunnel else mgr.hostaddr,
+                port=mgr.local_bind_port if mgr.use_ssh_tunnel else mgr.port,
                 database=database,
                 user=user,
                 password=password,
@@ -291,6 +303,7 @@ class Connection(BaseConnection):
                 self._wait(pg_conn)
 
         except psycopg2.Error as e:
+            mgr.stop_ssh_tunnel()
             if e.pgerror:
                 msg = e.pgerror
             elif e.diag.message_detail:
@@ -317,6 +330,7 @@ class Connection(BaseConnection):
         try:
             status, msg = self._initialize(conn_id, **kwargs)
         except Exception as e:
+            mgr.stop_ssh_tunnel()
             current_app.logger.exception(e)
             self.conn = None
             if not self.reconnecting:
@@ -480,6 +494,12 @@ WHERE
         return True, None
 
     def __cursor(self, server_cursor=False):
+
+        # Check SSH Tunnel is alive or not. If used by the database
+        # server for the connection.
+        if self.manager.use_ssh_tunnel == 1:
+            self.manager.check_ssh_tunnel_alive()
+
         if self.wasConnected is False:
             raise ConnectionLost(
                 self.manager.sid,
@@ -1181,9 +1201,9 @@ WHERE
 
         try:
             pg_conn = psycopg2.connect(
-                host=mgr.host,
-                hostaddr=mgr.hostaddr,
-                port=mgr.port,
+                host=mgr.local_bind_host if mgr.use_ssh_tunnel else mgr.host,
+                hostaddr=mgr.local_bind_host if mgr.use_ssh_tunnel else mgr.hostaddr,
+                port=mgr.local_bind_port if mgr.use_ssh_tunnel else mgr.port,
                 database=self.db,
                 user=mgr.user,
                 password=password,
@@ -1456,9 +1476,12 @@ Failed to reset the connection to the server due to following error:
 
             try:
                 pg_conn = psycopg2.connect(
-                    host=self.manager.host,
-                    hostaddr=self.manager.hostaddr,
-                    port=self.manager.port,
+                    host=self.manager.local_bind_host if
+                        self.manager.use_ssh_tunnel else self.manager.host,
+                    hostaddr=self.manager.local_bind_host if
+                        self.manager.use_ssh_tunnel else self.manager.hostaddr,
+                    port=self.manager.local_bind_port if
+                        self.manager.use_ssh_tunnel else self.manager.port,
                     database=self.db,
                     user=self.manager.user,
                     password=password,
diff --git a/web/pgadmin/utils/driver/psycopg2/server_manager.py b/web/pgadmin/utils/driver/psycopg2/server_manager.py
index 3066b1a..3e27bbb 100644
--- a/web/pgadmin/utils/driver/psycopg2/server_manager.py
+++ b/web/pgadmin/utils/driver/psycopg2/server_manager.py
@@ -12,14 +12,19 @@ Implementation of ServerManager
 """
 import os
 import datetime
+import config
 from flask import current_app, session
 from flask_security import current_user
 from flask_babelex import gettext
 
+from pgadmin.utils import get_complete_file_path
 from pgadmin.utils.crypto import decrypt
 from .connection import Connection
-from pgadmin.model import Server
-from pgadmin.utils.exception import ConnectionLost
+from pgadmin.model import Server, User
+from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
+
+if config.SUPPORT_SSH_TUNNEL:
+    from sshtunnel import SSHTunnelForwarder, BaseSSHTunnelForwarderError
 
 
 class ServerManager(object):
@@ -32,6 +37,9 @@ class ServerManager(object):
 
     def __init__(self, server):
         self.connections = dict()
+        self.local_bind_host = '127.0.0.1'
+        self.local_bind_port = None
+        self.tunnel_object = None
 
         self.update(server)
 
@@ -66,6 +74,20 @@ class ServerManager(object):
         self.sslcrl = server.sslcrl
         self.sslcompression = True if server.sslcompression else False
         self.service = server.service
+        if config.SUPPORT_SSH_TUNNEL:
+            self.use_ssh_tunnel = server.use_ssh_tunnel
+            self.tunnel_host = server.tunnel_host
+            self.tunnel_port = server.tunnel_port
+            self.tunnel_username = server.tunnel_username
+            self.tunnel_authentication = server.tunnel_authentication
+            self.tunnel_identity_file = server.tunnel_identity_file
+        else:
+            self.use_ssh_tunnel = 0
+            self.tunnel_host = None
+            self.tunnel_port = 22
+            self.tunnel_username = None
+            self.tunnel_authentication = None
+            self.tunnel_identity_file = None
 
         for con in self.connections:
             self.connections[con]._release()
@@ -167,7 +189,11 @@ WHERE db.oid = {0}""".format(did))
                             ))
 
         if database is None:
-            raise ConnectionLost(self.sid, None, None)
+            # Check SSH Tunnel is alive or not.
+            if self.use_ssh_tunnel == 1:
+                self.check_ssh_tunnel_alive()
+            else:
+                raise ConnectionLost(self.sid, None, None)
 
         my_id = (u'CONN:{0}'.format(conn_id)) if conn_id is not None else \
             (u'DB:{0}'.format(database))
@@ -247,6 +273,9 @@ WHERE db.oid = {0}""".format(did))
                     self.connections.pop(conn_info['conn_id'])
 
     def release(self, database=None, conn_id=None, did=None):
+        # Stop the SSH tunnel if created.
+        self.stop_ssh_tunnel()
+
         if did is not None:
             if did in self.db_info and 'datname' in self.db_info[did]:
                 database = self.db_info[did]['datname']
@@ -332,3 +361,71 @@ WHERE db.oid = {0}""".format(did))
                 self.password, current_user.password
             ).decode()
             os.environ[str(env)] = password
+
+    def create_ssh_tunnel(self, tunnel_password):
+        """
+        This method is used to create ssh tunnel and update the IP Address and
+        IP Address and port to localhost and the local bind port return by the
+        SSHTunnelForwarder class.
+        :return: True if tunnel is successfully created else error message.
+        """
+        # Fetch Logged in User Details.
+        user = User.query.filter_by(id=current_user.id).first()
+        if user is None:
+            return False, gettext("Unauthorized request.")
+
+        try:
+            tunnel_password = decrypt(tunnel_password, user.password)
+            # Handling of non ascii password (Python2)
+            if hasattr(str, 'decode'):
+                tunnel_password = tunnel_password.decode('utf-8').encode('utf-8')
+            # password is in bytes, for python3 we need it in string
+            elif isinstance(tunnel_password, bytes):
+                tunnel_password = tunnel_password.decode()
+
+        except Exception as e:
+            current_app.logger.exception(e)
+            return False, "Failed to decrypt the SSH tunnel " \
+                          "password.\nError: {0}".format(str(e))
+
+        try:
+            # If authentication method is 1 then it uses identity file and password
+            if self.tunnel_authentication == 1:
+                self.tunnel_object = SSHTunnelForwarder(
+                    self.tunnel_host,
+                    ssh_username=self.tunnel_username,
+                    ssh_pkey=get_complete_file_path(self.tunnel_identity_file),
+                    ssh_private_key_password=tunnel_password,
+                    remote_bind_address=(self.host, self.port)
+                )
+            else:
+                self.tunnel_object = SSHTunnelForwarder(
+                    self.tunnel_host,
+                    ssh_username=self.tunnel_username,
+                    ssh_password=tunnel_password,
+                    remote_bind_address=(self.host, self.port)
+                )
+
+            self.tunnel_object.start()
+        except BaseSSHTunnelForwarderError as e:
+            current_app.logger.exception(e)
+            return False, "Failed to create the SSH tunnel." \
+                          "\nError: {0}".format(str(e))
+
+        # Update the port to communicate locally
+        self.local_bind_port = self.tunnel_object.local_bind_port
+
+        return True, None
+
+    def check_ssh_tunnel_alive(self):
+        # Check SSH Tunnel is alive or not. if it is not then
+        # raise the ConnectionLost exception.
+        if self.tunnel_object is None or not self.tunnel_object.is_active:
+            raise SSHTunnelConnectionLost(self.tunnel_host)
+
+    def stop_ssh_tunnel(self):
+        # Stop the SSH tunnel if created.
+        if self.tunnel_object and self.tunnel_object.is_active:
+            self.tunnel_object.stop()
+            self.local_bind_port = None
+            self.tunnel_object = None
diff --git a/web/pgadmin/utils/exception.py b/web/pgadmin/utils/exception.py
index 5fe2e92..62b7068 100644
--- a/web/pgadmin/utils/exception.py
+++ b/web/pgadmin/utils/exception.py
@@ -48,3 +48,35 @@ class ConnectionLost(HTTPException):
     def __repr__(self):
         return "Connection (id #{2}) lost for the server (#{0}) on " \
                "database ({1})".format(self.sid, self.db, self.conn_id)
+
+
+class SSHTunnelConnectionLost(HTTPException):
+    """
+    Exception when connection to SSH tunnel is lost
+    """
+
+    def __init__(self, _tunnel_host):
+        self.tunnel_host = _tunnel_host
+        HTTPException.__init__(self)
+
+    @property
+    def name(self):
+        return HTTP_STATUS_CODES.get(503, 'Service Unavailable')
+
+    def get_response(self, environ=None):
+        return service_unavailable(
+            _("Connection to the SSH Tunnel for host '{0}' has been lost. "
+              "Reconnect to the database server.").format(self.tunnel_host),
+            info="SSH_TUNNEL_CONNECTION_LOST",
+            data={
+                'tunnel_host': self.tunnel_host
+            }
+        )
+
+    def __str__(self):
+        return "Connection to the SSH Tunnel for host '{0}' has been lost. " \
+               "Reconnect to the database server".format(self.tunnel_host)
+
+    def __repr__(self):
+        return "Connection to the SSH Tunnel for host '{0}' has been lost. " \
+               "Reconnect to the database server".format(self.tunnel_host)
diff --git a/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js b/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js
index 85e51eb..57b8cf1 100644
--- a/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js
+++ b/web/regression/javascript/browser/server_groups/servers/model_validation_spec.js
@@ -67,7 +67,52 @@ describe('Server#ModelValidation', () => {
         });
       });
 
-
+      describe('SSH Tunnel parameters', () => {
+        beforeEach(() => {
+          model.isNew.and.returnValue(true);
+          model.allValues['name'] = 'some name';
+          model.allValues['username'] = 'some username';
+          model.allValues['port'] = 12345;
+          model.allValues['host'] = 'some host';
+          model.allValues['db'] = 'some db';
+          model.allValues['hostaddr'] = '1.1.1.1';
+          model.allValues['use_ssh_tunnel'] = 1;
+        });
+        it('sets the "SSH Tunnel host must be specified." error', () => {
+          model.allValues['tunnel_port'] = 22;
+          model.allValues['tunnel_username'] = 'user1';
+          expect(modelValidation.validate()).toBe('SSH Tunnel host must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            tunnel_host:'SSH Tunnel host must be specified.'
+          });
+        });
+        it('sets the "SSH Tunnel port must be specified." error', () => {
+          model.allValues['tunnel_host'] = 'host';
+          model.allValues['tunnel_username'] = 'user1';
+          expect(modelValidation.validate()).toBe('SSH Tunnel port must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            tunnel_port:'SSH Tunnel port must be specified.'
+          });
+        });
+        it('sets the "SSH Tunnel username be specified." error', () => {
+          model.allValues['tunnel_host'] = 'host';
+          model.allValues['tunnel_port'] = 22;
+          expect(modelValidation.validate()).toBe('SSH Tunnel username must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            tunnel_username:'SSH Tunnel username must be specified.'
+          });
+        });
+        it('sets the "SSH Tunnel identity file be specified." error', () => {
+          model.allValues['tunnel_host'] = 'host';
+          model.allValues['tunnel_port'] = 22;
+          model.allValues['tunnel_username'] = 'user1';
+          model.allValues['tunnel_authentication'] = 1;
+          expect(modelValidation.validate()).toBe('SSH Tunnel identity file must be specified.');
+          expect(model.errorModel.set).toHaveBeenCalledWith({
+            tunnel_identity_file:'SSH Tunnel identity file must be specified.'
+          });
+        });
+      });
     });
 
     describe('When no parameters are valid', () => {
