├── .gitignore ├── LICENSE ├── README.md ├── flask-deploy ├── 01-our-deployment │ └── tbd.md ├── 11-deployment-automation │ └── tbd.md ├── 12-servers │ └── tbd.md ├── 13-operating-system │ └── tbd.md ├── 14-web-server │ └── tbd.md ├── 15-source-control │ └── tbd.md ├── 16-databases │ └── tbd.md ├── 17-app-dependencies │ └── tbd.md ├── 18-wsgi-server │ └── tbd.md ├── 19-task-queue │ └── tbd.md ├── 20-continuous-integration │ └── tbd.md └── README.md └── intro-ansible ├── 01-introduction ├── deploy.yml ├── hosts └── roles │ └── common │ └── tasks │ └── main.yml ├── 04-running-playbooks ├── hosts ├── playbook.yml ├── roles │ └── common │ │ └── tasks │ │ ├── main.yml │ │ ├── new_user.yml │ │ └── ping.yml └── run_playbook.sh ├── 05-storing-securing-data ├── group_vars │ └── all ├── hosts ├── playbook.yml └── roles │ └── common │ ├── tasks │ ├── main.yml │ ├── new_user.yml │ ├── ping.yml │ └── write_template.yml │ └── templates │ └── example.template.j2 ├── 06-configuring-servers ├── group_vars │ └── all ├── hosts ├── init_config.yml ├── roles │ ├── common │ │ └── tasks │ │ │ ├── main.yml │ │ │ └── security.yml │ ├── database │ │ └── tasks │ │ │ ├── main.yml │ │ │ ├── postgresql.yml │ │ │ └── security.yml │ ├── init_config │ │ └── tasks │ │ │ └── main.yml │ └── webserver │ │ ├── tasks │ │ ├── main.yml │ │ ├── nginx.yml │ │ └── security.yml │ │ └── templates │ │ └── nginx_conf.j2 └── webanddatabase.yml ├── 07-web-app-deploys ├── group_vars │ └── all ├── hosts ├── init_config.yml ├── roles │ ├── common │ │ └── tasks │ │ │ ├── main.yml │ │ │ └── security.yml │ ├── database │ │ └── tasks │ │ │ ├── main.yml │ │ │ ├── postgresql.yml │ │ │ └── security.yml │ ├── init_config │ │ └── tasks │ │ │ └── main.yml │ └── webserver │ │ ├── handlers │ │ └── main.yml │ │ ├── tasks │ │ ├── dependencies.yml │ │ ├── git.yml │ │ ├── main.yml │ │ ├── nginx.yml │ │ ├── security.yml │ │ └── wsgi.yml │ │ └── templates │ │ ├── nginx_conf.j2 │ │ └── supervisor_app.conf.j2 └── webanddatabase.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | */*/deploy.retry 103 | */*.swp 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Full Stack Python 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Full Stack Python books & videos code 2 | Code from Full Stack Python books, such as Deploying Flask Web Applications 3 | and Introduction to Ansible. 4 | -------------------------------------------------------------------------------- /flask-deploy/01-our-deployment/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/01-our-deployment/tbd.md -------------------------------------------------------------------------------- /flask-deploy/11-deployment-automation/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/11-deployment-automation/tbd.md -------------------------------------------------------------------------------- /flask-deploy/12-servers/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/12-servers/tbd.md -------------------------------------------------------------------------------- /flask-deploy/13-operating-system/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/13-operating-system/tbd.md -------------------------------------------------------------------------------- /flask-deploy/14-web-server/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/14-web-server/tbd.md -------------------------------------------------------------------------------- /flask-deploy/15-source-control/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/15-source-control/tbd.md -------------------------------------------------------------------------------- /flask-deploy/16-databases/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/16-databases/tbd.md -------------------------------------------------------------------------------- /flask-deploy/17-app-dependencies/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/17-app-dependencies/tbd.md -------------------------------------------------------------------------------- /flask-deploy/18-wsgi-server/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/18-wsgi-server/tbd.md -------------------------------------------------------------------------------- /flask-deploy/19-task-queue/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/19-task-queue/tbd.md -------------------------------------------------------------------------------- /flask-deploy/20-continuous-integration/tbd.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackpython/book-and-video-code-examples/3283912950f9b4876e901e0a054b666a5ae6d59e/flask-deploy/20-continuous-integration/tbd.md -------------------------------------------------------------------------------- /flask-deploy/README.md: -------------------------------------------------------------------------------- 1 | # Deploying Flask Web Applications 2 | This repository is the code companion to 3 | [Deploying Flask Web Applications](https://www.deploypython.com/) 4 | book. 5 | 6 | The book is currently [available on Gumroad](https://gumroad.com/l/WOvyt)! 7 | 8 | 9 | ## Subdirectories 10 | The following links take you to each chapter’s corresponding tag as the 11 | book builds up the automation scripts progressively: 12 | 13 | * [01-our-deployment](./01-our-deployment): a stub directory for your SSH keys 14 | * [11-deployment-automation](./11-deployment-automation): a stub directory for your new SSH keys 15 | * [12-servers](./12-servers): initial Ansible playbook files 16 | * [13-operating-system](./13-operating-system): add system packages to playbook 17 | * [14-web-server](./14-web-server): builds Nginx configuration into the Ansible playbook 18 | * [15-source-control](./15-source-control): adds Git repository cloning to the playbook 19 | * [16-databases](./16-databases): sets up PostgreSQL and Redis 20 | * [17-app-dependencies](./17-app-dependencies): installs Python packages into a virtualenv, establishes environment variables and syncs the app to the database 21 | * [18-wsgi-server](./18-wsgi-server): configures Supervisor to run the app with Green Unicorn 22 | * [19-task-queue](./19-task-queue): adds to the Supervisor configuration for Celery and Redis 23 | * [20-continuous-integration](./20-continuous-integration): modifies the Ansible playbook so it does not prompt for SUDO password for CI server 24 | -------------------------------------------------------------------------------- /intro-ansible/01-introduction/deploy.yml: -------------------------------------------------------------------------------- 1 | # High level instructions for which tasks apply to which hosts 2 | - name: apply local configuration to servers listed in hosts 3 | hosts: all 4 | user: root 5 | roles: 6 | - common 7 | -------------------------------------------------------------------------------- /intro-ansible/01-introduction/hosts: -------------------------------------------------------------------------------- 1 | [common] 2 | 206.189.233.46 ansible_python_interpreter=/usr/bin/python3 3 | -------------------------------------------------------------------------------- /intro-ansible/01-introduction/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # create directory on remote system if it doesn't exist 2 | - file: 3 | path: /home/matt 4 | state: directory 5 | mode: 0755 6 | 7 | - file: 8 | path: /home/matt/hello-ansible.md 9 | state: touch 10 | mode: 0655 11 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/hosts: -------------------------------------------------------------------------------- 1 | [common] 2 | 142.93.119.85 ansible_python_interpreter=/usr/bin/python3 3 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/playbook.yml: -------------------------------------------------------------------------------- 1 | # High level instructions for which tasks apply to which hosts 2 | - name: apply local configuration to servers listed in hosts 3 | hosts: all 4 | user: root 5 | roles: 6 | - common 7 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # include other YAML files with tasks here 2 | - include: ping.yml 3 | - include: new_user.yml 4 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/roles/common/tasks/new_user.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # establishes non-root user with sudo privileges 3 | ## 4 | - name: Run equivalent of "apt-get update" 5 | apt: 6 | update_cache: yes 7 | 8 | - name: create a non-root group 9 | group: 10 | name: "deployers" 11 | state: present 12 | 13 | - name: create non-root user 14 | user: 15 | name: "deployer" 16 | group: "deployers" 17 | shell: "/bin/bash" 18 | state: present 19 | 20 | - name: add authorized_key to non-root user 21 | authorized_key: 22 | user: deployer 23 | state: present 24 | key: "{{ lookup('file', '/home/matt/first_playbook/first_playbook.pub') }}" 25 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/roles/common/tasks/ping.yml: -------------------------------------------------------------------------------- 1 | # entry point for common playbook 2 | - name: ping remote server 3 | ping: 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /intro-ansible/04-running-playbooks/run_playbook.sh: -------------------------------------------------------------------------------- 1 | ansible-playbook -i ./hosts --private-key=./first_playbook playbook.yml 2 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/group_vars/all: -------------------------------------------------------------------------------- 1 | deploy_group: deployers 2 | deploy_user: deployer 3 | ssh_dir: "/home/matt/" 4 | ssh_key_name: "first_playbook" 5 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/hosts: -------------------------------------------------------------------------------- 1 | [common] 2 | 142.93.119.85 ansible_python_interpreter=/usr/bin/python3 3 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/playbook.yml: -------------------------------------------------------------------------------- 1 | # High level instructions for which tasks apply to which hosts 2 | - name: apply local configuration to servers listed in hosts 3 | hosts: all 4 | user: root 5 | roles: 6 | - common 7 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # include other YAML files with tasks here 2 | - include: ping.yml 3 | - include: new_user.yml 4 | - include: write_template.yml 5 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/roles/common/tasks/new_user.yml: -------------------------------------------------------------------------------- 1 | ### 2 | # establishes non-root user 3 | ## 4 | - name: Run equivalent of "apt-get update" 5 | apt: 6 | update_cache: yes 7 | 8 | - name: create a non-root group 9 | group: 10 | name: "{{ deploy_group }}" 11 | state: present 12 | 13 | - name: create non-root user 14 | user: 15 | name: "deployer" 16 | group: "deployers" 17 | shell: "/bin/bash" 18 | state: present 19 | 20 | - name: add authorized_key to non-root user 21 | authorized_key: 22 | user: deployer 23 | state: present 24 | key: "{{ lookup('file', '/home/matt/first_playbook/first_playbook.pub') }}" 25 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/roles/common/tasks/ping.yml: -------------------------------------------------------------------------------- 1 | # entry point for common playbook 2 | - name: ping remote server 3 | ping: 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/roles/common/tasks/write_template.yml: -------------------------------------------------------------------------------- 1 | # writes an example template file 2 | - name: write example template file 3 | template: src=example.template.j2 4 | dest=/home/{{ deploy_user }}/our_example_template 5 | -------------------------------------------------------------------------------- /intro-ansible/05-storing-securing-data/roles/common/templates/example.template.j2: -------------------------------------------------------------------------------- 1 | This is an example template. We set up a user named {{ deploy_user }} 2 | and a group named {{ deploy_group }}. 3 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/group_vars/all: -------------------------------------------------------------------------------- 1 | deploy_user: deployer 2 | deploy_group: deployers 3 | ssh_dir: "/home/matt/serverconfig/" 4 | ssh_key_name: "serverconfig" 5 | 6 | db_name: "chapter6" 7 | db_user: "{{ deploy_user }}" 8 | db_password: pleaseuseagoodpassword 9 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/hosts: -------------------------------------------------------------------------------- 1 | [init_config] 2 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 3 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 4 | 5 | [common] 6 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 7 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 8 | 9 | [webserver] 10 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 11 | 12 | [database] 13 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 14 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/init_config.yml: -------------------------------------------------------------------------------- 1 | # sets up a non-root user and group 2 | - name: apply initial config to server 3 | hosts: init_config 4 | user: root 5 | roles: 6 | - init_config 7 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration regardless of server type 2 | - include: security.yml 3 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/common/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: ensure python packages are installed 3 | apt: 4 | name: "{{ item }}" 5 | update_cache: yes 6 | become: yes 7 | with_items: 8 | - "python3-pip" 9 | - "python3-dev" 10 | - "fail2ban" 11 | 12 | 13 | - name: enable SSH within the firewall 14 | ufw: rule=allow port=22 15 | become: yes 16 | 17 | - name: enable firewall itself 18 | ufw: state=enabled 19 | become: yes 20 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/database/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration for database servers 2 | - include: security.yml 3 | - include: postgresql.yml 4 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/database/tasks/postgresql.yml: -------------------------------------------------------------------------------- 1 | # install and configure PostgreSQL database 2 | - name: ensure postgresql database packages are installed 3 | apt: name={{ item }} 4 | with_items: 5 | - postgresql 6 | - libpq-dev 7 | - python3-psycopg2 8 | - postgresql-client 9 | - postgresql-client-common 10 | become: yes 11 | 12 | 13 | - name: create database instance 14 | postgresql_db: name={{ db_name }} 15 | become: yes 16 | become_user: postgres 17 | 18 | 19 | - name: configure separate PostgreSQL user 20 | postgresql_user: db={{ db_name }} name={{ db_user }} 21 | password={{ db_password }} priv=ALL 22 | role_attr_flags=NOSUPERUSER 23 | become: yes 24 | become_user: postgres 25 | 26 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/database/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: enable PostgreSQL access 3 | ufw: rule=allow port=5432 4 | become: yes 5 | 6 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/init_config/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # establish non-root user with sudo privileges 2 | - name: create a non-root group 3 | group: 4 | name: "{{ deploy_group }}" 5 | state: present 6 | 7 | 8 | - name: create non-root user 9 | user: 10 | name: "{{ deploy_user }}" 11 | group: "{{ deploy_group }}" 12 | shell: "/bin/bash" 13 | state: present 14 | 15 | 16 | - name: add authorized_key to non-root user 17 | authorized_key: 18 | user: "{{ deploy_user }}" 19 | state: present 20 | key: "{{ lookup('file', ssh_dir + ssh_key_name + '.pub') }}" 21 | 22 | 23 | - name: add non-root group to sudo privileges 24 | lineinfile: 25 | dest: /etc/sudoers 26 | state: present 27 | regexp: "^{{ deploy_group }}" 28 | line: "%{{ deploy_group }} ALL=(ALL) NOPASSWD: ALL" 29 | validate: visudo -cf %s 30 | 31 | 32 | - name: disable root SSH logins 33 | replace: 34 | destfile: /etc/ssh/sshd_config 35 | regexp: "^PermitRootLogin yes" 36 | replace: "PermitRootLogin no" 37 | backup: no 38 | 39 | 40 | - name: disable SSH logins by password 41 | replace: 42 | destfile: /etc/ssh/sshd_config 43 | regexp: "^PasswordAuthentication yes" 44 | replace: "PasswordAuthentication no" 45 | backup: no 46 | 47 | 48 | - name: restart SSH service 49 | service: 50 | name: ssh 51 | state: restarted 52 | 53 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/webserver/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration for web servers 2 | - include: security.yml 3 | - include: nginx.yml 4 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/webserver/tasks/nginx.yml: -------------------------------------------------------------------------------- 1 | # install and configure Nginx 2 | - name: ensure Nginx is installed 3 | apt: name=nginx state=present update_cache=yes 4 | become: yes 5 | 6 | - name: write custom Nginx configuration 7 | template: src=nginx_conf.j2 8 | dest=/etc/nginx/conf.d/app.conf 9 | become: yes 10 | 11 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/webserver/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: enable HTTP access 3 | ufw: rule=allow port=80 4 | become: yes 5 | 6 | - name: enable HTTPS access 7 | ufw: rule=allow port=443 8 | become: yes 9 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/roles/webserver/templates/nginx_conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | } 4 | -------------------------------------------------------------------------------- /intro-ansible/06-configuring-servers/webanddatabase.yml: -------------------------------------------------------------------------------- 1 | # configures a web server 2 | - name: apply webserver config to server 3 | hosts: webserver 4 | user: deployer 5 | roles: 6 | - common 7 | - webserver 8 | 9 | 10 | # configures a database server 11 | - name: apply database config to server 12 | hosts: database 13 | user: deployer 14 | roles: 15 | - common 16 | - database 17 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/group_vars/all: -------------------------------------------------------------------------------- 1 | deploy_user: deployer 2 | deploy_group: deployers 3 | ssh_dir: "/home/matt/flaskdeploy/" 4 | ssh_key_name: "flaskdeploy" 5 | 6 | db_name: "flaskdeploy" 7 | db_user: "{{ deploy_user }}" 8 | db_password: pleaseuseagoodpassword 9 | 10 | app_name: flaskdeploy 11 | fqdn: "www.ansibledeploymentexample.com" 12 | web_serve_dir: "/var/www/html" 13 | ssl_cert_email: "matthew.makai@gmail.com" 14 | 15 | wsgi_server_port: 8000 16 | sub_app_dir: "/home/{{ deploy_user }}/{{ app_name }}/app" 17 | 18 | app_dir: "/home/{{ deploy_user }}/{{ app_name }}" 19 | local_deploy_key_dir: "/home/matt/devel/flaskdeploy" 20 | code_repository: "ssh://git@github.com/fullstackpython/flask-git-dashboard.git" 21 | read_only_deploy_key_name: deploy_key 22 | 23 | venv_dir: "/home/{{ deploy_user }}/venv" 24 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/hosts: -------------------------------------------------------------------------------- 1 | [init_config] 2 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 3 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 4 | 5 | [common] 6 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 7 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 8 | 9 | [webserver] 10 | 142.93.123.128 ansible_python_interpreter=/usr/bin/python3 11 | 12 | [database] 13 | 142.93.119.59 ansible_python_interpreter=/usr/bin/python3 14 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/init_config.yml: -------------------------------------------------------------------------------- 1 | # sets up a non-root user and group 2 | - name: apply initial config to server 3 | hosts: init_config 4 | user: root 5 | roles: 6 | - init_config 7 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration regardless of server type 2 | - include: security.yml 3 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/common/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: ensure python packages are installed 3 | apt: 4 | name: "{{ item }}" 5 | update_cache: yes 6 | become: yes 7 | with_items: 8 | - "python3-pip" 9 | - "python3-dev" 10 | - "fail2ban" 11 | 12 | 13 | - name: enable SSH within the firewall 14 | ufw: rule=allow port=22 15 | become: yes 16 | 17 | - name: enable firewall itself 18 | ufw: state=enabled 19 | become: yes 20 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/database/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration for database servers 2 | - include: security.yml 3 | - include: postgresql.yml 4 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/database/tasks/postgresql.yml: -------------------------------------------------------------------------------- 1 | # install and configure PostgreSQL database 2 | - name: ensure postgresql database packages are installed 3 | apt: name={{ item }} 4 | with_items: 5 | - postgresql 6 | - libpq-dev 7 | - python3-psycopg2 8 | - postgresql-client 9 | - postgresql-client-common 10 | become: yes 11 | 12 | 13 | - name: create database instance 14 | postgresql_db: name={{ db_name }} 15 | become: yes 16 | become_user: postgres 17 | 18 | 19 | - name: configure separate PostgreSQL user 20 | postgresql_user: db={{ db_name }} name={{ db_user }} 21 | password={{ db_password }} priv=ALL 22 | role_attr_flags=NOSUPERUSER 23 | become: yes 24 | become_user: postgres 25 | 26 | 27 | - name: allow connections from our webserver 28 | lineinfile: 29 | path: /etc/postgresql/10/main/pg_hba.conf 30 | state: present 31 | insertafter: "# TYPE DATABASE USER ADDRESS METHOD" 32 | line: "host {{ db_name }} {{ db_user }} 142.93.123.128 trust " 33 | become: yes 34 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/database/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: enable PostgreSQL access 3 | ufw: rule=allow port=5432 4 | become: yes 5 | 6 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/init_config/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # establish non-root user with sudo privileges 2 | - name: create a non-root group 3 | group: 4 | name: "{{ deploy_group }}" 5 | state: present 6 | 7 | 8 | - name: create non-root user 9 | user: 10 | name: "{{ deploy_user }}" 11 | group: "{{ deploy_group }}" 12 | shell: "/bin/bash" 13 | state: present 14 | 15 | 16 | - name: add authorized_key to non-root user 17 | authorized_key: 18 | user: "{{ deploy_user }}" 19 | state: present 20 | key: "{{ lookup('file', ssh_dir + ssh_key_name + '.pub') }}" 21 | 22 | 23 | - name: add non-root group to sudo privileges 24 | lineinfile: 25 | dest: /etc/sudoers 26 | state: present 27 | regexp: "^{{ deploy_group }}" 28 | line: "%{{ deploy_group }} ALL=(ALL) NOPASSWD: ALL" 29 | validate: visudo -cf %s 30 | 31 | 32 | - name: disable root SSH logins 33 | replace: 34 | destfile: /etc/ssh/sshd_config 35 | regexp: "^PermitRootLogin yes" 36 | replace: "PermitRootLogin no" 37 | backup: no 38 | 39 | 40 | - name: disable SSH logins by password 41 | replace: 42 | destfile: /etc/ssh/sshd_config 43 | regexp: "^PasswordAuthentication yes" 44 | replace: "PasswordAuthentication no" 45 | backup: no 46 | 47 | 48 | - name: restart SSH service 49 | service: 50 | name: ssh 51 | state: restarted 52 | 53 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/handlers/main.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: restart nginx 3 | service: name=nginx state=restarted 4 | become: yes 5 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/dependencies.yml: -------------------------------------------------------------------------------- 1 | # handles application dependencies and virtual environment 2 | 3 | - name: ensure Python virtualenv and pip are installed 4 | apt: 5 | name: "{{ item }}" 6 | update_cache: yes 7 | become: yes 8 | with_items: 9 | - python3-venv 10 | - python3-pip 11 | 12 | - name: check if virtualenv is created 13 | stat: 14 | path: "{{ venv_dir }}" 15 | register: venv_created 16 | 17 | - name: create virtualenv if it does not already exist 18 | shell: "python3 -m venv {{ venv_dir }}" 19 | when: not venv_created.stat.exists 20 | 21 | - name: use pip3 to install application dependencies 22 | shell: "cd {{ app_dir }}; {{ venv_dir }}/bin/pip3 install --upgrade -r requirements.txt" 23 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/git.yml: -------------------------------------------------------------------------------- 1 | # clone our source code repo onto our server 2 | - name: ensure Git is installed 3 | apt: name=git state=present update_cache=yes 4 | become: yes 5 | 6 | - name: create deploy key directory 7 | file: path=/home/{{ deploy_user }}/git_deploy_key state=directory 8 | 9 | - name: ensure deploy key is on the remote server 10 | copy: src={{ local_deploy_key_dir }}/{{ read_only_deploy_key_name }} 11 | dest=/home/{{ deploy_user }}/git_deploy_key/{{ read_only_deploy_key_name }} 12 | mode=0600 owner={{ deploy_user }} group={{ deploy_group }} 13 | 14 | - name: clone or pull latest code 15 | git: repo={{ code_repository }} dest={{ app_dir }} 16 | key_file=/home/{{ deploy_user }}/git_deploy_key/{{ read_only_deploy_key_name }} 17 | accept_hostkey=yes 18 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # handles server protection and configuration for web servers 2 | - include: security.yml 3 | - include: nginx.yml 4 | - include: git.yml 5 | - include: dependencies.yml 6 | - include: wsgi.yml 7 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/nginx.yml: -------------------------------------------------------------------------------- 1 | # install and configure Nginx 2 | - name: ensure Nginx is installed 3 | apt: name=nginx state=present update_cache=yes 4 | become: yes 5 | 6 | - name: ensure Lets Encrypt is installed 7 | apt: name=letsencrypt state=present update_cache=yes 8 | become: yes 9 | 10 | - name: write custom Nginx configuration 11 | template: src=nginx_conf.j2 12 | dest=/etc/nginx/conf.d/{{ app_name }}.conf 13 | become: yes 14 | notify: 15 | - restart nginx 16 | 17 | - name: create Lets Encrypt directory 18 | file: name=/var/www/letsencrypt state=directory 19 | become: yes 20 | 21 | - name: remove default Nginx configuration 22 | file: name=/etc/nginx/sites-enabled/default state=absent 23 | become: yes 24 | 25 | 26 | - name: check if certificate already created 27 | stat: 28 | path: "/etc/letsencrypt/live/{{ fqdn }}" 29 | register: certs 30 | become: yes 31 | 32 | - name: stop nginx to obtain Lets Encrypt certificate 33 | service: 34 | name: nginx 35 | state: stopped 36 | become: yes 37 | when: not certs.stat.exists 38 | 39 | - name: create certificate with Lets Encrypt command 40 | shell: "letsencrypt certonly --standalone -d {{ fqdn }} -w {{ web_serve_dir }} -m {{ ssl_cert_email }} --agree-tos --renew-by-default --non-interactive" 41 | become: yes 42 | when: not certs.stat.exists 43 | 44 | - name: generate strong Diffie-Hellman group 45 | shell: "openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048" 46 | become: yes 47 | when: not certs.stat.exists 48 | 49 | - name: start nginx back after Lets Encrypt cert obtained 50 | service: 51 | name: nginx 52 | state: started 53 | become: yes 54 | when: not certs.stat.exists 55 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/security.yml: -------------------------------------------------------------------------------- 1 | # install security configuration 2 | - name: enable HTTP access 3 | ufw: rule=allow port=80 4 | become: yes 5 | 6 | - name: enable HTTPS access 7 | ufw: rule=allow port=443 8 | become: yes 9 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/tasks/wsgi.yml: -------------------------------------------------------------------------------- 1 | # configures and runs WSGI server via Supervisor 2 | 3 | - name: ensure Supervisor is installed 4 | apt: name=supervisor state=present update_cache=yes 5 | become: yes 6 | 7 | - name: set Supervisor configuration 8 | template: src=supervisor_app.conf.j2 9 | dest=/etc/supervisor/conf.d/{{ app_name }}.conf 10 | become: yes 11 | 12 | - name: stop Supervisor 13 | service: name=supervisor state=stopped 14 | become: yes 15 | 16 | - name: pause for configuration to take effect 17 | pause: seconds=2 18 | 19 | - name: start Supervisor back up 20 | service: name=supervisor state=started 21 | become: yes 22 | notify: 23 | - restart nginx 24 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/templates/nginx_conf.j2: -------------------------------------------------------------------------------- 1 | upstream appserver_wsgi_app { 2 | server localhost:{{ wsgi_server_port }} fail_timeout=0; 3 | } 4 | 5 | 6 | server { 7 | listen 80; 8 | server_name {{ fqdn }}; 9 | rewrite ^(.*) https://$server_name$1 permanent; 10 | } 11 | 12 | 13 | server { 14 | server_name {{ fqdn }}; 15 | listen 443 ssl; 16 | ssl_certificate /etc/letsencrypt/live/{{ fqdn }}/fullchain.pem; 17 | ssl_certificate_key /etc/letsencrypt/live/{{ fqdn }}/privkey.pem; 18 | ssl_protocols TLSv1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2 19 | ssl_prefer_server_ciphers on; 20 | ssl_dhparam /etc/ssl/certs/dhparam.pem; # openssl dhparam -out /etc/nginx/dhparam.pem 4096 21 | ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; 22 | ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 23 | ssl_session_timeout 10m; 24 | ssl_session_cache shared:SSL:10m; 25 | ssl_session_tickets off; # Requires nginx >= 1.5.9 26 | ssl_stapling on; # Requires nginx >= 1.3.7 27 | ssl_stapling_verify on; # Requires nginx => 1.3.7 28 | add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; 29 | add_header X-Frame-Options DENY; 30 | add_header X-Content-Type-Options nosniff; 31 | add_header X-XSS-Protection "1; mode=block"; 32 | 33 | access_log /var/log/nginx/{{ fqdn }}.access.log; 34 | error_log /var/log/nginx/{{ fqdn }}.error.log info; 35 | keepalive_timeout 5; 36 | 37 | # nginx serve up static files, not through WSGI server 38 | location /static { 39 | autoindex on; 40 | alias {{ sub_app_dir }}/static; 41 | } 42 | 43 | location / { 44 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 45 | proxy_set_header Host $http_host; 46 | proxy_redirect off; 47 | if (!-f $request-filename) { 48 | proxy_pass http://appserver_wsgi_app; 49 | break; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/roles/webserver/templates/supervisor_app.conf.j2: -------------------------------------------------------------------------------- 1 | [program:{{ app_name }}] 2 | command={{ venv_dir }}/bin/gunicorn app:app 3 | directory={{ app_dir }} 4 | user={{ deploy_user }} 5 | autostart=true 6 | autorestart=true 7 | redirect_stderr=true 8 | -------------------------------------------------------------------------------- /intro-ansible/07-web-app-deploys/webanddatabase.yml: -------------------------------------------------------------------------------- 1 | # configures a web server 2 | - name: apply webserver config to server 3 | hosts: webserver 4 | user: deployer 5 | roles: 6 | - common 7 | - webserver 8 | 9 | 10 | # configures a database server 11 | - name: apply database config to server 12 | hosts: database 13 | user: deployer 14 | roles: 15 | - common 16 | - database 17 | -------------------------------------------------------------------------------- /intro-ansible/README.md: -------------------------------------------------------------------------------- 1 | Introduction to Ansible book code goes here. 2 | --------------------------------------------------------------------------------