Changeset - 9cb0c6f6768a
[Not reviewed]
0 0 3
Ben Sturmfels (bsturmfels) - 11 months ago 2023-05-02 06:35:51
ben@sturm.com.au
Add deployment scripts
3 files changed with 305 insertions and 0 deletions:
0 comments (0 inline, 0 general)
deploy/basics.yml
Show inline comments
 
new file 100644
 
# Basic Ansible playbook to set up security essentials: Nginx dhparams, fail2ban,
 
# unattended-upgrades, history logging, firewall, no SSH keys and Postfix
 
# relay/rewriting/aliases.
 
#
 
# Run with:
 
# ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook deploy/basics.yml -i deploy/inventory.yml --verbose
 
- hosts: web
 
  become: true
 
  vars:
 
    ansible_ssh_pipelining: true
 
  tasks:
 
    - name: Generate dhparams file for HTTP2
 
      ansible.builtin.command:
 
        cmd: openssl dhparam -out /etc/nginx/dhparam.pem 2048
 
        creates: /etc/nginx/dhparam.pem
 
    - name: Install fail2ban
 
      apt:
 
        pkg: fail2ban
 

	
 
    - name: Install unattended-upgrades
 
      apt:
 
        pkg: unattended-upgrades
 

	
 
    - name: Configure unattended upgrades overrides
 
      # See defaults in 50unattended-upgrades.
 
      copy:
 
        dest: /etc/apt/apt.conf.d/20auto-upgrades
 
        content: |
 
          APT::Periodic::Update-Package-Lists "1";
 
          APT::Periodic::Unattended-Upgrade "1";
 
          Unattended-Upgrade::Automatic-Reboot "true";
 
          Unattended-Upgrade::Automatic-Reboot-Time "02:00";
 
          Unattended-Upgrade::Mail "root";
 

	
 
    - name: Add extensive history logging
 
      blockinfile:
 
        path: /etc/bash.bashrc
 
        block: |
 
          # Write to history file immediately (rather than only when shell is
 
          # closed). For setting history length see HISTSIZE and HISTFILESIZE in
 
          # bash(1).
 
          shopt -s histappend
 
          PROMPT_COMMAND='history -a'
 
          HISTSIZE=1000000
 
          HISTFILESIZE=1000000
 
        insertafter: EOF
 

	
 
    - name: Install `netfilter-persistent` && `iptables-persistent` packages
 
      apt:
 
        pkg:
 
          - iptables-persistent
 
          - netfilter-persistent
 

	
 
    - name: Flush existing firewall rules
 
      iptables:
 
        flush: true
 

	
 
    - name: Firewall rule - allow all loopback traffic
 
      iptables:
 
        action: append
 
        chain: INPUT
 
        in_interface: lo
 
        jump: ACCEPT
 

	
 
    - name: Firewall rule - allow established connections
 
      iptables:
 
        chain: INPUT
 
        ctstate: ESTABLISHED,RELATED
 
        jump: ACCEPT
 

	
 
    - name: Firewall rule - allow port ping traffic
 
      iptables:
 
        chain: INPUT
 
        jump: ACCEPT
 
        protocol: icmp
 

	
 
    - name: Firewall rule - allow port 22/SSH traffic
 
      iptables:
 
        chain: INPUT
 
        destination_port: '22'
 
        jump: ACCEPT
 
        protocol: tcp
 

	
 
    - name: Firewall rule - allow port 80/HTTP traffic
 
      iptables:
 
        chain: INPUT
 
        destination_port: '80'
 
        jump: ACCEPT
 
        protocol: tcp
 

	
 
    - name: Firewall rule - allow port 443/HTTPS traffic
 
      iptables:
 
        chain: INPUT
 
        destination_port: '443'
 
        jump: ACCEPT
 
        protocol: tcp
 

	
 
    - name: Firewall rule - drop any traffic without rule
 
      iptables:
 
        chain: INPUT
 
        jump: DROP
 

	
 
    - name: Disable SSH password authentication
 
      lineinfile:
 
        path: /etc/ssh/sshd_config
 
        line: 'PasswordAuthentication no'
 
        regexp: 'PasswordAuthentication '
 

	
 

	
 
    # Postfix
 
    - name: Postfix
 
      apt:
 
        pkg:
 
          - postfix
 
          - mailutils
 

	
 
    ## Commented because you only want this on first run ever.
 
    # - name: Add file for SMTP credentials
 
    #   copy:
 
    #     dest: /etc/postfix/sasl_passwd
 
    #     content: |-
 
    #       # After updating, run `sudo postmap hash:/etc/postfix/sasl_passwd`.
 
    #       [pine.sfconservancy.org]:587 conference@sfconservancy.org:PASSWORD
 

	
 
    - name: Configure Postfix envelope rewriting
 
      copy:
 
        dest: /etc/postfix/canonical
 
        content: |-
 
          /./ conference@sfconservancy.org
 

	
 
    - name: Configure Postfix From header rewriting
 
      copy:
 
        dest: /etc/postfix/header_checks
 
        content: |-
 
          /^From:.*/ REPLACE From: conference@sfconservancy.org
 

	
 
    - name: Configure Postfix for relaying
 
      copy:
 
        src: postfix/main.cf
 
        dest: /etc/postfix/main.cf
 

	
 
    - name: Alias mail to root
 
      copy:
 
        dest: /etc/aliases
 
        content: |-
 
          postmaster: root
 
          root: sysadmin@sfconservancy.org, sysadmin@sturm.com.au
