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…
	
	Add table
		
		Reference in a new issue