new wordpress creation pipeline!

This commit is contained in:
Adrien Luxey 2020-05-01 15:40:16 +02:00
parent 78efb7b7e2
commit 62af249e77
10 changed files with 359 additions and 96 deletions

View file

@ -1,6 +1,23 @@
# Deployer: deploy your shit and make it run
So lame to have to configure nginx, MySQL, and your filesystem to install a stupid Wordpress instance.
**Deployer** does my config for me like the slave it is.
All the configuration is defined in `group_vars/all/vars.yml`, go check.
Create a side `group_vars/all/vault.yml` for your secrets, and encrypt it with Ansible Vault:
ansible-vault encrypt group_vars/all/vault.yml
# other sub-commands: edit, decrypt...
I usually run the following command:
ansible-playbook --ask-vault-pass sites.yml -i inventory -v
## Required packages on remote
Python (installed with `pip` for user `adrien`):
Python modules:
* docker
* docker-compose
@ -8,38 +25,33 @@ Python (installed with `pip` for user `adrien`):
TODO: Ansible task to install that before the rest
## Configure mailing
# Features
When I send mails from the container with `php`, with current `msmtp` config, it fails if the recipient isn't `@luxeylab.net`.
* Creating Wordpress instances (yoohoo, da best)
Host `mail.log`:
# mail('adrien@yopmai.com'...) in container
Apr 2 09:07:56 Serenity postfix/smtpd[22617]: connect from unknown[172.27.1.2]
Apr 2 09:07:56 Serenity postfix/smtpd[22617]: NOQUEUE: reject: RCPT from unknown[172.27.1.2]: 454 4.7.1 <adrien@yopmail.com>: Relay access denied; from=<php@www.rennesdesbois.fr> to=<adrien@yopmail.com> proto=ESMTP helo=<localhost>
Apr 2 09:07:56 Serenity postfix/smtpd[22617]: lost connection after DATA from unknown[172.27.1.2]
Apr 2 09:07:56 Serenity postfix/smtpd[22617]: disconnect from unknown[172.27.1.2] ehlo=1 mail=1 rcpt=0/1 data=0/1 commands=2/4
* That send mail!!11!1!
# mail('adrien@yopmai.com'...) on host
Apr 2 09:10:33 Serenity postfix/cleanup[27364]: 5DB1D3CCDA: message-id=<20200402071033.5DB1D3CCDA@luxeylab.net>
Apr 2 09:10:33 Serenity postfix/qmgr[2066]: 5DB1D3CCDA: from=<adrien@luxeylab.net>, size=286, nrcpt=1 (queue active)
Apr 2 09:10:33 Serenity postfix/smtp[27366]: 5DB1D3CCDA: to=<adrien@yopmail.com>, relay=smtp.yopmail.com[87.98.164.155]:25, delay=0.68, delays=0.35/0/0.23/0.09, dsn=2.0.0, status=sent (250 mail saved)
Apr 2 09:10:33 Serenity postfix/qmgr[2066]: 5DB1D3CCDA: removed
### Does not support
# mail('adrien@luxeylab.net'...) in container
Apr 2 08:08:12 Serenity postfix/smtpd[2647]: connect from unknown[172.27.1.2]
Apr 2 08:08:12 Serenity postfix/smtpd[2647]: 6BA723CCD8: client=unknown[172.27.1.2]
Apr 2 08:08:12 Serenity postfix/cleanup[5829]: 6BA723CCD8: message-id=<>
Apr 2 08:08:12 Serenity postfix/smtpd[2647]: disconnect from unknown[172.27.1.2] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
Apr 2 08:08:12 Serenity postfix/qmgr[2066]: 6BA723CCD8: from=<php@www.rennesdesbois.fr>, size=290, nrcpt=1 (queue active)
Apr 2 08:08:13 Serenity postfix/smtp[5831]: 6BA723CCD8: to=<adrien.luxey@gmail.com>, orig_to=<adrien@luxeylab.net>, relay=gmail-smtp-in.l.google.com[74.125.133.26]:25, delay=0.79, delays=0.23/0.03/0.11/0.42, dsn=2.0.0, status=sent (250 2.0.0 OK 1585807693 130si4035771wma.123 - gsmtp)
Apr 2 08:08:13 Serenity postfix/qmgr[2066]: 6BA723CCD8: removed
* Setting up the host
Good docs on the topic:
* SSL certificate creation (bro, do it yourself!). That is:
* [Explains postfix on Host+ssmtp in Docker in detail, poorly written](https://medium.com/@thilinaviraj950/configure-and-use-host-base-postfix-to-send-emails-from-a-container-18cd279fc460)
* [Another one](https://www.michelebologna.net/2019/send-an-email-from-a-docker-container/)
I needed to add Docker's network (`172.0.0.0/8`) to `mynetworks` in `/etc/postfix/main.cf`. Also `inet_interfaces` would have had to be changed if Arthur didn't put it to `all`.
**Now solved.**
```bash
# Make an nginx file for certbot
cat << EOF > /etc/nginx/sites-enabled/yoursite.com
server {
listen 80;
server_name www.yoursite.com yoursite.com;
include snippets/letsencrypt.conf;
}
EOF
nginx -t # Is everything alright?
# If so, restart nginx
service nginx restart
# Create the certificate
certbot certonly --webroot -w /var/www/letsencrypt -d yoursite.com -d www.yoursite.com
# Remove the stupid file
rm /etc/nginx/sites-enabled/yoursite.com
service nginx restart
```

View file

@ -3,7 +3,29 @@
www_path: /vault/www
sites_path: /vault/sites
wordpress:
version: 5.4
checksum: sha1:d5f1e6d7cadd72c11d086a2e1ede0a72f23d993e
sites:
- slug: test # Shorthand name to use as directory/file name
# The site URL (without www)
url: test.luxeylab.net
# Ask nginx to redirect url to www
# Else, we redirect www to url
redirect_to_www: no
# What kind of site is that?
type: wordpress
# Subnet addresses
subnet_cidr_address: 172.27.6.0/24
subnet_gateway_ip: 172.27.6.1
subnet_nginx_ip: 172.27.6.2
subnet_site_ip: 172.27.6.3
mysql_database: wp_test
mysql_username: wp_test
mysql_password: "{{ vault_wp_test_mysql_password }}"
# - slug: rdb # Shorthand name to use as directory/file name
# # The site URL (without www)
# url: rennesdesbois.fr
@ -72,25 +94,25 @@ sites:
# mysql_username: lexperimental
# mysql_password: "{{ vault_lexperimental_mysql_password }}"
- slug: mts # Shorthand name to use as directory/file name
# The site URL (without www)
url: editionsmangetasoupe.fr
# Ask nginx to redirect url to www
# Else, we redirect www to url
redirect_to_www: no
# What kind of site is that?
type: drupal
# Subnet addresses
subnet_cidr_address: 172.27.5.0/24
subnet_gateway_ip: 172.27.5.1
subnet_site_ip: 172.27.5.2
# - slug: mts # Shorthand name to use as directory/file name
# # The site URL (without www)
# url: editionsmangetasoupe.fr
# # Ask nginx to redirect url to www
# # Else, we redirect www to url
# redirect_to_www: no
# # What kind of site is that?
# type: drupal
# # Subnet addresses
# subnet_cidr_address: 172.27.5.0/24
# subnet_gateway_ip: 172.27.5.1
# subnet_site_ip: 172.27.5.2
# This will allow setting up MySQL
# Configuration on Drupal's side must be done by hand:
# Edit your <drupal_install>/sites/default/settings.php
mysql_database: mts8
mysql_username: mts
mysql_password: "{{ vault_mts_mysql_password }}"
# # This will allow setting up MySQL
# # Configuration on Drupal's side must be done by hand:
# # Edit your <drupal_install>/sites/default/settings.php
# mysql_database: mts8
# mysql_username: mts
# mysql_password: "{{ vault_mts_mysql_password }}"
mysql_root_password: "{{ vault_mysql_root_password }}"
adrien_serenity_password: "{{ vault_adrien_serenity_password }}"

View file

@ -1,30 +1,34 @@
$ANSIBLE_VAULT;1.1;AES256
30333835653963663535316564323735303838343861333835313232626632336635623361316534
3931353931336638626634643133343865363539633266660a646339376138346330336232373139
65393937643136623530663130626664666466623930376462613237616230343165346163383563
3639336166373234360a306532306231333363333533373937613763626565353630626237373037
66613537356339376131653239323330346530323137346430656635613331653261306234636432
61663539656166613338333336323962623434656532323037356336346439386130313936613864
39393466306635656532336162326266366361356530393264316437326335346439616136646665
36356633643434326364346564613562356637663463653935616665373966386335373530323333
30393138383365646438616265353165613233313039333538393634313330343565633161623266
35656330383065373966383132323235663866613139613830333435393438333761313438663763
66366166303462613666393936303966616338393166373539336464366361656530323335333934
66366462323665326465353731666264383431623637373330343039666538313063396263373066
38323366393738353763626436373830396462386566346535646663326233636462303164316561
37646230663131643936376135343462363839336431353564303564373233376132346139326535
33326165633239333864666233643466376164653835626262616264666434306530346462323464
37643962656336356436616138626531396665616235353263326265326236383739363130386262
65356238303437303232396639396563353836333937303836313637376139386239653465333962
62346236353934396236366535323262643161336366326432363539386664323766633561363232
30623364323035343830663362326466326433643165356435343965343162303965323063383732
33373639343038663931646262373164356563366531656432626232343762396661386338386165
35363666383164383031663532616563373735663565343130643638313739656266653336323339
61383834653032643335653866653338643035376366373238343435653632383338346566643132
39663234373637343565623431343366386635666635623838396163396137363465613438353737
39353561656239636533613161316634633035626338663433626131613461346439353061373765
65313632346339303164656561303538343937366264376665666364663762656435636166623861
61346562366235343437636337306163326365666463343038633861333265623135356439313264
32343036373433313331356566363636643237623835343033616338386336363465356233386431
38313536323661623165623662616162396363396436613838663864613165653635663638353839
30343333363633373338626535373034623738336463383266346464646434623731
34386539366237643636326332343434623662373561336536363338373535393662323663656535
6431333031393336353139616535363739343763356163620a356164313063616231373634393963
30366366616539306438326563623832626666363562383334303961643862373736356337356635
6461363664316566660a346539376262326438353933663333393137623438643166623335383236
38643631363032636138333739353337633839303562383664366163303537633463663331313362
30306132383465633434376236626562353935383463306333386538653436323832313063646162
35653934306631383633636132666534373537316137623961653566303463376433373561323166
38353034393830323165653130663063646135656137633434313066616663636635346431333138
65633663626266613039323264653738316564643563303764646330643837383639646131643639
36646664386138623565353864373235326362353537306133633663316238613862616464323732
66383733306166633466333833643162353261356565326232356132303062623936663731663136
36613261646130383632636165393061316235326435383064313233316639383830323165653265
31383063303463303561383434623832323536336438623265316264623661653333323133356231
64663734343333663130303565383466666436383964646564313932323637393136646165643634
66636261663561613139393333326663646464303339323933663061383736343536623164356566
36323534323666303532383333343363373666386565643566396636386265393232636233396133
35393338613166653931666536663336633364383735303361636366656666636433356262656262
34633534306239663563326566623161633363633466613764343836613331326265636439623530
37326138393738316536393464643131653537663434323664343237393165613039646232343835
34323036386232363437633966303861643962393337326332613334343431383231646465336334
65396465373163643163313466376334646436386463373232613663393663373139356234326165
64353633313832633335633830323337636130636431666263383061626231343138646665346136
36323232373338373734333436656435646163393961633366613735616537656638633034373965
38663662373736626561666239333733393630653439383265616433663934366435323136373963
66303836363764393363383833323066616234316666303934626638363734346663616661616664
36366635656534343738343234356363323032636435336433333135666232623231333739336262
30636561613938396631623365613535656666313334663665363037636534353030626262313134
62663931626661313231613235366364613635383638633265386330373731663738336435346537
30393137373636363237643365626437623734376162626437306364313564616530663934343137
33313065346465633933616134386434323031313239303736653564623833376563336361366164
37666635643334663665633966653738616131643237343063326430326263363461303966333032
64616336303361626336343930363666623565356431336461346132326636333636663730343934
3666

View file

@ -3,7 +3,7 @@
path: "{{ sites_path }}/{{ item.slug }}"
state: directory
mode: '750'
tags: [docker, nginx]
tags: [docker, nginx, mysql]
- name: Render sexy Dockerfile
template:

View file

@ -1,3 +1,59 @@
- name: Is it a new install?
stat:
path: "{{ www_path }}/{{ item.slug }}_wp-content/index.php"
register: wpcontent
- name: Populate wp-content folder
block:
- name: "Clear folder {{ www_path }}/{{ item.slug }}_wp-content"
file:
path: "{{ www_path }}/{{ item.slug }}_wp-content"
state: absent
- name: "Download Wordpress v{{ wordpress.version }} archive"
get_url:
url: "https://wordpress.org/wordpress-{{ wordpress.version }}.tar.gz"
dest: "/tmp/wordpress.tgz"
checksum: "{{ wordpress.checksum }}"
- name: "Extract Wordpress v{{ wordpress.version }} archive"
unarchive:
src: "/tmp/wordpress.tgz"
dest: /tmp
remote_src: yes
- name: "Copy wp-content folder to destination"
copy:
src: /tmp/wordpress/wp-content/
dest: "{{ www_path }}/{{ item.slug }}_wp-content"
remote_src: yes
# group: www-data
# mode: '0660'
# directory_mode: '0770'
- name: Set proper access rights to wp-content tree
file:
path: "{{ www_path }}/{{ item.slug }}_wp-content"
state: directory
recurse: yes
group: www-data
mode: "u=rwX,g=rwX,o="
- name: "Remove downloaded content"
file:
path: "{{ toremove }}"
state: absent
loop:
- /tmp/wordpress.tgz
- /tmp/wordpress
loop_control:
loop_var: toremove
when: wpcontent.stat.exists is not defined or wpcontent.stat.exists == False
tags: [docker, nginx]
####################
# Render templates #
####################
- name: "Create folder {{ sites_path }}/{{ item.slug }}"
file:
path: "{{ sites_path }}/{{ item.slug }}"
@ -5,25 +61,39 @@
mode: '750'
tags: [docker, nginx]
- name: Render sexy Dockerfile
template:
src: wordpress/Dockerfile.j2
dest: "{{ sites_path }}/{{ item.slug }}/Dockerfile"
tags: docker
- name: "Create {{ sites_path }}/{{ item.slug }} sub-directories"
file:
path: "{{ sites_path }}/{{ item.slug }}/{{ filetree_item.path }}"
state: directory
mode: "{{ filetree_item.mode }}"
with_filetree: "../templates/wordpress/"
loop_control:
loop_var: filetree_item
when: filetree_item.state == 'directory'
tags: [docker, nginx]
- name: Render marvelous docker-compose.yml
- name: Render template files
template:
src: wordpress/docker-compose.yml.j2
dest: "{{ sites_path }}/{{ item.slug }}/docker-compose.yml"
tags: docker
src: "{{ filetree_item.src }}"
dest: "{{ sites_path }}/{{ item.slug }}/{{ filetree_item.path | regex_replace('.j2','') }}"
mode: "{{ filetree_item.mode }}"
with_filetree: "../templates/wordpress/"
loop_control:
loop_var: filetree_item
when: filetree_item.state == 'file'
tags: [docker, nginx]
- name: Render swell nginx site config
template:
src: wordpress/nginx.j2
- name: Copy host config to /etc/nginx/sites-available
copy:
remote_src: yes
src: "{{ sites_path }}/{{ item.slug }}/nginx.host"
dest: "/etc/nginx/sites-available/{{ item.url }}"
become: yes
tags: nginx
# - name: Create Let's Encrypt certificate
# This seems hard, see:
# https://docs.ansible.com/ansible/latest/modules/acme_certificate_module.html#acme-certificate-module

View file

@ -1,11 +1,13 @@
version: '3'
# Generated by ansible for site {{ item.url }}
# At {{ item.subnet_site_ip }} on {{ item.subnet_cidr_address }}
# On network {{ item.subnet_cidr_address }}:
# - web server (nginx) at {{ item.subnet_nginx_ip }}
# - php-fpm (wordpress) at {{ item.subnet_site_ip }}
services:
wp:
build: .
site:
build: site
restart: always
environment:
WORDPRESS_DB_HOST: "{{ item.subnet_gateway_ip }}"
@ -13,14 +15,31 @@ services:
WORDPRESS_DB_PASSWORD: "{{ item.mysql_password }}"
WORDPRESS_DB_NAME: "{{ item.mysql_database }}"
volumes:
- "html_data:/var/www/html"
- "{{ www_path }}/{{ item.slug }}_wp-content:/var/www/html/wp-content"
networks:
net:
ipv4_address: "{{ item.subnet_site_ip }}"
nginx:
build: nginx
restart: always
depends_on:
- site
volumes:
- "html_data:/var/www/html"
- "{{ www_path }}/{{ item.slug }}_wp-content:/var/www/html/wp-content"
networks:
net:
ipv4_address: "{{ item.subnet_nginx_ip }}"
networks:
net:
ipam:
driver: default
config:
- subnet: "{{ item.subnet_cidr_address }}"
volumes:
html_data:

View file

@ -49,7 +49,7 @@ server {
location / {
include snippets/header-params_location.conf;
proxy_pass http://{{ item.subnet_site_ip }}:80;
proxy_pass http://{{ item.subnet_nginx_ip }}:80;
}
}

View file

@ -0,0 +1,5 @@
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
# Should be UID & GID=33
# USER www-data:www-data

View file

@ -0,0 +1,128 @@
user www-data www-data;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
charset utf-8;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15s;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 10M;
client_body_timeout 60;
# server_names_hash_bucket_size 64;
server_name_in_redirect off;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default;
listen [::]:80 default;
server_name {{ item.url }} www.{{ item.url }};
root /var/www/html;
index index.php;
# Restrictions
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~ /\. {
deny all;
}
location = /_.gif {
expires max;
empty_gif;
}
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
expires max;
log_not_found off;
}
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# XMLRPC is a great way to bruteforce passwords
location = /xmlrpc.php {
deny all;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
# Breaks site...
# try_files $fastcgi_script_name =404;
# Block httpoxy attacks. See https://httpoxy.org/ :
fastcgi_param HTTP_PROXY "";
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
# Do not diplay errors:
# fastcgi_intercept_errors on;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_pass site:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
}

View file

@ -1,4 +1,4 @@
FROM wordpress:apache
FROM wordpress:{{ wordpress.version }}-fpm
RUN apt-get update; \
apt-get install -y --no-install-recommends msmtp; \
@ -16,4 +16,7 @@ RUN echo "\
account default\n\
host {{ item.subnet_gateway_ip }}\n\
port 25\n\
from php@{{ item.url }}\n" > /etc/msmtprc
from php@{{ item.url }}\n" > /etc/msmtprc
# Should be UID & GID=33
# USER www-data:www-data