deploy/postfix/main.cf
Show inline comments
 
new file 100644
 
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
 

	
 

	
 
# Debian specific:  Specifying a file name will cause the first
 
# line of that file to be used as the name.  The Debian default
 
# is /etc/mailname.
 
#myorigin = /etc/mailname
 

	
 
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
 
biff = no
 

	
 
# appending .domain is the MUA's job.
 
append_dot_mydomain = no
 

	
 
# Uncomment the next line to generate "delayed mail" warnings
 
#delay_warning_time = 4h
 

	
 
readme_directory = no
 

	
 
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
 
# fresh installs.
 
compatibility_level = 2
 

	
 
# TLS parameters
 
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
 
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
 
smtpd_use_tls=yes
 
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
 
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
 

	
 
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
 
# information on enabling SSL in the smtp client.
 

	
 
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
 
myhostname = symposion.sfconservancy.org
 
alias_maps = hash:/etc/aliases
 
alias_database = hash:/etc/aliases
 
myorigin = /etc/mailname
 
mydestination = $myhostname, symposion.novalocal, symposion, localhost
 
relayhost = [pine.sfconservancy.org]:587
 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
 
mailbox_size_limit = 0
 
recipient_delimiter = +
 
inet_interfaces = loopback-only
 
inet_protocols = all
 

	
 
# Relay to Conservancy
 
#
 
smtp_sasl_auth_enable = yes
 
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
 
smtp_sasl_security_options = noanonymous
 
smtp_tls_security_level = secure
 
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
 

	
 
# Increase default limit of 10M to 50M
 
message_size_limit = 51200000
 

	
 
# This configuration rewrites both envelope sender and From header to a single
 
# fixed address so that all outgoing mail, including cron emails, can be
 
# delivered through a single user-grade SMTP account.
 
#
 
# Envelope
 
canonical_maps = regexp:/etc/postfix/canonical
 
canonical_classes = envelope_sender
 
# From header
 
smtp_header_checks = regexp:/etc/postfix/header_checks
...
 
\ No newline at end of file
fabfile.py
Show inline comments
 
new file 100644
 
"""Automated deployment with Fabric.
 

	
 
To deploy to production:
 

	
 
  python3 -m pip install --user vps-deploy
 
  export FABRIC_HOSTS=user@hostname
 
  fab deploy
 
"""
 

	
 
