{"id":764,"date":"2020-04-20T20:06:37","date_gmt":"2020-04-21T01:06:37","guid":{"rendered":"https:\/\/hackarandas.com\/blog\/?p=764"},"modified":"2025-09-25T22:43:59","modified_gmt":"2025-09-26T04:43:59","slug":"secure-by-default-postgres-docker-container-for-development","status":"publish","type":"post","link":"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/","title":{"rendered":"Secure by Default Postgres Docker Container for Development"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/hackarandas.com\/blog\/wp-content\/uploads\/2020\/04\/postgres-mac-icon-150x150.jpg\" alt=\"\" width=\"150\" height=\"150\" class=\"alignleft size-thumbnail wp-image-771\" srcset=\"https:\/\/hackarandas.com\/blog\/wp-content\/uploads\/2020\/04\/postgres-mac-icon-150x150.jpg 150w, https:\/\/hackarandas.com\/blog\/wp-content\/uploads\/2020\/04\/postgres-mac-icon-300x300.jpg 300w, https:\/\/hackarandas.com\/blog\/wp-content\/uploads\/2020\/04\/postgres-mac-icon-768x768.jpg 768w, https:\/\/hackarandas.com\/blog\/wp-content\/uploads\/2020\/04\/postgres-mac-icon.jpg 1024w\" sizes=\"auto, (max-width: 150px) 100vw, 150px\" \/><br \/>\nIn this post I will explain how to provide a secure postgres server docker container. This is useful when developing certain applications, for example a Django application. You can only run a this script and it will automatically detect if an old version of the container exists, delete it and deploy a new one. Or just to deploy a quick and secure by default postgres docker container. The limit is your imagination!<\/p>\n<p>The files used in this post are part of my <a href=\"https:\/\/github.com\/ch0ks\/devops-tools\">DevOps Tools Github repository<\/a>. Please visit it and look around, you might get some good ideas from it.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 ez-toc-wrap-left counter-hierarchy ez-toc-counter ez-toc-transparent ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#problem\" >Problem<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#proposal\" >Proposal<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#requirements\" >Requirements<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#tldr\" >TL;DR<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#glossary\" >Glossary<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#what_is_postgressql\" >What is PostgresSQL?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#what_is_docker\" >What is Docker?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#what_is_pipenv\" >What is Pipenv?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#what_is_django\" >What is Django?<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#how_everything_come_together\" >How Everything Come Together?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/#sample_django_project\" >Sample Django Project<\/a><\/li><\/ul><\/nav><\/div>\n<h3><span class=\"ez-toc-section\" id=\"problem\"><\/span>Problem<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>I am developing a Django application and during the development I need to create a secure by default postgres docker container. I also need to quickly reset the db constantly for testing purposes.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"proposal\"><\/span>Proposal<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Create a script to automate the process and minimize human error.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"requirements\"><\/span>Requirements<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>This environment was built with:<\/p>\n<ul>\n<li>Python 3.6.8<\/li>\n<li>Pipenv version 11.9.0<\/li>\n<li>Docker version 19.03.8<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"tldr\"><\/span>TL;DR<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Quick and dirty way of doing this. Use on your own risk!<\/p>\n<pre lang=\"text\">$ mkdir djangoproject\n$ cd djangoproject \n$ pipenv --three\n$ pipenv shell\n$ pipenv install django dj-database-url django-heroku\n$ django-admin startproject myproject\n$ mv myproject myproject-delme\n$ mv  myproject-delme\/* .\n$ rm -fr myproject-delme \n$ python manage.py startapp myapp\n$ grep \"SECRET_KEY\" myproject\/settings.py | tr -d ' ' > .env \n$ curl -s https:\/\/raw.githubusercontent.com\/ch0ks\/devops-tools\/master\/files-misc\/django-sample-dburl-and-heroku-settings.py > myproject\/settings.py \n$ curl -s https:\/\/raw.githubusercontent.com\/ch0ks\/devops-tools\/master\/docker-secure-postgres.sh | sudo bash 2>&1 | tee \/dev\/stderr | egrep '(DATABASE_URL|PGPASSWORD)' >> .env\n$ exit\n$ pipenv shell\n$ python manage.py migrate\n$ python manage.py createsuperuser \n$ python manage.py runserver \n<\/pre>\n<p>Now point your browser to <a href=\"http:\/\/127.0.0.1:8000\/\">http:\/\/127.0.0.1:8000\/admin<\/a>. Profit!<\/p>\n<h2><span class=\"ez-toc-section\" id=\"glossary\"><\/span>Glossary<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>In this section you will find definitions and explanations for the technologies that we are going to use.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"what_is_postgressql\"><\/span>What is PostgresSQL?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>PostgreSQL, also known as Postgres, is a free and open-source relational database management system (RDBMS) emphasizing extensibility and SQL compliance. It was originally named POSTGRES, referring to its origins as a successor to the Ingres database developed at the University of California, Berkeley. In 1996, the project was renamed to PostgreSQL to reflect its support for SQL. After a review in 2007, the development team decided to keep the name PostgreSQL.<\/em><\/p>\n<p><em>&#8212; <a href=\"https:\/\/en.wikipedia.org\/wiki\/PostgreSQL\">Wikipedia entry on PostgresSQL<\/a>, April 20, 2020<\/em><\/p>\n<p>Website: <a href=\"https:\/\/www.postgresql.org\/\">https:\/\/www.postgresql.org\/<\/a><\/p>\n<h3><span class=\"ez-toc-section\" id=\"what_is_docker\"><\/span>What is Docker?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>Docker is a set of platform as a service (PaaS) products that uses OS-level virtualization to deliver software in packages called containers. Containers are isolated from one another and bundle their own software, libraries and configuration files; they can communicate with each other through well-defined channels. All containers are run by a single operating system kernel and therefore use fewer resources than virtual machines.<\/em><\/p>\n<p><em>&#8212; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Docker_(software)\">Wikipedia entry on Docker (software)<\/a>, April 20, 2020<\/em><\/p>\n<p>Website: <a href=\"https:\/\/www.docker.com\/\">https:\/\/www.docker.com\/<\/a>*<\/p>\n<h3><span class=\"ez-toc-section\" id=\"what_is_pipenv\"><\/span>What is Pipenv?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>Pipenv is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world. Windows is a first-class citizen, in our world.<\/em><\/p>\n<p><em>It automatically creates and manages a virtualenv for your projects, as well as adds\/removes packages from your Pipfile as you install\/uninstall packages. It also generates the ever-important Pipfile.lock, which is used to produce deterministic builds.<\/em><\/p>\n<p><em>&#8212; <a href=\"https:\/\/pipenv.pypa.io\/en\/latest\/\">Pipenv: Python Dev Workflow for Humans<\/a>, April 20, 2020<\/em><\/p>\n<p>Great guide to understand pyenv: https:\/\/realpython.com\/intro-to-pyenv\/<\/p>\n<h3><span class=\"ez-toc-section\" id=\"what_is_django\"><\/span>What is Django?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><em>Django, stylised as django is a Python-based free and open-source web framework, which follows the model-template-view (MTV) architectural pattern. It is maintained by the Django Software Foundation (DSF), an independent organization established as a 501(c)(3) non-profit.<\/em><\/p>\n<p><em>&#8212; <a href=\"https:\/\/en.wikipedia.org\/wiki\/Django_(web_framework)\">Wikipedia entry on Django (web framework)<\/a>, April 20, 2020<\/em><\/p>\n<p>Website: <a href=\"https:\/\/www.djangoproject.com\/\">https:\/\/www.djangoproject.com\/<\/a><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how_everything_come_together\"><\/span>How Everything Come Together?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>You can download the script from here: <a href=\"https:\/\/raw.githubusercontent.com\/ch0ks\/devops-tools\/master\/docker-secure-postgres.sh\">docker-secure-postgres.sh<\/a><\/p>\n<p>Let&#8217;s analyze it by parts. Here we can check which user is executing the script, if is it not <code>root<\/code> then it autoexecute itself with <code>sudo<\/code>.<\/p>\n<pre lang=\"text\">\n#!\/usr\/bin\/env bash\n#title          :Secure Postgress Docker Container for Development\n#description    :This is a script that I created to expedite the \n#        creation of a secure docker container during the \n#                development of a Django application\n#file_nam       :docker-secure-postgres.sh\n#author         :Adrian Puente Z.\n#date           :20200315\n#version        :1.0\n#bash_version   :GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)\n#==================================================================\n\nset -euo pipefail\n\n[ $(id -u) -ne 0 ] && echo \"Only root can do that! sudoing...\"\nif [ \"${EUID}\" != 0 ]; then sudo $(which ${0}) ${@}; exit; fi\n<\/pre>\n<p>At this point the script is now running as <code>root<\/code> so now it is defining the variables that will use during the execution. You should configure these variables if needed, for example is you want to use another user or name the database something else.<\/p>\n<p>Using Python it will generate two 32 character long strings using numbers, upper and lower case letter and symbols and assign them to the variables  variables <code>APPDBPASSWD<\/code> and <code>PGPASSWORD<\/code>. They will be used as the postgres user password and the django user database password. These passwords will be configured later in the script.<\/p>\n<pre lang=\"text\">\n## Configure these variables if needed\nAPPDBHOSTNAME=\"localhost\"\nAPPDBSRVPORT=\"5432\"\nAPPUSRNAME=\"appusr\"\nAPPDBNAME=\"appdb\"\nAPPDBPASSWD=$(python -c \"import random ; print(''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789%^&*(-_+)') for i in range(25)]))\")\nPGPASSWORD=$(python -c \"import random ; print(''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789%^&*(-_+)') for i in range(25)]))\")\nDOCKERSRVNAME=\"postgres-securesrv\"\nSQLFILE=$(mktemp)\nTrue=0\nFalse=1\n<\/pre>\n<p>The piece below checks if a copy of the container is running in the machine, if so it will stop it and delete it, otherwise continues with the execution of the script.<\/p>\n<pre lang=\"text\">\n# Destroys any old docker container with the same name.\nif docker ps -a | grep ${DOCKERSRVNAME} >\/dev\/null 2>&1 \nthen \n  echo \"Deleting existing ${DOCKERSRVNAME} container.\"\n  docker stop ${DOCKERSRVNAME} > \/dev\/null 2>&1\n  docker rm ${DOCKERSRVNAME} > \/dev\/null 2>&1\nfi\n<\/pre>\n<p>Then it will generate a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Self-signed_certificate\">self signed SSL certificate<\/a> to use it in the postgres server. You can comment the <code>openssl<\/code> commands and just add your own certificate by adding the server certificate as a file named <code>server.crt<\/code> and the key as a file named <code>server.key<\/code>.<\/p>\n<pre lang=\"text\">\n# You can comment this part and add your own certificates. Be sure\n# to copy them in this directory and to name them accordingly.\nopenssl req -new -text -passout pass:abcd -subj \/CN=${APPDBHOSTNAME} -out server.req -keyout privkey.pem\nopenssl rsa -in privkey.pem -passin pass:abcd -out server.key\nopenssl req -x509 -in server.req -text -key server.key -out server.crt\n## Setting the right permissions for the postgress user\nchmod 600 server.key\nchown 999:999 server.key\n<\/pre>\n<p>Once the pre-requisites are met then Docker will check if the <a href=\"https:\/\/hub.docker.com\/_\/postgres\/\">official container image for PostgreSQL<\/a> exists in the machine, if not it will download a copy from from <a href=\"https:\/\/hub.docker.com\/\">Docker Hub<\/a>. Once the image is in the machine it will configure a new postgres docker container by adding the certificates mentioned before and the postgres user password contained in the variable <code>PGPASSWORD<\/code>, the database base administrator user of the server.<\/p>\n<pre lang=\"text\">\ndocker run -d --name ${DOCKERSRVNAME} \\\n       -v \"${PWD}\/server.crt:\/var\/lib\/postgresql\/server.crt:ro\" \\\n       -v \"${PWD}\/server.key:\/var\/lib\/postgresql\/server.key:ro\" \\\n       -e POSTGRES_PASSWORD=${PGPASSWORD} \\\n       -p ${APPDBSRVPORT}:${APPDBSRVPORT} \\\n       postgres \\\n       -c ssl=on \\\n       -c ssl_cert_file=\/var\/lib\/postgresql\/server.crt \\\n       -c ssl_key_file=\/var\/lib\/postgresql\/server.key \n<\/pre>\n<p>Finally, once the configuration is completed, it will make six tries waiting five seconds in between each to detect if the postgres container is up and running.<\/p>\n<pre lang=\"text\">\necho \"Waiting for the container to initialize.\"\nFAILED=${True}\n# Waits up to 30 seconds for the container to initialize.\nfor ((i=0 ; i<6 ; i++))\ndo\n  sleep 5\n  if docker ps | grep ${DOCKERSRVNAME} > \/dev\/null 2>&1 \n  then\n    if  PGPASSWORD=\"${PGPASSWORD}\" \\\n      pg_isready -h ${APPDBHOSTNAME} \\\n             -p ${APPDBSRVPORT} \\\n             -U postgres\n    then\n      FAILED=${False}\n      break\n    fi\n  fi\ndone\n\nif [ ${FAILED} -eq ${True} ]\nthen\n  echo \"Container execution failed, showing the logs\"\n  docker logs ${DOCKERSRVNAME}\n  exit 1\nfi\n<\/pre>\n<p>Once it finds a functional connection it will connect as the dba using the password in the variable <code>PGPASSWORD<\/code> and configures:<\/p>\n<ul>\n<li>The app database name using the variable <code>APPDBNAME<\/code><\/li>\n<li>The app user using the variable <code>APPUSRNAME<\/code> <\/li>\n<li>The app user password using the variable <code>APPDBPASSWD<\/code><\/li>\n<\/ul>\n<p>Notice how it uses the postgres client command line <code>psql<\/code> and the password configured before reusing the password contained in the variable <code>PGPASSWORD<\/code>.<\/p>\n<pre lang=\"text\">\necho \"Creating sample database.\"\ncat > ${SQLFILE} << _END\nCREATE DATABASE ${APPDBNAME};\nCREATE USER ${APPUSRNAME} WITH PASSWORD '${APPDBPASSWD}';\nALTER ROLE ${APPUSRNAME} SET client_encoding TO 'utf8';\nALTER ROLE ${APPUSRNAME} SET default_transaction_isolation TO 'read committed';\nALTER ROLE ${APPUSRNAME} SET timezone TO 'UTC';\nGRANT ALL PRIVILEGES ON DATABASE ${APPDBNAME} TO ${APPUSRNAME};\n_END\n\n# Creating the sample database.\nPGPASSWORD=\"${PGPASSWORD}\" psql -h ${APPDBHOSTNAME} -U postgres -f ${SQLFILE}\nrm -fr ${SQLFILE}\n\necho \"Sample database created successfully\"\necho -en \"Save both strings below in your .env file and restart the pipenv environment.\\n\\n\"\necho \"DATABASE_URL=\\\"postgres:\/\/${APPUSRNAME}:${APPDBPASSWD}@${APPDBHOSTNAME}:${APPDBSRVPORT}\/${APPDBNAME}\\\"\"\necho \"PGPASSWORD=\\\" ${PGPASSWORD}\\\"\"\nexit 0\n<\/pre>\n<p>Once all is done it will show you both password in the right format for you to add them to your environmental variables.<\/p>\n<pre lang=\"text\">\nDATABASE_URL=\"postgres:\/\/appusr:gz)i+jq5xwr0^(3vc-jpbg6t3@localhost:5432\/appdb\"\nPGPASSWORD=\"a^-mu-*gfm70mrl&nwh5ci_(a\"\n<\/pre>\n<p>In this case I am using pipenv and I will add them to the <code>.env<\/code> file. Have in mind that the <code>DATABASE_URL<\/code> variable follows the database url that is a platform independent way of addressing a database. A database URL is of the form <code>service:\/\/[user]:[password]@[hostname]:[port]\/[databasename]<\/code>. I also recommend adding the libraries <a href=\"https:\/\/pypi.org\/project\/dj-database-url\/\">dj-database-url<\/a> and <a href=\"https:\/\/github.com\/heroku\/django-heroku\">django_heroku<\/a> to your python projects to use this format.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"sample_django_project\"><\/span>Sample Django Project<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let's play a little with this new script. First let's create a proper development environment with pipenv:<\/p>\n<pre lang=\"text\">~\/github\n$ mkdir django-sample\n~\/github\n$ cd django-sample\n~\/github\/django-sample\n$ pipenv --three\nCreating a virtualenv for this project\u2026\nUsing ~\/.pyenv\/versions\/3.6.8\/bin\/python3 (3.6.8) to create virtualenv\u2026\n?Running virtualenv with interpreter ~\/.pyenv\/versions\/3.6.8\/bin\/python3\nAlready using interpreter ~\/.pyenv\/versions\/3.6.8\/bin\/python3\nUsing base prefix '~\/.pyenv\/versions\/3.6.8'\nNew python executable in ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/python3\nAlso creating executable in ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/python\nInstalling setuptools, pip, wheel...\ndone.\n\nVirtualenv location: ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\nCreating a Pipfile for this project\u2026\n~\/github\/django-sample\n$ pipenv shell\nSpawning environment shell (\/usr\/bin\/zsh). Use 'exit' to leave.\nOK\n. ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/activate\n. ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/activate\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Now let's install the django module and the dj-database-url library:<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ pipenv install django dj-database-url django_heroku\nInstalling django\u2026\n-----8<----------8<----------8<----------8<----------8<-----\n----->8---------->8---------->8---------->8---------->8-----\nInstalling collected packages: sqlparse, pytz, asgiref, django\nSuccessfully installed asgiref-3.2.7 django-3.0.5 pytz-2019.3 sqlparse-0.3.1\n\nInstalling django_heroku\u2026\nLooking in indexes: https:\/\/pypi.python.org\/simple\nCollecting django_heroku\n  Downloading django_heroku-0.3.1-py2.py3-none-any.whl (6.2 kB)\n-----8<----------8<----------8<----------8<----------8<-----\n----->8---------->8---------->8---------->8---------->8-----\nInstalling collected packages: psycopg2, whitenoise, django-heroku\nSuccessfully installed django-heroku-0.3.1 psycopg2-2.8.5 whitenoise-5.0.1\n\nAdding django to Pipfile's [packages]\u2026\nInstalling dj-database-url\u2026\n-----8<----------8<----------8<----------8<----------8<-----\n----->8---------->8---------->8---------->8---------->8----- (5.5 kB)\nInstalling collected packages: dj-database-url\nSuccessfully installed dj-database-url-0.5.0\n\nAdding dj-database-url to Pipfile's [packages]\u2026\nPipfile.lock not found, creating\u2026\nLocking [dev-packages] dependencies\u2026\nLocking [packages] dependencies\u2026\nUpdated Pipfile.lock (9a4335)!\nInstalling dependencies from Pipfile.lock (9a4335)\u2026\n  ####   ######################################## 5\/5 \u2014 00:00:00\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Now let's create the django project and it's application<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ ls \nPipfile  Pipfile.lock\n~\/github\/django-sample\n$ django-admin startproject myproject\n~\/github\/django-sample\n$ ls\nmyproject  Pipfile  Pipfile.lock\n~\/github\/django-sample\n$ mv myproject myproject-delme \n~\/github\/django-sample\n$ mv myproject-delme\/* .\n~\/github\/django-sample\n$ rm -fr myproject-delme \n~\/github\/django-sample\n$ ls\nmanage.py  myproject  Pipfile  Pipfile.lock\n~\/github\/django-sample\n$ python manage.py startapp myapp\n~\/github\/django-sample\n$ ls\nmanage.py  myapp  myproject  Pipfile  Pipfile.lock\n~\/github\/django-sample\n$  \n<\/pre>\n<p>I know it looks confusing so let me try to explain. After installing the django module and the dj-database-url library I created a new project using django-admin startproject myproject. This will create a new directory named myproject with all the configuration files. I like to move these files to the current working directory to avoid confusions then using python manage.py startapp myapp to create the application. You can see that the command creates another directory named myapp that contains all the files needed for the application. It is important to have the project and application directory at the same level than the manage.py file or it won't work.<\/p>\n<p>This is the final tree directory:<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ tree\n.\n|-- manage.py\n|-- myapp\n|   |-- admin.py\n|   |-- apps.py\n|   |-- __init__.py\n|   |-- migrations\n|   |   `-- __init__.py\n|   |-- models.py\n|   |-- tests.py\n|   `-- views.py\n|-- myproject\n|   |-- asgi.py\n|   |-- __init__.py\n|   |-- settings.py\n|   |-- urls.py\n|   `-- wsgi.py\n|-- Pipfile\n`-- Pipfile.lock\n\n\n3 directories, 15 files\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Now let's create the postgres database:<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ sudo bash docker-secure-postgres.sh\nDeleting existing postgres-securesrv container.\nGenerating a RSA private key\n..........................................................................+++++\n...............................................................+++++\nwriting new private key to 'privkey.pem'\n-----\nwriting RSA key\n37f96111773e465bb9d02b52098101c72cff3cc3c1fa92e0f01cc3afa1451cbe\nWaiting for the container to initialize.\nlocalhost:5432 - rejecting connections\nlocalhost:5432 - accepting connections\nCreating sample database.\nCREATE DATABASE?p=755#how-everything-comes-together\nCREATE ROLE\nALTER ROLE\nALTER ROLE\nALTER ROLE\nGRANT\nSample database created successfully\nSave both strings below in your .env file and restart the pipenv environment.\n\nDATABASE_URL=\"postgres:\/\/appusr:+x6odg_mrmvt+ktnd35_9-795@localhost:5432\/appdb\"\nPGPASSWORD=\"1%isf7s9u7xqqplzqwk)wt9z0\"\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Save the DATABASE_URL and the PGPASSWORD variables, we will use them later.<\/p>\n<p>Finally we will configure the environment for Django to work as expected. Follow these steps:<\/p>\n<ol>\n<li>At the top of the file myproject\/settings.py file add the following: <\/li>\n<\/ol>\n<pre lang=\"text\">import os &lt;--- After this library\nimport dj_database_url\nimport django_heroku\n\n###################################\n## Code and other configurations ##\n###################################\n\n## At the very bottom of the file\nSTATIC_URL = '\/static\/' &lt;--- After this value\n\ndjango_heroku.settings(locals())\n<\/pre>\n<ol>\n<li>In the same file look for the variable SECRET_KEY, save it somewhere else and delete it from the file<\/li>\n<li>In the same file also look for the DATABASE variable and change it like this:<\/li>\n<\/ol>\n<pre lang=\"text\"># Database\n# https:\/\/docs.djangoproject.com\/en\/3.0\/ref\/settings\/#databases\n\n#DATABASES = {\n#    'default': {\n#        'ENGINE': 'django.db.backends.sqlite3',\n#        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),\n#    }\n#}\n\nDATABASES = {\n    'default': dj_database_url.config()\n}\n<\/pre>\n<ol>\n<li>Add the variables DATABASE_URL, PGPASSWORD and SECRET_KEY to the file .env (if it does not exist create it), it should look like this:<\/li>\n<\/ol>\n<pre lang=\"text\">~\/github\/django-sample\n$ cat .env \nDATABASE_URL=\"postgres:\/\/appusr:+x6odg_mrmvt+ktnd35_9-795@localhost:5432\/appdb\"\nPGPASSWORD=\"1%isf7s9u7xqqplzqwk)wt9z0\"\nSECRET_KEY=\"cj4k*%2y%7nz&)3chs*%+ti&o40l)l)jm*^4zk)pkp7tt)cqfn\"\n\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Now restart the python environment. This is important or the environmental variables won't be taken into account. Do it like this:<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ exit\n~\/github\/django-sample\n$ pipenv shell\nLoading .env environment variables\u2026\nSpawning environment shell (\/usr\/bin\/zsh). Use 'exit' to leave.\nOK\n. ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/activate\n. ~\/.local\/share\/virtualenvs\/django-sample-2uB5phZ-\/bin\/activate\n~\/github\/django-sample\n$ env | egrep '(PASS|KEY|URL)'\nDATABASE_URL=postgres:\/\/appusr:+x6odg_mrmvt+ktnd35_9-795@localhost:5432\/appdb\nPGPASSWORD=1%isf7s9u7xqqplzqwk)wt9z0\nSECRET_KEY=7f4d%*zn1f5muug2(eu118++-cm)98gy\n~\/github\/django-sample\n$ \n<\/pre>\n<p>Perfect! At this point you should be able to start your project and initialize your database.<\/p>\n<pre lang=\"text\">~\/github\/django-sample\n$ python manage.py migrate \nOperations to perform:\n  Apply all migrations: admin, auth, contenttypes, sessions\nRunning migrations:\n  Applying contenttypes.0001_initial... OK\n  Applying auth.0001_initial... OK\n  Applying admin.0001_initial... OK\n  Applying admin.0002_logentry_remove_auto_add... OK\n  Applying admin.0003_logentry_add_action_flag_choices... OK\n  Applying contenttypes.0002_remove_content_type_name... OK\n  Applying auth.0002_alter_permission_name_max_length... OK\n  Applying auth.0003_alter_user_email_max_length... OK\n  Applying auth.0004_alter_user_username_opts... OK\n  Applying auth.0005_alter_user_last_login_null... OK\n  Applying auth.0006_require_contenttypes_0002... OK\n  Applying auth.0007_alter_validators_add_error_messages... OK\n  Applying auth.0008_alter_user_username_max_length... OK\n  Applying auth.0009_alter_user_last_name_max_length... OK\n  Applying auth.0010_alter_group_name_max_length... OK\n  Applying auth.0011_update_proxy_permissions... OK\n  Applying sessions.0001_initial... OK\n~\/github\/django-sample\n$ python manage.py createsuperuser \nUsername (leave blank to use 'apuente'): apuente\nEmail address: nospam@noserver.com\nPassword: \nPassword (again): \nSuperuser created successfully.\n~\/github\/django-sample\n$ psql $(echo ${DATABASE_URL}) -c \"select id,is_superuser,username,email from auth_user\" \n id | is_superuser | username |          email          \n----+--------------+----------+-------------------------\n  1 | t            | apuente  | nospam@noserver.com\n(1 row)\n~\/github\/django-sample\n$ python manage.py runserver\nWatching for file changes with StatReloader\nPerforming system checks...\n\nSystem check identified no issues (0 silenced).\nApril 20, 2020 - 23:24:35\nDjango version 3.0.5, using settings 'myproject.settings'\nStarting development server at http:\/\/127.0.0.1:8000\/\nQuit the server with CONTROL-C.\n<\/pre>\n<p>Now point your browser to <a href=\"http:\/\/127.0.0.1:8000\/\">http:\/\/127.0.0.1:8000\/admin<\/a>. Profit!<\/p>\n\n<div style=\"font-size: 0px; height: 0px; line-height: 0px; margin: 0; padding: 0; clear: both;\"><\/div>","protected":false},"excerpt":{"rendered":"<p>In this post I will explain how to provide a secure postgres server docker container. This is useful when developing certain applications, for example a Django application. You can only run a this script and it will automatically detect if &hellip; <a href=\"https:\/\/hackarandas.com\/blog\/2020\/04\/20\/secure-by-default-postgres-docker-container-for-development\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,95,94,84,96,4],"tags":[],"class_list":["post-764","post","type-post","status-publish","format-standard","hentry","category-code","category-databases","category-devops","category-docker","category-postgres","category-security"],"_links":{"self":[{"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/posts\/764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/comments?post=764"}],"version-history":[{"count":20,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/posts\/764\/revisions"}],"predecessor-version":[{"id":795,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/posts\/764\/revisions\/795"}],"wp:attachment":[{"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/media?parent=764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/categories?post=764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hackarandas.com\/blog\/wp-json\/wp\/v2\/tags?post=764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}