Add deployment scripts
This commit is contained in:
parent
3f31086dd8
commit
9cb0c6f676
3 changed files with 305 additions and 0 deletions
147
deploy/basics.yml
Normal file
147
deploy/basics.yml
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# 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
|
66
deploy/postfix/main.cf
Normal file
66
deploy/postfix/main.cf
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# 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
|
92
fabfile.py
vendored
Normal file
92
fabfile.py
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
"""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',
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in a new issue