import os
 

	
 
from fabric import task  # type: ignore
 
from invoke.collection import Collection  # type: ignore
 
from patchwork.files import exists
 
from vps_deploy import django_fabric2 as df2  # type: ignore
 

	
 
hosts = os.environ['FABRIC_HOSTS'].split(',')
 

	
 

	
 
def install_essentials(c):
 
    # ImageMagick (convert) and Inkscape required for generating badges.
 
    c.run('sudo apt-get install -yy git python3-dev python3-venv python3-wheel build-essential python3-cairocffi python3-psycopg2 postgresql uwsgi-emperor uwsgi-plugin-python3 memcached netcat nginx certbot libmemcached-dev xmlsec1 imagemagick inkscape')
 

	
 

	
 
@task(hosts=hosts)
 
def deploy(c):
 
    install_essentials(c)
 
    df2.transfer_files_git(c)
 
    df2.init(c)
 
    if not exists(c, c.env.virtualenv):
 
        c.sudo(f'mkdir -p $(dirname {c.env.virtualenv})')
 
        c.sudo(f'chown {c.user} $(dirname {c.env.virtualenv})')
 
        c.run('{env.python} -m venv --system-site-packages {env.virtualenv}'.format(env=c.env))
 
    with c.cd(c.env.project_dir):
 
        c.run('{env.virtualenv}/bin/python -m pip install -c constraints.txt -r requirements.txt'.format(env=c.env))
 
        # Fixes "AttributeError: install_layout". See:
 
        # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1003252
 
        # https://github.com/pypa/setuptools/issues/3278
 
        c.run('SETUPTOOLS_USE_DISTUTILS=stdlib {env.virtualenv}/bin/python -m pip install -c constraints.txt -r vendored_requirements.txt'.format(env=c.env))
 
    df2.prepare_django(c, fail_level='ERROR')
 
    df2.fix_permissions(
 
        c,
 
        read=['pinaxcon', 'vendor', 'static'],
 
        read_write=[],
 
    )
 
    df2.reload_uwsgi(c)
 
    df2.flush_memcached(c)
 
    df2.update_nginx(c)
 
    df2.install_scheduled_jobs(
 
        c,
 
        periodic_jobs=[
 
            'deploy/cron/cron.daily',
 
        ],
 
    )
 
    df2.check_site_online(c)
 

	
 

	
 
# The "ns" appears to be a magic name.
 
ns = Collection(
 
    deploy,
 
    task(df2.download_postgres_db, hosts=hosts),
 
    task(df2.mirror_postgres_db, hosts=hosts),
 
    task(df2.mirror_media, hosts=hosts),
 
    task(df2.django_shell, hosts=hosts),
 
    task(df2.bash, hosts=hosts),
 
)
 
ns.configure({
 
    # Built-in Fabric config.
 
    'run': {
 
        'echo': True,
 
        # Needed so local commands work. Can also use FABRIC_RUN_REPLACE_ENV.
 
        'replace_env': False,
 
    },
 

	
 
    # Our custom project config.
 
    'env': {
 
        'branch': 'fossy2023',
 
        'app_user': 'www-data',
 
        'db_name': 'symposion',
 
        'project_dir': '/srv/symposion_app',
 
        'media_dir': 'media',
 
        'virtualenv': '/srv/venvs/symposion-django-py39',
 
        'site_name': 'symposion',
 
        'requirements': 'requirements.txt',
 
        'settings': 'pinaxcon.settings',
 
        'uwsgi_conf': 'deploy/uwsgi.ini',
 
        'nginx_conf': 'deploy/nginx.conf',
 
        'python': '/usr/bin/python3.9',
 
        'url': 'https://2023.fossy.us/',
 
        'domain': '2023.fossy.us',
 
    },
 
})
0 comments (0 inline, 0 general)