Search code examples
pythondjangossh-tunnel

django db with ssh tunnel


Is there a python native way to connect django to a database through an ssh tunnel? I have seen people using ssh port forwarding in the host machine but I would prefer a solution that can be easily containerized.


Solution

  • It is pretty seamless.

    Requirements: The sshtunnel package https://github.com/pahaz/sshtunnel

    1. In the django settings.py create an ssh tunnel before the django DB settings block:
    from sshtunnel import SSHTunnelForwarder
    
    # Connect to a server using the ssh keys. See the sshtunnel documentation for using password authentication
    ssh_tunnel = SSHTunnelForwarder(
        SERVER_IP,
        ssh_private_key=PATH_TO_SSH_PRIVATE_KEY,
        ssh_private_key_password=SSH_PRIVATE_KEY_PASSWORD,
        ssh_username=SSH_USERNAME,
        remote_bind_address=('localhost', LOCAL_DB_PORT_ON_THE_SERVER),
    )
    ssh_tunnel.start()
    
    1. Then add the DB info block in the settings.py. Here I am adding a default local DB and the remote DB that we connect to using the ssh tunnel
    DATABASES = {
        'default': {
            'ENGINE': 'django.contrib.gis.db.backends.postgis',
            'HOST': NORMAL_DB_HOST,
            'PORT': NORMAL_DB_PORT,
            'NAME': NORMAL_DB_NAME,
            'USER': NORMAL_DB_USER,
            'PASSWORD': NORMAL_DB_PASSWORD,
        },
        'shhtunnel_db': {
            'ENGINE': 'django.contrib.gis.db.backends.postgis',
            'HOST': 'localhost',
            'PORT': ssh_tunnel.local_bind_port,
            'NAME': REMOTE_DB_DB_NAME,
            'USER': REMOTE_DB_USERNAME,
            'PASSWORD': REMOTE_DB_PASSWORD,
        },
    }
    

    That is it. Now one can make migratations to the remote db using commands like $ python manage.py migrate --database=shhtunnel_db or make calls to the db from within the python code using lines like Models.objects.all().using('shhtunnel_db')

    Extra: In my case the remote db was created by someone else and I only wanted to read it. In order to avoid writing the models and deactivating the model manager I used the following django command to get the models from the database [src]:

    python manage.py inspectdb