diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 5ab3ba4cdf..0000000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-version: 2.1
-
-orbs:
- trellis:
- executors:
- python-2:
- docker:
- - image: 'circleci/python:2-stretch'
- python-3:
- docker:
- - image: 'circleci/python:3-stretch'
-
- jobs:
- syntax-check:
- parameters:
- ansible-version:
- type: string
- python-version:
- type: enum
- enum: ['3', '2']
- executor: python-<< parameters.python-version >>
- steps:
- - run: python --version
- - checkout
- - restore_cache:
- keys:
- - ansible-v1-<< parameters.python-version >>-<< parameters.ansible-version >>-{{ checksum "galaxy.yml" }}
- - run:
- name: Install Python dependencies in a venv
- command: |
- virtualenv venv
- . venv/bin/activate
- pip install ansible<< parameters.ansible-version >>
- ansible --version
- - run:
- name: Install Galaxy roles
- command: |
- . venv/bin/activate
- ansible-galaxy install -r galaxy.yml
- - save_cache:
- key: ansible-v1-<< parameters.python-version >>-<< parameters.ansible-version >>-{{ checksum "galaxy.yml" }}
- paths:
- - venv
- - vendor
- - run:
- name: Check Playbook syntax
- command: |
- . venv/bin/activate
- ansible-playbook --syntax-check -e env=development deploy.yml
- ansible-playbook --syntax-check -e env=development dev.yml
- ansible-playbook --syntax-check -e env=development server.yml
-
-workflows:
- syntax-check:
- jobs:
- - trellis/syntax-check:
- name: syntax-check-python-3-ansible-latest
- python-version: '3'
- ansible-version: ''
- - trellis/syntax-check:
- name: syntax-check-python-3-ansible-2.8
- python-version: '3'
- ansible-version: ~=2.8.0
- - trellis/syntax-check:
- name: syntax-check-python-3-ansible-2.7
- python-version: '3'
- ansible-version: ~=2.7.0
-
- - trellis/syntax-check:
- name: syntax-check-python-2-ansible-latest
- python-version: '2'
- ansible-version: ''
- - trellis/syntax-check:
- name: syntax-check-python-2-ansible-2.8
- python-version: '2'
- ansible-version: ~=2.8.0
- - trellis/syntax-check:
- name: syntax-check-python-2-ansible-2.7
- python-version: '2'
- ansible-version: ~=2.7.0
diff --git a/.gitattributes b/.gitattributes
index 41c1519e9f..aacb20e852 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,2 @@
-/.circleci export-ignore
/.gitattributes export-ignore
+/.github export-ignore
diff --git a/.github/actions/setup-step-ca/action.yml b/.github/actions/setup-step-ca/action.yml
new file mode 100644
index 0000000000..1f95236d7f
--- /dev/null
+++ b/.github/actions/setup-step-ca/action.yml
@@ -0,0 +1,42 @@
+name: Setup Step CA ACME server
+description: Installs and runs an ACME compatible server via step-ca
+inputs:
+ path:
+ description: 'step-ca path'
+ required: false
+ default: /root/.step
+runs:
+ using: composite
+ steps:
+ - name: Set STEP_CA_PATH env
+ run: echo STEP_CA_PATH=${{ inputs.path }} >> $GITHUB_ENV
+ shell: bash
+ - name: Download packages
+ run: |
+ wget -q https://dl.step.sm/gh-release/cli/docs-ca-install/v0.18.1/step-cli_0.18.1_amd64.deb
+ wget -q https://dl.step.sm/gh-release/certificates/docs-ca-install/v0.18.1/step-ca_0.18.1_amd64.deb
+ shell: bash
+ - name: Install packages
+ run: |
+ sudo dpkg -i step-cli_0.18.1_amd64.deb
+ sudo dpkg -i step-ca_0.18.1_amd64.deb
+ shell: bash
+ - name: Create password file
+ run: |
+ sudo mkdir $STEP_CA_PATH && sudo touch $STEP_CA_PATH/password.txt
+ echo $(openssl rand -hex 12) | sudo tee $STEP_CA_PATH/password.txt
+ shell: bash
+ - name: Initialize
+ run: |
+ sudo step ca init --name trellis-local-ca --dns 127.0.0.1 --address :8443 --provisioner admin --password-file $STEP_CA_PATH/password.txt --provisioner-password-file $STEP_CA_PATH/password.txt
+ sudo step ca provisioner add acme --type ACME
+ shell: bash
+ - name: Install certificate to system
+ run: |
+ sudo step certificate install $STEP_CA_PATH/certs/root_ca.crt
+ shell: bash
+ - name: Run service
+ run: |
+ sudo cp .github/files/step-ca.service /etc/systemd/system/step-ca.service
+ sudo systemctl start step-ca
+ shell: bash
diff --git a/.github/files/inventory b/.github/files/inventory
new file mode 100644
index 0000000000..87d5779a91
--- /dev/null
+++ b/.github/files/inventory
@@ -0,0 +1,4 @@
+[production]
+localhost ansible_connection=local
+[web]
+localhost ansible_connection=local
diff --git a/.github/files/step-ca.service b/.github/files/step-ca.service
new file mode 100644
index 0000000000..e62cc2bc5d
--- /dev/null
+++ b/.github/files/step-ca.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=step-ca service
+After=network.target
+StartLimitIntervalSec=0
+
+[Service]
+Type=simple
+Restart=always
+RestartSec=1
+Environment=STEPPATH=/root/.step
+WorkingDirectory=/root/.step
+ExecStart=/usr/bin/step-ca config/ca.json --password-file password.txt
+
+[Install]
+WantedBy=multi-user.target
diff --git a/.github/files/vault.yml b/.github/files/vault.yml
new file mode 100644
index 0000000000..0dd232f46a
--- /dev/null
+++ b/.github/files/vault.yml
@@ -0,0 +1,36 @@
+# Documentation: https://roots.io/trellis/docs/vault/
+vault_mysql_root_password: productionpw
+
+# Documentation: https://roots.io/trellis/docs/security/
+vault_users:
+ - name: "{{ admin_user }}"
+ password: example_password
+ salt: "generateme"
+
+# Variables to accompany `group_vars/production/wordpress_sites.yml`
+# Note: the site name (`example.com`) must match up with the site name in the above file.
+vault_wordpress_sites:
+ example.com:
+ env:
+ db_password: example_dbpassword
+ # Generate your keys here: https://roots.io/salts.html
+ auth_key: "generateme"
+ secure_auth_key: "generateme"
+ logged_in_key: "generateme"
+ nonce_key: "generateme"
+ auth_salt: "generateme"
+ secure_auth_salt: "generateme"
+ logged_in_salt: "generateme"
+ nonce_salt: "generateme"
+ example-https.com:
+ env:
+ db_password: example_dbpassword
+ # Generate your keys here: https://roots.io/salts.html
+ auth_key: "generateme"
+ secure_auth_key: "generateme"
+ logged_in_key: "generateme"
+ nonce_key: "generateme"
+ auth_salt: "generateme"
+ secure_auth_salt: "generateme"
+ logged_in_salt: "generateme"
+ nonce_salt: "generateme"
diff --git a/.github/files/wordpress_sites.yml b/.github/files/wordpress_sites.yml
new file mode 100644
index 0000000000..8fa390b191
--- /dev/null
+++ b/.github/files/wordpress_sites.yml
@@ -0,0 +1,34 @@
+letsencrypt_contact_emails:
+ - admin@example.com
+
+wordpress_sites:
+ example.com:
+ site_hosts:
+ - canonical: example.com
+ redirects:
+ - www.example.com
+ local_path: ../site
+ repo: git@github.com:roots/bedrock.git
+ branch: master
+ multisite:
+ enabled: false
+ ssl:
+ enabled: false
+ provider: letsencrypt
+ cache:
+ enabled: true
+ example-https.com:
+ site_hosts:
+ - canonical: example-https.com
+ redirects:
+ - www.example-https.com
+ local_path: ../site
+ repo: git@github.com:roots/bedrock.git
+ branch: master
+ multisite:
+ enabled: false
+ ssl:
+ enabled: true
+ provider: letsencrypt
+ cache:
+ enabled: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..67ab61288b
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,37 @@
+name: ci
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+ workflow_dispatch:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ['3.x']
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ architecture: x64
+ cache: 'pip'
+ - run: pip install -r requirements.txt
+ - uses: actions/cache@v2
+ with:
+ path: vendor
+ key: ${{ runner.os }}-galaxy-${{ hashFiles('galaxy.yml') }}
+ - run: ansible-galaxy install -r galaxy.yml
+ - name: Check playbook syntax
+ run: |
+ ansible-playbook --syntax-check -e env=development deploy.yml
+ ansible-playbook --syntax-check -e env=development dev.yml
+ ansible-playbook --syntax-check -e env=development server.yml
+ ansible-playbook --syntax-check -e env=development rollback.yml
+ ansible-playbook --syntax-check -e xdebug_tunnel_inventory_host=1 xdebug-tunnel.yml
diff --git a/.github/workflows/discourse.yml b/.github/workflows/discourse.yml
new file mode 100644
index 0000000000..0205abbd1f
--- /dev/null
+++ b/.github/workflows/discourse.yml
@@ -0,0 +1,17 @@
+name: Post release topic on Discourse
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ post:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: roots/discourse-topic-github-release-action@main
+ with:
+ discourse-api-key: ${{ secrets.DISCOURSE_RELEASES_API_KEY }}
+ discourse-base-url: ${{ secrets.DISCOURSE_BASE_URL }}
+ discourse-author-username: swalkinshaw
+ discourse-category: 12
+ discourse-tags: releases
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
new file mode 100644
index 0000000000..9741423727
--- /dev/null
+++ b/.github/workflows/integration.yml
@@ -0,0 +1,67 @@
+name: Integration
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+ workflow_dispatch:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: mkdir $HOME/.ssh
+ - name: Remove and cleanup mysql
+ run: |
+ sudo apt-get remove --purge mysql*
+ sudo apt-get autoremove
+ sudo apt-get autoclean
+ sudo rm -rf /etc/apparmor.d/abstractions/mysql /etc/apparmor.d/cache/usr.sbin.mysqld /etc/mysql /var/lib/mysql /var/log/mysql* /var/log/upstart/mysql.log* /var/run/mysqld ~/.mysql_history
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-python@v2
+ with:
+ python-version: '3.9'
+ - uses: ./.github/actions/setup-step-ca
+ - uses: roots/setup-trellis-cli@v1
+ with:
+ ansible-vault-password: 'fake'
+ auto-init: false
+ galaxy-install: false
+ trellis-directory: '.'
+ - name: Create new Trellis project
+ run: trellis new --name example.com --host www.example.com --trellis-version ${{ github.sha }} ./example.com
+ - name: Update configs
+ run: |
+ sudo echo "127.0.0.1 www.example.com example.com www.example-https.com example-https.com" | sudo tee -a /etc/hosts
+ cp ../../.github/files/inventory hosts/production
+ cp ../../.github/files/wordpress_sites.yml group_vars/production/wordpress_sites.yml
+ cp ../../.github/files/vault.yml group_vars/production/vault.yml
+ working-directory: example.com/trellis
+ - run: trellis exec ansible-playbook --version
+ working-directory: example.com/trellis
+ - name: Provision
+ run: trellis provision --extra-vars "web_user=runner letsencrypt_ca=https://127.0.0.1:8443/acme/acme" production
+ working-directory: example.com
+ - name: Deploy non-https site
+ run: trellis deploy --extra-vars "web_user=runner project_git_repo=https://github.com/roots/bedrock.git" production example.com
+ working-directory: example.com
+ - name: Install WordPress
+ run: |
+ wp core install --url="http://example.com" --title="Example.com" --admin_user="admin" --admin_password="password" --admin_email="admin@example.com"
+ working-directory: /srv/www/example.com/current
+ - name: Verify install
+ run: curl -s http://example.com | grep "
Example"
+ - name: Deploy https site
+ run: trellis deploy --extra-vars "web_user=runner project_git_repo=https://github.com/roots/bedrock.git" production example-https.com
+ working-directory: example.com
+ - name: Install WordPress
+ run: |
+ wp core install --url="http://example-https.com" --title="Example HTTPS" --admin_user="admin" --admin_password="password" --admin_email="admin@example.com"
+ working-directory: /srv/www/example-https.com/current
+ - name: Verify install
+ run: curl -s https://example-https.com | grep "Example HTTPS"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84e59ca325..dbb85e0e16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,111 @@
+### HEAD
+* Add built-in fail2ban filters ([#1375](https://github.com/roots/trellis/pull/1375))
+* Support Ansible >= 2.10 (tested up to 5.4.0) ([#1373](https://github.com/roots/trellis/pull/1373))
+* Remove Python 2 support ([#1361](https://github.com/roots/trellis/pull/1361))
+
+### 1.14.0: February 16th, 2022
+* Fix #1026 - Preserve nested path for copied folders between deploys ([#1364](https://github.com/roots/trellis/pull/1364))
+* Fix #1354 - Ensure correct PHP version is set ([#1365](https://github.com/roots/trellis/pull/1365))
+* Create mysql my.cnf credentials file earlier ([#1360](https://github.com/roots/trellis/pull/1360))
+* Remove bin scripts (trellis-cli should be used instead) ([#1352](https://github.com/roots/trellis/pull/1352))
+* Update `wp_cli_version` to `2.6.0` ([#1358](https://github.com/roots/trellis/pull/1358))
+* Deploy hook build example: update Sage build command ([#1356](https://github.com/roots/trellis/pull/1356))
+
+### 1.13.0: January 21st, 2022
+* Fix #1354 - Prevent apt from installing recommended packages for php ([#1355](https://github.com/roots/trellis/pull/1355))
+* Update default ssh key paths (include ed25519 keys) ([#1348](https://github.com/roots/trellis/pull/1348))
+* Use trellis-cli for Vagrant galaxy install when available ([#1349](https://github.com/roots/trellis/pull/1349))
+* Fix #970 - Improve git clone failure error ([#1351](https://github.com/roots/trellis/pull/1351))
+
+### 1.12.0: January 3rd, 2022
+* Improve support for adding public SSH keys ([#1344](https://github.com/roots/trellis/pull/1344))
+* Update default Vagrant IP to 192.168.56.5 ([#1341](https://github.com/roots/trellis/pull/1341))
+* Remove old WP customizer frame options hack ([#1338](https://github.com/roots/trellis/pull/1338))
+* Fix #1319 - Improve how ssh_args are loaded ([#1337](https://github.com/roots/trellis/pull/1337))
+* Fix #1331 - Improve passlib instructions([#1336](https://github.com/roots/trellis/pull/1336))
+
+### 1.11.0: December 10th, 2021
+* Bump minimum ansible version to `2.10.0` and add `ansible-base` to requirements ([#1334](https://github.com/roots/trellis/pull/1334))
+* Fix Ansible `2.10.16` - set default for `ansible_ssh_extra_args` ([#1333](https://github.com/roots/trellis/pull/1333))
+* Set max supported Vagrant version to `< 2.2.19` ([#1332](https://github.com/roots/trellis/pull/1332))
+* Bump `vagrant_ansible_version` to `2.10.7` ([#1329](https://github.com/roots/trellis/pull/1329))
+* Remove Nginx `ssl_dhparam` directive and Diffie-Hellman params group ([#1326](https://github.com/roots/trellis/pull/1326))
+* Add PHP 8.1 support ([#1325](https://github.com/roots/trellis/pull/1325))
+
+### 1.10.0: November 28th, 2021
+* Default to PHP 8.0 ([#1322](https://github.com/roots/trellis/pull/1322))
+* Add GitHub SSH ed25519 key to known hosts ([#1324](https://github.com/roots/trellis/pull/1324))
+* Enable pipelining for local Ansible connections ([#1323](https://github.com/roots/trellis/pull/1323))
+
+### 1.9.1: November 11th, 2021
+* Update MariaDB mirror source ([#1320](https://github.com/roots/trellis/pull/1320))
+* Remove explicit arch deb options for MariaDB (improves ARM support) ([#1318](https://github.com/roots/trellis/pull/1318))
+
+### 1.9.0: October 27th, 2021
+* Bump max tested Ansible version to `2.10.7` ([#1317](https://github.com/roots/trellis/pull/1317))
+* Fix display color output in logs ([#1316](https://github.com/roots/trellis/pull/1316))
+* Define `composer_authentications` default ([#1315](https://github.com/roots/trellis/pull/1315))
+* Fix #1311 - Remove explicit permission for site directory ([#1314](https://github.com/roots/trellis/pull/1314))
+* Fix #1277 - Disable PHP CLI memory limit ([#1278](https://github.com/roots/trellis/pull/1278))
+* Fix #1285 - Improve handling of WP-CLI failed verification ([#1295](https://github.com/roots/trellis/pull/1295))
+* Fix #1284 - Update logrotate postrotate Nginx command ([#1293](https://github.com/roots/trellis/pull/1293))
+* Replace php-gd with php-imagick ([#1292](https://github.com/roots/trellis/pull/1292))
+* Improve handling of PHP versions and support PHP 8.0 (default is still 7.4) ([#1284](https://github.com/roots/trellis/pull/1284))
+
+### 1.8.0: February 12th, 2021
+* Set permissions on all file related tasks ([#1270](https://github.com/roots/trellis/pull/1270))
+* Use Python 3 for `ansible_local` Vagrant provisioner ([#1269](https://github.com/roots/trellis/pull/1269))
+* Bump `vagrant_ansible_version` to `2.9.10` ([#1268](https://github.com/roots/trellis/pull/1268))
+* Migrate to Xdebug 3 ([#1260](https://github.com/roots/trellis/pull/1260))
+
+### 1.7.1: January 20th, 2021
+* Improved repo connection failure message on deploys ([#1265](https://github.com/roots/trellis/pull/1265))
+* Fix #1263 - Remove deprecated PHP option `track_errors` ([#1264](https://github.com/roots/trellis/pull/1264))
+* Validate that `letsencrypt_contact_emails` is a list ([#1250](https://github.com/roots/trellis/pull/1250))
+* Add config for PHP CLI ([#1261](https://github.com/roots/trellis/pull/1261))
+* Fix security issue with empty password ([#1256](https://github.com/roots/trellis/pull/1256))
+
+### 1.7.0: November 9th, 2020
+* Officially support Ubuntu 20.04 (and default Vagrant to it) ([#1197](https://github.com/roots/trellis/pull/1197))
+
+### 1.6.0: November 5th, 2020
+* Remove prestissimo for Composer 2.0 support ([#1247](https://github.com/roots/trellis/pull/1247))
+* Allow WP cron intervals to be configurable ([#1222](https://github.com/roots/trellis/pull/1222))
+* Remove default Vagrant SMB credentials ([#1215](https://github.com/roots/trellis/pull/1215))
+* Fix usage of `ANSIBLE_CONFIG` env var ([#1217](https://github.com/roots/trellis/pull/1217))
+* Update MariaDB package to 10.5 ([#1212](https://github.com/roots/trellis/pull/1212))
+* Switch to official Nginx Ubuntu package ([#1208](https://github.com/roots/trellis/pull/1208))
+
+### 1.5.0: August 5th, 2020
+* Improve Nginx reloading for failed Let's Encrypt certificates ([#1207](https://github.com/roots/trellis/pull/1207))
+* Add support for Lets Encrypt contact emails ([#1206](https://github.com/roots/trellis/pull/1206))
+* Support branch variable for deploys ([#1204](https://github.com/roots/trellis/pull/1204))
+* Removes ID from Lets Encrypt bundled certificate and make filename stable ([#834](https://github.com/roots/trellis/pull/834))
+* Make Fail2ban settings extensible ([#1177](https://github.com/roots/trellis/pull/1177))
+* Improve ip_whitelist in development ([#1183](https://github.com/roots/trellis/pull/1183))
+* Support Ansible 2.9 ([#1169](https://github.com/roots/trellis/pull/1169))
+* [BREAKING] Remove `nginx_includes_deprecated` feature ([#1173](https://github.com/roots/trellis/pull/1173))
+* Bump Ansible version_tested_max to 2.8.10 ([#1167](https://github.com/roots/trellis/pull/1167))
+* Bump Ansible requirement to 2.8.0 ([#1147](https://github.com/roots/trellis/pull/1147))
+* Update CircleCI Config ([#1184](https://github.com/roots/trellis/pull/1184))
+
+### 1.4.0: April 2nd, 2020
+* Update PHP to 7.4 ([#1164](https://github.com/roots/trellis/pull/1164))
+* Update `wp_cli_version` to 2.4.0 ([#1131](https://github.com/roots/trellis/pull/1131))
+* Fix `subjectAltName` for self-signed certificates ([#1128](https://github.com/roots/trellis/pull/1128))
+* `composer install` without `--no-scripts` during deploy ([#1133](https://github.com/roots/trellis/pull/1133))
+* Allow `composer install` with `--classmap-authoritative` during deploy ([#1132](https://github.com/roots/trellis/pull/1132))
+* Use modern SSL config for Nginx ([#1127](https://github.com/roots/trellis/pull/1127))
+* Fix `DEPLOY_UNFINISHED` not being copied over to `release` folder ([#1145](https://github.com/roots/trellis/pull/1145))
+* Deploy: Remove untracked files from project folder ([#1146](https://github.com/roots/trellis/pull/1146))
+* Nginx: Block `composer/installed.json` ([#1150](https://github.com/roots/trellis/pull/1150))
+* Run `git clean` after checking `git clone` is successful ([#1151](https://github.com/roots/trellis/pull/1151))
+* Lint: Fix: `[206] Variables should have spaces before and after: {{ var_name }}` ([#1152](https://github.com/roots/trellis/pull/1152))
+* Lint: Fix: `[306] Shells that use pipes should set the pipefail option ([#1153](https://github.com/roots/trellis/pull/1153))
+* Lint: Fix `[301] Commands should not change things if nothing needs doing ([#1139](https://github.com/roots/trellis/pull/1139))
+* Void rolled back releases ([#1148](https://github.com/roots/trellis/pull/1148))
+* Add `WP_DEBUG_LOG` to `.env` on deploy ([#1160](https://github.com/roots/trellis/pull/1160))
+
### 1.3.0: December 7th, 2019
* Add `git_sha` and `release_version` to `.env` on deploy ([#1124](https://github.com/roots/trellis/pull/1124))
* Lower self-signed certificate expiry time for macOS Cataline support ([#1120](https://github.com/roots/trellis/pull/1120))
diff --git a/LICENSE.md b/LICENSE.md
index ee859d0f58..1b5cd27da6 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright (c) Roots
+Copyright (c) Roots Software Foundation LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/README.md b/README.md
index 447222f7da..eca8889534 100644
--- a/README.md
+++ b/README.md
@@ -1,120 +1,155 @@
-# Trellis
-[](https://github.com/roots/trellis/releases)
-[](https://circleci.com/gh/roots/trellis)
-[](https://twitter.com/rootswp)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ansible-powered LEMP stack for WordPress
+
+
+
+ Website
Documentation
Releases
Support
+
+
+## Sponsors
+
+**Trellis** is an open source project and completely free to use.
+
+However, the amount of effort needed to maintain and develop new features and products within the Roots ecosystem is not sustainable without proper financial backing. If you have the capability, please consider [sponsoring Roots](https://github.com/sponsors/roots).
+
+
+
+
+
+## Overview
Ansible playbooks for setting up a LEMP stack for WordPress.
- Local development environment with Vagrant
- High-performance production servers
- Zero-downtime deploys for your [Bedrock](https://roots.io/bedrock/)-based WordPress sites
+- [trellis-cli](https://github.com/roots/trellis-cli) for easier management
## What's included
Trellis will configure a server with the following and more:
-* Ubuntu 18.04 Bionic LTS
-* Nginx (with optional FastCGI micro-caching)
-* PHP 7.3
-* MariaDB (a drop-in MySQL replacement)
-* SSL support (scores an A+ on the [Qualys SSL Labs Test](https://www.ssllabs.com/ssltest/))
-* Let's Encrypt for free SSL certificates
-* HTTP/2 support (requires SSL)
-* Composer
-* WP-CLI
-* sSMTP (mail delivery)
-* MailHog
-* Memcached
-* Fail2ban and ferm
-
-## Documentation
-
-Full documentation is available at [https://roots.io/trellis/docs/](https://roots.io/trellis/docs/).
+- Ubuntu 20.04 Focal LTS
+- Nginx (with optional FastCGI micro-caching)
+- PHP 8.0
+- MariaDB (a drop-in MySQL replacement)
+- SSL support (scores an A+ on the [Qualys SSL Labs Test](https://www.ssllabs.com/ssltest/))
+- Let's Encrypt for free SSL certificates
+- HTTP/2 support (requires SSL)
+- Composer
+- WP-CLI
+- sSMTP (mail delivery)
+- MailHog
+- Memcached
+- Fail2ban and ferm
## Requirements
-Make sure all dependencies have been installed before moving on:
-
-* [Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx)
-* [Virtualbox](https://www.virtualbox.org/wiki/Downloads) >= 4.3.10
-* [Vagrant](https://www.vagrantup.com/downloads.html) >= 2.1.0
-
-**Windows user?** [Read the Windows getting started docs](https://roots.io/getting-started/docs/windows-development-environment-trellis/) for slightly different installation instructions.
+See the full [installation](https://docs.roots.io/trellis/master/installation/#installation) docs for requirements.
## Installation
-The recommended directory structure for a Trellis project looks like:
+Create a new project:
-```shell
-example.com/ # → Root folder for the project
-├── trellis/ # → Your clone of this repository
-└── site/ # → A Bedrock-based WordPress site
- └── web/
- ├── app/ # → WordPress content directory (themes, plugins, etc.)
- └── wp/ # → WordPress core (don't touch!)
+```bash
+$ trellis new example.com
```
-See a complete working example in the [roots-example-project.com repo](https://github.com/roots/roots-example-project.com).
+## Local development setup
+
+1. Review the automatically created site in `group_vars/development/wordpress_sites.yml`
+2. Customize settings if necessary
-1. Create a new project directory:
-```plain
-$ mkdir example.com && cd example.com
-```
-2. Install Trellis:
-```plain
-$ git clone --depth=1 git@github.com:roots/trellis.git && rm -rf trellis/.git
-```
-3. Install Bedrock into the `site` directory:
-```plain
-$ composer create-project roots/bedrock site
+Start the Vagrant virtual machine:
+
+```bash
+$ trellis up
```
-## Local development setup
+[Read the local development docs](https://docs.roots.io/trellis/master/local-development/#wordpress-installation) for more information.
-1. Configure your WordPress sites in `group_vars/development/wordpress_sites.yml` and in `group_vars/development/vault.yml`
-2. Ensure you're in the trellis directory: `cd trellis`
-3. Run `vagrant up`
+## Remote server setup (staging/production)
-[Read the local development docs](https://roots.io/trellis/docs/local-development-setup/) for more information.
+A base Ubuntu 18.04 (Bionic) or Ubuntu 20.04 (Focal LTS) server is required for setting up remote servers.
-## Remote server setup (staging/production)
+1. Configure your WordPress sites in `group_vars//wordpress_sites.yml` and in `group_vars//vault.yml` (see the [Vault docs](https://docs.roots.io/trellis/master/vault/) for how to encrypt files containing passwords)
+2. Add your server IP/hostnames to `hosts/`
+3. Specify public SSH keys for `users` in `group_vars/all/users.yml` (see the [SSH Keys docs](https://docs.roots.io/trellis/master/ssh-keys/))
-For remote servers, installing Ansible locally is an additional requirement. See the [docs](https://roots.io/trellis/docs/remote-server-setup/#requirements) for more information.
+Provision the server:
-A base Ubuntu 18.04 (Bionic) server is required for setting up remote servers. OS X users must have [passlib](http://pythonhosted.org/passlib/install.html#installation-instructions) installed.
+```bash
+$ trellis provision production
+```
-1. Configure your WordPress sites in `group_vars//wordpress_sites.yml` and in `group_vars//vault.yml` (see the [Vault docs](https://roots.io/trellis/docs/vault/) for how to encrypt files containing passwords)
-2. Add your server IP/hostnames to `hosts/`
-3. Specify public SSH keys for `users` in `group_vars/all/users.yml` (see the [SSH Keys docs](https://roots.io/trellis/docs/ssh-keys/))
-4. Run `ansible-playbook server.yml -e env=` to provision the server
+Or take advantage of its [Digital Ocean](https://roots.io/r/digitalocean) support to create a Droplet _and_ provision it in a single command:
+
+```bash
+$ trellis droplet create production
+```
-[Read the remote server docs](https://roots.io/trellis/docs/remote-server-setup/) for more information.
+[Read the remote server docs](https://docs.roots.io/trellis/master/remote-server-setup/) for more information.
## Deploying to remote servers
1. Add the `repo` (Git URL) of your Bedrock WordPress project in the corresponding `group_vars//wordpress_sites.yml` file
-2. Set the `branch` you want to deploy
-3. Run `./bin/deploy.sh `
-4. To rollback a deploy, run `ansible-playbook rollback.yml -e "site= env="`
+2. Set the `branch` you want to deploy (defaults to `master`)
+
+Deploy a site:
-[Read the deploys docs](https://roots.io/trellis/docs/deploys/) for more information.
+```bash
+$ trellis deploy
+```
+
+Rollback a deploy:
-## Contributing
+```bash
+$ trellis rollback
+```
-Contributions are welcome from everyone. We have [contributing guidelines](https://github.com/roots/guidelines/blob/master/CONTRIBUTING.md) to help you get started.
+[Read the deploys docs](https://roots.io/docs/trellis/master/deployments/) for more information.
-## Trellis sponsors
+## Migrating existing projects to trellis-cli:
-Help support our open-source development efforts by [becoming a patron](https://www.patreon.com/rootsdev).
+Assuming you're using the standard project structure, you just need to make the
+project trellis-cli compatible by initializing it:
-
+```bash
+$ trellis init
+```
## Community
Keep track of development and community news.
-* Participate on the [Roots Discourse](https://discourse.roots.io/)
-* Follow [@rootswp on Twitter](https://twitter.com/rootswp)
-* Read and subscribe to the [Roots Blog](https://roots.io/blog/)
-* Subscribe to the [Roots Newsletter](https://roots.io/subscribe/)
-* Listen to the [Roots Radio podcast](https://roots.io/podcast/)
+- Join us on Discord by [sponsoring us on GitHub](https://github.com/sponsors/roots)
+- Participate on the [Roots Discourse](https://discourse.roots.io/)
+- Follow [@rootswp on Twitter](https://twitter.com/rootswp)
+- Read and subscribe to the [Roots Blog](https://roots.io/blog/)
+- Subscribe to the [Roots Newsletter](https://roots.io/subscribe/)
diff --git a/Vagrantfile b/Vagrantfile
index d59cdb3b5a..1b7484cf47 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -16,7 +16,11 @@ ensure_plugins(vconfig.fetch('vagrant_plugins')) if vconfig.fetch('vagrant_insta
trellis_config = Trellis::Config.new(root_path: ANSIBLE_PATH)
-Vagrant.require_version '>= 2.1.0'
+if Vagrant::Util::Platform.darwin?
+ Vagrant.require_version '>= 2.1.0', '< 2.2.19'
+else
+ Vagrant.require_version '>= 2.1.0'
+end
Vagrant.configure('2') do |config|
config.vm.box = vconfig.fetch('vagrant_box')
@@ -64,14 +68,12 @@ Vagrant.configure('2') do |config|
fail_with_message "vagrant-hostmanager missing, please install the plugin with this command:\nvagrant plugin install vagrant-hostmanager\n\nOr install landrush for multisite subdomains:\nvagrant plugin install landrush"
end
- bin_path = File.join(ANSIBLE_PATH_ON_VM, 'bin')
-
vagrant_mount_type = vconfig.fetch('vagrant_mount_type')
extra_options = if vagrant_mount_type == 'smb'
{
- smb_username: vconfig.fetch('vagrant_smb_username', 'vagrant'),
- smb_password: vconfig.fetch('vagrant_smb_password', 'vagrant'),
+ smb_username: vconfig.fetch('vagrant_smb_username', nil),
+ smb_password: vconfig.fetch('vagrant_smb_password', nil),
}
else
{}
@@ -84,7 +86,6 @@ Vagrant.configure('2') do |config|
end
config.vm.synced_folder ANSIBLE_PATH, ANSIBLE_PATH_ON_VM, mount_options: mount_options(vagrant_mount_type, dmode: 755, fmode: 644), type: vagrant_mount_type, **extra_options
- config.vm.synced_folder File.join(ANSIBLE_PATH, 'bin'), bin_path, mount_options: mount_options(vagrant_mount_type, dmode: 755, fmode: 755), type: vagrant_mount_type, **extra_options
elsif !Vagrant.has_plugin?('vagrant-bindfs')
fail_with_message "vagrant-bindfs missing, please install the plugin with this command:\nvagrant plugin install vagrant-bindfs"
else
@@ -95,7 +96,6 @@ Vagrant.configure('2') do |config|
config.vm.synced_folder ANSIBLE_PATH, '/ansible-nfs', type: 'nfs'
config.bindfs.bind_folder '/ansible-nfs', ANSIBLE_PATH_ON_VM, o: 'nonempty', p: '0644,a+D'
- config.bindfs.bind_folder bin_path, bin_path, perms: '0755'
end
vconfig.fetch('vagrant_synced_folders', []).each do |folder|
@@ -117,23 +117,10 @@ Vagrant.configure('2') do |config|
provisioner = local_provisioning? ? :ansible_local : :ansible
provisioning_path = local_provisioning? ? ANSIBLE_PATH_ON_VM : ANSIBLE_PATH
- # Fix for https://github.com/hashicorp/vagrant/issues/10914
- if local_provisioning?
- config.vm.provision 'shell', inline: <<~SHELL
- sudo apt-get update -y -qq &&
- sudo dpkg-reconfigure libc6 &&
- export DEBIAN_FRONTEND=noninteractive &&
- sudo -E apt-get -q --option \"Dpkg::Options::=--force-confold\" --assume-yes install libssl1.1
- SHELL
- end
-
config.vm.provision provisioner do |ansible|
if local_provisioning?
ansible.install_mode = 'pip'
- if Vagrant::VERSION >= '2.2.5'
- # Fix for https://github.com/hashicorp/vagrant/issues/10950
- ansible.pip_install_cmd = 'curl https://bootstrap.pypa.io/get-pip.py | sudo python'
- end
+ ansible.pip_install_cmd = 'sudo apt-get install -y -qq python3-pip'
ansible.provisioning_path = provisioning_path
ansible.version = vconfig.fetch('vagrant_ansible_version')
end
@@ -143,6 +130,10 @@ Vagrant.configure('2') do |config|
ansible.galaxy_role_file = File.join(provisioning_path, 'galaxy.yml') unless vconfig.fetch('vagrant_skip_galaxy') || ENV['SKIP_GALAXY']
ansible.galaxy_roles_path = File.join(provisioning_path, 'vendor/roles')
+ if which('trellis')
+ ansible.galaxy_command = 'trellis galaxy install'
+ end
+
ansible.groups = {
'web' => ['default'],
'development' => ['default']
diff --git a/ansible.cfg b/ansible.cfg
index 1321fe3630..e6467d2586 100644
--- a/ansible.cfg
+++ b/ansible.cfg
@@ -8,8 +8,8 @@ inventory = hosts
nocows = 1
roles_path = vendor/roles
vars_plugins = ~/.ansible/plugins/vars:/usr/share/ansible/plugins/vars:lib/trellis/plugins/vars
+pipelining = True
[ssh_connection]
-ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s -o HostKeyAlgorithms=ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa
-pipelining = True
+ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s
retries = 1
diff --git a/bin/deploy.sh b/bin/deploy.sh
deleted file mode 100755
index 0b346e8e34..0000000000
--- a/bin/deploy.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-shopt -s nullglob
-
-ENVIRONMENTS=( hosts/* )
-ENVIRONMENTS=( "${ENVIRONMENTS[@]##*/}" )
-
-show_usage() {
- echo "Usage: deploy [options]
-
- is the environment to deploy to ("staging", "production", etc)
- is the WordPress site to deploy (name defined in "wordpress_sites")
-[options] is any number of parameters that will be passed to ansible-playbook
-
-Available environments:
-`( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" )`
-
-Examples:
- deploy staging example.com
- deploy production example.com
- deploy staging example.com -vv -T 60
-"
-}
-
-[[ $# -lt 2 ]] && { show_usage; exit 127; }
-
-for arg
-do
- [[ $arg = -h ]] && { show_usage; exit 0; }
-done
-
-ENV="$1"; shift
-SITE="$1"; shift
-EXTRA_PARAMS=$@
-DEPLOY_CMD="ansible-playbook deploy.yml -e env=$ENV -e site=$SITE $EXTRA_PARAMS"
-HOSTS_FILE="hosts/$ENV"
-
-if [[ ! -e $HOSTS_FILE ]]; then
- echo "Error: $ENV is not a valid environment ($HOSTS_FILE does not exist)."
- echo
- echo "Available environments:"
- ( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" )
- exit 1
-fi
-
-$DEPLOY_CMD
diff --git a/bin/xdebug-tunnel.sh b/bin/xdebug-tunnel.sh
deleted file mode 100755
index e60e4d48bb..0000000000
--- a/bin/xdebug-tunnel.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-show_usage() {
- echo "
-Usage: bin/xdebug-tunnel.sh
-
- can be 'open' or 'close'
- is the hostname, IP, or inventory alias in your \`hosts/\` file.
-
-Examples:
- To open a tunnel:
- bin/xdebug-tunnel.sh open 12.34.56.78
-
- To close a tunnel:
- bin/xdebug-tunnel.sh close 12.34.56.78
-"
-}
-
-ENABLE_TCP_FORWARDING=
-if [[ $1 == "open" ]]; then
- REMOTE_ENABLE=1
- ENABLE_TCP_FORWARDING="-e sshd_allow_tcp_forwarding=yes"
-elif [[ $1 == "close" ]]; then
- REMOTE_ENABLE=0
-else
- >&2 echo "The provided argument '${1}' is not acceptable."
- show_usage
- exit 1
-fi
-
-if [[ -z $2 ]]; then
- >&2 echo "The argument is required."
- show_usage
- exit 1
-fi
-
-XDEBUG_ENABLE="-e xdebug_remote_enable=${REMOTE_ENABLE}"
-SSH_HOST="-e xdebug_tunnel_inventory_host=$2"
-
-if [[ -n $DEBUG ]]; then
- PARAMS="$PARAMS ${VERBOSITY:--vvvv}"
-fi
-
-ansible-playbook xdebug-tunnel.yml $XDEBUG_ENABLE $ENABLE_TCP_FORWARDING $SSH_HOST $PARAMS
diff --git a/deploy-hooks/build-after.yml b/deploy-hooks/build-after.yml
new file mode 100644
index 0000000000..9cce3a2ac3
--- /dev/null
+++ b/deploy-hooks/build-after.yml
@@ -0,0 +1,11 @@
+# Placeholder `deploy_build_after` hook
+#
+# ⚠️ This example assumes your theme is using Sage 10
+#
+# Uncomment the lines below if you are using Sage 10
+#
+# ---
+# - name: Run Acorn optimize
+# command: wp acorn optimize
+# args:
+# chdir: "{{ deploy_helper.new_release_path }}"
diff --git a/deploy-hooks/build-before.yml b/deploy-hooks/build-before.yml
index e6893432ba..78003bd92c 100644
--- a/deploy-hooks/build-before.yml
+++ b/deploy-hooks/build-before.yml
@@ -1,10 +1,10 @@
# Placeholder `deploy_build_before` hook for building theme assets on the
# host machine and then copying the files to the remote server
#
-# ⚠️ This example assumes your theme is using Sage 9
-# An example for themes built with Sage 8 can be found at: https://git.io/vdgUt
+# ⚠️ This example assumes your theme is using Sage 10
#
-# Uncomment the lines below and replace `sage` with your theme folder
+# Uncomment the lines below if you are using Sage 10
+# and replace `sage` with your theme folder
#
# ---
# - name: Install npm dependencies
@@ -14,19 +14,19 @@
# chdir: "{{ project_local_path }}/web/app/themes/sage"
#
# - name: Install Composer dependencies
-# command: composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader --no-scripts
+# command: composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader --no-scripts --classmap-authoritative
# args:
# chdir: "{{ deploy_helper.new_release_path }}/web/app/themes/sage"
#
# - name: Compile assets for production
-# command: yarn build:production
+# command: yarn build
# delegate_to: localhost
# args:
# chdir: "{{ project_local_path }}/web/app/themes/sage"
#
# - name: Copy production assets
# synchronize:
-# src: "{{ project_local_path }}/web/app/themes/sage/dist"
+# src: "{{ project_local_path }}/web/app/themes/sage/public"
# dest: "{{ deploy_helper.new_release_path }}/web/app/themes/sage"
# group: no
# owner: no
diff --git a/dev.yml b/dev.yml
index 8df24a21d7..d84cf893da 100644
--- a/dev.yml
+++ b/dev.yml
@@ -1,12 +1,5 @@
---
-- name: Set ansible_python_interpreter
- hosts: web:&development
- gather_facts: false
- become: yes
- roles:
- - { role: python_interpreter, tags: [always] }
-
-- name: "WordPress Server: Install LEMP Stack with PHP 7.3 and MariaDB MySQL"
+- name: "WordPress Server: Install LEMP Stack with PHP and MariaDB MySQL"
hosts: web:&development
become: yes
remote_user: vagrant
@@ -27,7 +20,6 @@
- { role: logrotate, tags: [logrotate] }
- { role: princexml, tags: [princexml] }
- { role: epubcheck, tags: [epubcheck] }
- - { role: kindlegen, tags: [kindlegen] }
- { role: saxon-he, tags: [saxon-he] }
- { role: composer, tags: [composer] }
- { role: wp-cli, tags: [wp-cli] }
diff --git a/galaxy.yml b/galaxy.yml
index 387e4b4a12..788cd63b1b 100644
--- a/galaxy.yml
+++ b/galaxy.yml
@@ -1,10 +1,10 @@
- name: composer
src: geerlingguy.composer
- version: 1.7.6
+ version: 1.9.0
- name: ntp
src: geerlingguy.ntp
- version: 1.6.4
+ version: 2.3.1
- name: logrotate
src: nickhammond.logrotate
@@ -12,15 +12,11 @@
- name: swapfile
src: oefenweb.swapfile
- version: v2.0.22
+ version: v2.0.32
- name: mailhog
src: geerlingguy.mailhog
- version: 2.1.4
-
-- name: princexml
- src: pressbooks.princexml
- version: 12.5.0
+ version: 2.3.0
- src: geerlingguy.redis
- version: 1.6.0
+ version: 1.7.0
diff --git a/group_vars/all/helpers.yml b/group_vars/all/helpers.yml
index 20a70ecf80..1e56f5c083 100644
--- a/group_vars/all/helpers.yml
+++ b/group_vars/all/helpers.yml
@@ -8,6 +8,7 @@ wordpress_env_defaults:
wp_home: "{{ ssl_enabled | ternary('https', 'http') }}://{{ site_hosts_canonical | first }}"
wp_siteurl: "{{ ssl_enabled | ternary('https', 'http') }}://{{ site_hosts_canonical | first }}/wp"
domain_current_site: "{{ site_hosts_canonical | first }}"
+ wp_debug_log: "{{ www_root }}/{{ item.key }}/logs/debug.log"
site_env: "{{ wordpress_env_defaults | combine(vault_wordpress_env_defaults | default({}), item.value.env | default({}), vault_wordpress_sites[item.key].env) }}"
site_hosts_canonical: "{{ item.value.site_hosts | map(attribute='canonical') | list }}"
diff --git a/group_vars/all/known_hosts.yml b/group_vars/all/known_hosts.yml
index b8af937ca5..1db951997f 100644
--- a/group_vars/all/known_hosts.yml
+++ b/group_vars/all/known_hosts.yml
@@ -6,6 +6,8 @@
known_hosts:
- name: github.com
key: github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
+ - name: github.com
+ key: github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
- name: bitbucket.org
key: bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
- name: gitlab.com
diff --git a/group_vars/all/logrotate.yml b/group_vars/all/logrotate.yml
index c6d24962c6..ba1ba124fa 100644
--- a/group_vars/all/logrotate.yml
+++ b/group_vars/all/logrotate.yml
@@ -17,4 +17,4 @@ logrotate_scripts:
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
- postrotate: service nginx rotate
+ postrotate: service nginx reload >/dev/null 2>&1
diff --git a/group_vars/all/main.yml b/group_vars/all/main.yml
index 6c26f24cf2..9b8d168ee6 100644
--- a/group_vars/all/main.yml
+++ b/group_vars/all/main.yml
@@ -1,12 +1,9 @@
-composer_keep_updated: true
-composer_version: "1.10.20"
-composer_version_branch: ''
-composer_global_packages:
- - { name: hirak/prestissimo }
apt_cache_valid_time: 3600
apt_package_state: present
apt_security_package_state: latest
apt_dev_package_state: latest
+composer_keep_updated: true
+php_version: "7.4"
apt_packages_custom:
libxml2-utils: "{{ apt_package_state }}"
default-jdk: "{{ apt_package_state }}"
diff --git a/group_vars/all/security.yml b/group_vars/all/security.yml
index 2d9df3d3b9..bac2bbb098 100644
--- a/group_vars/all/security.yml
+++ b/group_vars/all/security.yml
@@ -1,3 +1,5 @@
+# Documentation: https://roots.io/trellis/docs/security/
+
ferm_input_list:
- type: dport_accept
dport: [http, https]
@@ -10,8 +12,25 @@ ferm_input_list:
seconds: 300
hits: 20
-# Documentation: https://roots.io/trellis/docs/security/
+
+# Enable built-in fail2ban services or add your own custom ones
+fail2ban_services_custom:
+ - name: wordpress_xmlrpc
+ filter: wordpress-xmlrpc
+ enabled: "false"
+ port: http,https
+ logpath: "{{ www_root }}/**/logs/access.log"
+ - name: wordpress_wp_login
+ filter: wordpress-wp-login
+ enabled: "false"
+ port: http,https
+ logpath: "{{ www_root }}/**/logs/access.log"
+
# If sshd_permit_root_login: false, admin_user must be in 'users' (`group_vars/all/users.yml`) with sudo group
# and in 'vault_users' (`group_vars/staging/vault.yml`, `group_vars/production/vault.yml`)
sshd_permit_root_login: true
sshd_password_authentication: false
+
+ip_whitelist:
+ - 127.0.0.0/8
+ - "{{ ipify_public_ip | default('') }}"
diff --git a/group_vars/all/users.yml b/group_vars/all/users.yml
index 2290fea7de..4d38ac23d2 100644
--- a/group_vars/all/users.yml
+++ b/group_vars/all/users.yml
@@ -7,16 +7,18 @@ users:
groups:
- "{{ web_group }}"
keys:
- - "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+ - "{{ lookup('file', '~/.ssh/id_rsa.pub', errors='ignore') }}"
+ - "{{ lookup('file', '~/.ssh/id_ed25519.pub', errors='ignore') }}"
# - https://github.com/username.keys
- name: "{{ admin_user }}"
groups:
- sudo
keys:
- - "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+ - "{{ lookup('file', '~/.ssh/id_rsa.pub', errors='ignore') }}"
+ - "{{ lookup('file', '~/.ssh/id_ed25519.pub', errors='ignore') }}"
# - https://github.com/username.keys
web_user: web
web_group: www-data
web_sudoers:
- - "/usr/sbin/service php7.3-fpm *"
+ - "/usr/sbin/service php{{ php_version }}-fpm *"
diff --git a/group_vars/development/main.yml b/group_vars/development/main.yml
index 8d7c15913c..1a3d9f3bd2 100644
--- a/group_vars/development/main.yml
+++ b/group_vars/development/main.yml
@@ -1,5 +1,4 @@
acme_tiny_challenges_directory: "{{ www_root }}/letsencrypt"
env: development
-ferm_enabled: false
mysql_root_password: "{{ vault_mysql_root_password }}" # Define this variable in group_vars/development/vault.yml
web_user: vagrant
diff --git a/group_vars/development/php.yml b/group_vars/development/php.yml
index 8df88dc533..c8490ede4b 100644
--- a/group_vars/development/php.yml
+++ b/group_vars/development/php.yml
@@ -5,6 +5,9 @@ php_track_errors: 'On'
php_mysqlnd_collect_memory_statistics: 'On'
php_opcache_enable: 0
+xdebug_mode: 'debug'
+xdebug_start_with_request: 'yes'
+xdebug_discover_client_host: 1
xdebug_remote_enable: 1
xdebug_remote_connect_back: 1
xdebug_remote_autostart: 1
diff --git a/group_vars/development/security.yml b/group_vars/development/security.yml
new file mode 100644
index 0000000000..fe079a2f7e
--- /dev/null
+++ b/group_vars/development/security.yml
@@ -0,0 +1,6 @@
+ferm_enabled: false
+ip_whitelist:
+ - 127.0.0.0/8
+ - 10.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml
index 62e95f430c..e22a685929 100644
--- a/group_vars/production/wordpress_sites.yml
+++ b/group_vars/production/wordpress_sites.yml
@@ -3,13 +3,13 @@
# Define accompanying passwords/secrets in group_vars/production/vault.yml
wordpress_sites:
- example.com:
+ pressbooks.test:
site_hosts:
- - canonical: example.com
+ - canonical: pressbooks.test
redirects:
- - www.example.com
+ - www.pressbooks.test
local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root)
- repo: git@github.com:example/example.com.git # replace with your Git repo URL
+ repo: git@github.com:example/pressbooks.test.git # replace with your Git repo URL
repo_subtree_path: site # relative path to your Bedrock/WP directory in your repo
branch: master
multisite:
@@ -21,6 +21,6 @@ wordpress_sites:
cache:
enabled: false
env:
- domain_current_site: example.com
- wp_home: https://example.com
- wp_siteurl: https://example.com/wp
+ domain_current_site: pressbooks.test
+ wp_home: https://pressbooks.test
+ wp_siteurl: https://pressbooks.test/wp
diff --git a/hosts/development b/hosts/development
index 4af294aca7..694b39e540 100644
--- a/hosts/development
+++ b/hosts/development
@@ -1,41 +1,8 @@
-# This file is only used for Windows hosts.
-#
-# Windows
-# -------------------------------------------------------------
-# If you want to run `dev.yml` manually you can SSH into the VM
-# to the directory with the `dev.yml` playbook and run:
-
-# `ansible-playbook dev.yml`
-#
-# Non-Windows
-# -------------------------------------------------------------
-# If you want to run `dev.yml` manually via the `ansible-playbook`
-# command (vs. `vagrant up` or `vagrant provision`), you might be
-# inclined to define your development host information in this file.
-# We recommend instead that you use the `-i` (inventory) option with
-# your `ansible-playbook` command to specify the custom inventory file
-# Vagrant has created for the VM. Vagrant's custom inventory
-# includes necessary non-standard SSH connection information.
-#
-# Here is an example command:
-#
-# `ansible-playbook dev.yml -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory`
-#
-# The `.vagrant` directory above is usually in the same directory as
-# your `Vagrantfile`. If not, you will need to adjust this path in the
-# command.
-#
-# Why run `dev.yml` manually?
-# -------------------------------------------------------------
-# One reason you may want to run `dev.yml` via the `ansible-playbook`
-# command is for the convenience of adding Ansible options via the
-# command line (e.g., `--tags`, `--skip-tags`, or `-vvvv`). In contrast,
-# the commands `vagrant up` and `vagrant provision` would only run the
-# `dev.yml` playbook with such options if you were edit the options
-# into the Vagrantfile's `config.vm.provision` section.
+# Add each host to the [development] group and to a "type" group such as [web] or [db].
+# List each machine only once per [group], even if it will host multiple sites.
[development]
-192.168.50.5 ansible_connection=local
+192.168.56.5
[web]
-192.168.50.5 ansible_connection=local
+192.168.56.5
diff --git a/lib/trellis/__init__.py b/lib/trellis/__init__.py
deleted file mode 100644
index 980f84a225..0000000000
--- a/lib/trellis/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
diff --git a/lib/trellis/plugins/callback/output.py b/lib/trellis/plugins/callback/output.py
index 9bb2cbede6..61935552a0 100644
--- a/lib/trellis/plugins/callback/output.py
+++ b/lib/trellis/plugins/callback/output.py
@@ -1,16 +1,21 @@
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
import os.path
import sys
+DOCUMENTATION = '''
+ callback: output
+ type: stdout
+ short_description: Custom output for Trellis
+ extends_documentation_fragment:
+ - default_callback
+'''
+
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
try:
from trellis.utils import output as output
except ImportError:
- ansible_path = os.getenv('ANSIBLE_CONFIG', os.getcwd())
+ ansible_config_path = os.getenv('ANSIBLE_CONFIG')
+ ansible_path = os.path.dirname(ansible_config_path) if ansible_config_path else os.getcwd()
if sys.path.append(os.path.join(ansible_path, 'lib')) in sys.path: raise
sys.path.append(sys.path.append(os.path.join(ansible_path, 'lib')))
from trellis.utils import output as output
diff --git a/lib/trellis/plugins/callback/vars.py b/lib/trellis/plugins/callback/vars.py
index 338d8f9597..2ffbbf9854 100644
--- a/lib/trellis/plugins/callback/vars.py
+++ b/lib/trellis/plugins/callback/vars.py
@@ -1,8 +1,6 @@
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
import re
import sys
+import os
from __main__ import cli
from ansible.module_utils.six import iteritems
@@ -13,6 +11,8 @@
from ansible.plugins.callback import CallbackBase
from ansible.template import Templar
from ansible.utils.unsafe_proxy import wrap_var
+from ansible import context
+from ansible.plugins.loader import connection_loader
class CallbackModule(CallbackBase):
@@ -23,13 +23,7 @@ class CallbackModule(CallbackBase):
def __init__(self):
super(CallbackModule, self).__init__()
-
- # handle Ansible 2.7 and 2.8 cases by normalizing each into a dict
- try:
- from ansible import context
- self._options = context.CLIARGS
- except ImportError:
- self._options = vars(cli.options) if cli else {}
+ self._options = context.CLIARGS
def raw_triage(self, key_string, item, patterns):
# process dict values
@@ -99,6 +93,9 @@ def darwin_without_passlib(self):
return True
def v2_playbook_on_play_start(self, play):
+ play_context = PlayContext(play=play)
+ connection = connection_loader.get('ssh', play_context, os.devnull)
+
env = play.get_variable_manager().get_vars(play=play).get('env', '')
env_group = next((group for key,group in iteritems(play.get_variable_manager()._inventory.groups) if key == env), False)
if env_group:
@@ -107,7 +104,7 @@ def v2_playbook_on_play_start(self, play):
for host in play.get_variable_manager()._inventory.list_hosts(play.hosts[0]):
hostvars = play.get_variable_manager().get_vars(play=play, host=host)
self.raw_vars(play, host, hostvars)
- host.vars['ssh_args_default'] = PlayContext(play=play)._ssh_args.default
+ host.vars['ssh_args_default'] = connection.get_option('ssh_args')
host.vars['cli_options'] = self.cli_options()
host.vars['cli_ask_pass'] = self._options.get('ask_pass', False)
host.vars['cli_ask_become_pass'] = self._options.get('become_ask_pass', False)
diff --git a/lib/trellis/plugins/filter/filters.py b/lib/trellis/plugins/filter/filters.py
index 6c8eed5eb7..8b4f624232 100644
--- a/lib/trellis/plugins/filter/filters.py
+++ b/lib/trellis/plugins/filter/filters.py
@@ -1,7 +1,3 @@
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function, unicode_literals)
-__metaclass__ = type
-
import types
from ansible import errors
diff --git a/lib/trellis/plugins/vars/version.py b/lib/trellis/plugins/vars/version.py
index 93c325ab34..e05b94e800 100644
--- a/lib/trellis/plugins/vars/version.py
+++ b/lib/trellis/plugins/vars/version.py
@@ -1,12 +1,8 @@
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
from ansible import __version__
from ansible.errors import AnsibleError
from distutils.version import LooseVersion
from operator import eq, ge, gt
-from sys import version_info
+from platform import python_version, python_version_tuple
try:
from __main__ import display
@@ -14,26 +10,22 @@
from ansible.utils.display import Display
display = Display()
-version_requirement = '2.7.12'
-version_tested_max = '2.8.4'
-python3_required_version = '2.5.3'
+version_requirement = '2.10.0'
+version_tested_max = '5.4.0'
-if version_info[0] == 3 and not ge(LooseVersion(__version__), LooseVersion(python3_required_version)):
- raise AnsibleError(('Ansible >= {} is required when using Python 3.\n'
- 'Either downgrade to Python 2 or update your Ansible version to {}.').format(python3_required_version, python3_required_version))
+if python_version_tuple()[0] == '2':
+ raise AnsibleError(('Trellis no longer supports Python 2 (you are using version {}).'
+ ' Python 2 reached end of life in 2020 and is unmaintained.\n'
+ 'Python 3 is required as of Trellis version v1.15.0.').format(python_version()))
if not ge(LooseVersion(__version__), LooseVersion(version_requirement)):
raise AnsibleError(('Trellis no longer supports Ansible {}.\n'
'Please upgrade to Ansible {} or higher.').format(__version__, version_requirement))
elif gt(LooseVersion(__version__), LooseVersion(version_tested_max)):
- display.warning(u'You Ansible version is {} but this version of Trellis has only been tested for '
+ display.warning(u'Your Ansible version is {} but this version of Trellis has only been tested for '
u'compatability with Ansible {} -> {}. It is advisable to check for Trellis updates or '
u'downgrade your Ansible version.'.format(__version__, version_requirement, version_tested_max))
-if eq(LooseVersion(__version__), LooseVersion('2.5.0')):
- display.warning(u'You Ansible version is {}. Consider upgrading your Ansible version to avoid '
- u'erroneous warnings such as `Removed restricted key from module data...`'.format(__version__))
-
# Import BaseVarsPlugin after Ansible version check.
# Otherwise import error for Ansible versions older than 2.4 would prevent display of version check message.
from ansible.plugins.vars import BaseVarsPlugin
diff --git a/lib/trellis/utils/__init__.py b/lib/trellis/utils/__init__.py
deleted file mode 100644
index 980f84a225..0000000000
--- a/lib/trellis/utils/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py
index 89b073b9c7..4ae252df67 100644
--- a/lib/trellis/utils/output.py
+++ b/lib/trellis/utils/output.py
@@ -1,5 +1,4 @@
# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os.path
@@ -9,12 +8,12 @@
from ansible import __version__
from ansible.module_utils._text import to_text
-from ansible.module_utils.six import string_types
def system(vagrant_version=None):
# Get most recent Trellis CHANGELOG entry
changelog_msg = ''
- ansible_path = os.getenv('ANSIBLE_CONFIG', os.getcwd())
+ ansible_config_path = os.getenv('ANSIBLE_CONFIG')
+ ansible_path = os.path.dirname(ansible_config_path) if ansible_config_path else os.getcwd()
changelog = os.path.join(ansible_path, 'CHANGELOG.md')
if os.path.isfile(changelog):
@@ -54,7 +53,7 @@ def replace_item_with_key(obj, result):
)
if should_replace:
- if 'key' in result._result[item]:
+ if type(result._result[item]) is dict and 'key' in result._result[item]:
result._result[item] = result._result[item]['key']
elif type(result._result[item]) is dict:
subitem = '_ansible_item_label' if '_ansible_item_label' in result._result[item] else 'item'
@@ -90,7 +89,7 @@ def display(obj, result):
# Must pass unicode strings to Display.display() to prevent UnicodeError tracebacks
if isinstance(msg, list):
msg = '\n'.join([to_text(x) for x in msg])
- elif not isinstance(msg, string_types):
+ elif not isinstance(msg, str):
msg = to_text(msg)
# Wrap text
@@ -101,17 +100,17 @@ def display(obj, result):
hr = '-' * int(wrap_width*.67)
if obj.task_failed and first:
- display(system(obj.vagrant_version), 'bright gray')
- display(hr, 'bright gray')
+ display(system(obj.vagrant_version), 'bright gray', screen_only=True)
+ display(hr, 'bright gray', screen_only=True)
if msg == '':
if obj.task_failed and not first:
- display(hr, 'bright gray')
+ display(hr, 'bright gray', screen_only=True)
else:
return
else:
if not first:
- display(hr, 'bright gray')
+ display(hr, 'bright gray', screen_only=True)
display(msg, 'red' if obj.task_failed else 'bright purple')
def display_host(obj, result):
diff --git a/lib/trellis/vagrant.rb b/lib/trellis/vagrant.rb
index fa60ad0112..d34f216009 100644
--- a/lib/trellis/vagrant.rb
+++ b/lib/trellis/vagrant.rb
@@ -1,5 +1,5 @@
# Set Ansible paths relative to Ansible directory
-ENV['ANSIBLE_CONFIG'] = ANSIBLE_PATH
+ENV['ANSIBLE_CONFIG'] = File.join(ANSIBLE_PATH, 'ansible.cfg')
ENV['ANSIBLE_CALLBACK_PLUGINS'] = "~/.ansible/plugins/callback:/usr/share/ansible/plugins/callback:#{File.join(ANSIBLE_PATH, 'lib/trellis/plugins/callback')}"
ENV['ANSIBLE_FILTER_PLUGINS'] = "~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter:#{File.join(ANSIBLE_PATH, 'lib/trellis/plugins/filter')}"
ENV['ANSIBLE_LIBRARY'] = "~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules:#{File.join(ANSIBLE_PATH, 'lib/trellis/modules')}"
diff --git a/public_keys/.gitkeep b/public_keys/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/requirements.txt b/requirements.txt
index cd72552dd7..5955db3396 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-ansible>=2.7.12,<=2.8.4
+ansible>=2.10.0
passlib
diff --git a/roles/acceptance-test/tasks/main.yml b/roles/acceptance-test/tasks/main.yml
index 73515895dc..86bd0cded0 100644
--- a/roles/acceptance-test/tasks/main.yml
+++ b/roles/acceptance-test/tasks/main.yml
@@ -31,24 +31,18 @@
login_user: "root"
login_password: "devpw"
-- name: Add Chrome deb to target
- copy:
- src: "google-chrome-stable_current_amd64.deb"
- dest: "/tmp/google-chrome-stable_current_amd64.deb"
+- name: Install Chromium
+ apt:
+ name: chromium-browser
+ state: present
-- name: Install a Google Chrome .deb package
+- name: Install Chromium Chrome Driver
apt:
- deb: "/tmp/google-chrome-stable_current_amd64.deb"
+ name: chromium-chromedriver
+ state: present
- name: Check that the chromedriver exists
stat:
path: /usr/bin/chromedriver
register: chromedriver_present
-- name: Add chromedriver
- copy:
- src: "chromedriver"
- dest: "/usr/bin/chromedriver"
- owner: root
- group: root
- mode: '0777'
diff --git a/roles/bash-for-pressbooks-dev/tasks/main.yml b/roles/bash-for-pressbooks-dev/tasks/main.yml
index 27f4cc9a98..88d22baf0e 100644
--- a/roles/bash-for-pressbooks-dev/tasks/main.yml
+++ b/roles/bash-for-pressbooks-dev/tasks/main.yml
@@ -1,15 +1,19 @@
-- name: Add hh PPA
+- name: Add hstr PPA
apt_repository:
repo: "ppa:ultradvorka/ppa"
update_cache: yes
-- name: Install hh
+ when: ansible_architecture == "x86_64"
+
+- name: Install hstr
apt:
- name: hh
+ name: hstr
state: present
+ when: ansible_architecture == "x86_64"
+
- name: Checkout git aware prompt
become_user: vagrant
git:
- repo: git://github.com/jimeh/git-aware-prompt.git
+ repo: https://github.com/jimeh/git-aware-prompt.git
dest: ~/.bash/git-aware-prompt
- name: Create .bash_aliases file
with_dict: "{{ wordpress_sites }}"
@@ -19,4 +23,4 @@
dest: ~/.bash_aliases
vars:
pathToLogs: "{{ www_root }}/{{ item.key }}/logs"
- pathToCurrent: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}"
\ No newline at end of file
+ pathToCurrent: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}"
diff --git a/roles/common/defaults/main.yml b/roles/common/defaults/main.yml
index 1dcdd38d15..2ba0cb8252 100644
--- a/roles/common/defaults/main.yml
+++ b/roles/common/defaults/main.yml
@@ -20,28 +20,21 @@ site_keys_by_env_pair: "[
{% endfor %}
]"
-_apt_packages_default:
+apt_packages_default:
build-essential: "{{ apt_package_state }}"
curl: "{{ apt_package_state }}"
dbus: "{{ apt_package_state }}"
+ ghostscript: "{{ apt_package_state }}"
git: "{{ apt_package_state }}"
+ imagemagick: "{{ apt_package_state }}"
+ libgs-dev: "{{ apt_package_state }}"
libnss-myhostname: "{{ apt_package_state }}"
- python: "{{ apt_package_state }}"
+ python3: "{{ apt_package_state }}"
+ python3-software-properties: "{{ apt_package_state }}"
+ python3-mysqldb: "{{ apt_package_state }}"
+ python3-pycurl: "{{ apt_package_state }}"
unzip: "{{ apt_package_state }}"
-apt_packages_python:
- '2':
- python-software-properties: "{{ apt_package_state }}"
- python-mysqldb: "{{ apt_package_state }}"
- python-pycurl: "{{ apt_package_state }}"
- '3':
- python3-software-properties: "{{ apt_package_state }}"
- python3-mysqldb: "{{ apt_package_state }}"
- python3-pycurl: "{{ apt_package_state }}"
-
-python_major_version: "{{ ansible_python_version[0] }}"
-apt_packages_default: "{{ _apt_packages_default | combine(apt_packages_python[python_major_version]) }}"
-
apt_packages_custom: {}
apt_packages: "{{ apt_packages_default | combine(apt_packages_custom) }}"
diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml
index ad0488c90b..8035cb64b4 100644
--- a/roles/common/handlers/main.yml
+++ b/roles/common/handlers/main.yml
@@ -9,7 +9,7 @@
- name: reload php-fpm
service:
- name: php7.3-fpm
+ name: php{{ php_version }}-fpm
state: reloaded
- import_tasks: reload_nginx.yml
diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml
index aeff82462d..eaa2ba0aad 100644
--- a/roles/common/tasks/main.yml
+++ b/roles/common/tasks/main.yml
@@ -34,6 +34,18 @@
when: item.value.site_hosts | rejectattr('canonical', 'defined') | list | count
tags: [letsencrypt, wordpress]
+- name: Import PHP version specific vars
+ include_vars: "{{ lookup('first_found', params) }}"
+ vars:
+ params:
+ files:
+ - '{{ php_version }}.yml'
+ - '7.4.yml'
+ paths:
+ - "{{ playbook_dir }}/roles/php/vars/"
+
+ tags: [php, memcached]
+
- name: Verify dict format for apt package component variables
fail:
msg: "{{ lookup('template', 'package_vars_wrong_format_msg.j2') }}"
@@ -61,13 +73,15 @@
memcached_packages: "{{ memcached_packages }}"
php_extensions: "{{ php_extensions }}"
sshd_packages: "{{ sshd_packages }}"
- package_vars_wrong_format: "[{% for k,v in package_vars.items() | list if v | type_debug != 'dict' %}'{{ k }}',{% endfor %}]"
+ package_vars_wrong_format: "[{% for k,v in package_vars.items() if v | type_debug != 'dict' %}'{{ k }}',{% endfor %}]"
tags: [sshd, memcached, php]
- name: Validate Ubuntu version
debug:
msg: |
- Trellis is built for Ubuntu 18.04 Bionic as of https://github.com/roots/trellis/pull/992
+ Ubuntu 18.04 Bionic is the minimum supported version of Ubuntu in Trellis 1.0+ (as of https://github.com/roots/trellis/pull/992)
+
+ 20.04 Focal is the recommend version for Trellis 1.7+ (as of https://github.com/roots/trellis/pull/1197)
Your Ubuntu version is {{ ansible_distribution_version }} {{ ansible_distribution_release }}
@@ -77,16 +91,27 @@
Development via Vagrant: `vagrant destroy && vagrant up`
- Staging/Production: Create a new server with Ubuntu 18.04 and provision
- when: ansible_distribution_release != 'bionic'
+ Staging/Production: Create a new server with Ubuntu 20.04 and provision
+ when: ansible_distribution_version is version('18.04', '<')
- name: Check whether passlib is needed
fail:
msg: |
- Ansible on OS X requires python passlib module to create user password hashes
+ Ansible on macOS requires Python's passlib module to create user password hashes
+
+ If you're seeing this error message, you likely didn't use trellis-cli to create your project.
+ We highly recommend installing and using trellis-cli to manage your Trellis projects.
+
+ See https://github.com/roots/trellis-cli for more documentation.
+
+ For existing projects, you can run `trellis init` which will manage the dependencies automatically and fix this problem
+ as long as you use the `trellis` commands (like `trellis provision`) afterwards.
+
+ To fix this manually, use pip to install the package: pip install passlib
+
+ If pip is not installed, you'll have to install it first.
+ See https://stackoverflow.com/questions/17271319/how-do-i-install-pip-on-macos-or-os-x for many options.
- sudo easy_install pip
- pip install passlib
when: env != 'development' and darwin_without_passlib | default(false)
run_once: true
@@ -147,13 +172,14 @@
generate_ssh_key: yes
when: env == 'development'
-- name: Retrieve SSH client IP
- block:
- - ipify_facts:
+- block:
+ - name: Retrieve SSH client IP
+ ipify_facts:
delegate_to: localhost
become: no
when: env != 'development' and ssh_client_ip_lookup | default(true)
tags: [fail2ban, ferm]
rescue:
- - fail:
+ - name: Fail when unable to retrieve SSH client IP
+ fail:
msg: "External IP resolution failed. Check that your DNS servers are working. Try to disable DNSCrypt if you are using it."
diff --git a/roles/common/tasks/reload_nginx.yml b/roles/common/tasks/reload_nginx.yml
index 952a6082e0..9d30718aa9 100644
--- a/roles/common/tasks/reload_nginx.yml
+++ b/roles/common/tasks/reload_nginx.yml
@@ -2,6 +2,7 @@
- name: reload nginx
command: nginx -t
notify: "{{ (role_path | basename == 'common') | ternary('perform nginx reload', omit) }}"
+ changed_when: true
- name: perform nginx reload
service:
diff --git a/roles/connection/tasks/main.yml b/roles/connection/tasks/main.yml
index 0675e77bf4..8acdef9606 100644
--- a/roles/connection/tasks/main.yml
+++ b/roles/connection/tasks/main.yml
@@ -12,13 +12,14 @@
register: preferred_host_key_algorithms
when:
- dynamic_host_key_algorithms | default(true)
- - ansible_ssh_extra_args | default('') == ''
+ - not ansible_ssh_extra_args | default(None)
- not (ansible_host_known or ssh_config_host_known)
- name: Check whether Ansible can connect as {{ dynamic_user | default(true) | ternary('root', web_user) }}
- local_action: |
- command ansible {{ inventory_hostname }} -m raw -a whoami
+ command: |
+ ansible {{ inventory_hostname }} -m raw -a whoami
-u {{ dynamic_user | default(true) | ternary('root', web_user) }} {{ cli_options | default('') }} -vvvv
+ delegate_to: localhost
environment:
ANSIBLE_SSH_ARGS: "{{ ssh_args_default }} {{ ansible_ssh_extra_args | default('') }}"
failed_when: false
diff --git a/roles/deploy/defaults/main.yml b/roles/deploy/defaults/main.yml
index 8e04496fff..329075b9de 100644
--- a/roles/deploy/defaults/main.yml
+++ b/roles/deploy/defaults/main.yml
@@ -2,7 +2,7 @@
# - you must set a repository (no default)
project_git_repo: "{{ project.repo }}"
# - you can set the git ref to deploy (can be a branch, tag or commit hash)
-project_version: "{{ project.branch | default('master') }}"
+project_version: "{{ branch is defined | ternary(branch, project.branch) | default('master') }}"
# The source_path is used to fetch the tags from git, or synchronise via rsync. This way
# you do not have to download/sync the entire project on every deploy
@@ -20,6 +20,7 @@ project_templates:
- name: .env config
src: roles/deploy/templates/env.j2
dest: .env
+ mode: '0600'
# The shared_children is a list of all files/folders in your project that need to be linked to a path in `/shared`.
# For example a sessions directory or an uploads folder. They are created if they don't exist, with the type
@@ -28,10 +29,10 @@ project_templates:
# project_shared_children:
# - path: app/sessions
# src: sessions
-# mode: '0755' // <- optional, must be quoted, defaults to `'0755'` if `directory` or `'0644'` if `file`
-# type: directory // <- optional, defaults to `directory`, options: `directory` or `file`
+# mode: '0755' // <- optional, use an octal number starting with 0 or quote it, defaults to `'0755'` if `directory` or `'0644'` if `file`
+# type: directory // <- optional, defaults to `directory`, options: `directory` or `file`
project_shared_children:
- - path: web/app/uploads
+ - path: "{{ project_public_path }}/{{ project_upload_path }}"
src: uploads
# The project_environment is a list of environment variables that can be used in hooks
@@ -48,10 +49,17 @@ project_current_path: "{{ project.current_path | default('current') }}"
# Whether to run `wp core update-db` at end of each deploy
update_db_on_deploy: true
+# Most scripts are used in development instead of remote servers. Use with caution.
+composer_no_scripts: true
+# Whether to autoload classes from classmap only.
+composer_classmap_authoritative: true
+
# Helpers
project: "{{ wordpress_sites[site] }}"
project_root: "{{ www_root }}/{{ site }}"
project_local_path: "{{ (lookup('env', 'USER') == 'vagrant') | ternary(project_root + '/' + project_current_path, project.local_path) }}"
+project_public_path: "{{ project.public_path | default('web') }}"
+project_upload_path: "{{ project.upload_path | default('app/uploads') }}"
# Deploy hooks
@@ -61,6 +69,7 @@ deploy_build_before:
deploy_build_after:
- "{{ playbook_dir }}/roles/deploy/hooks/build-after.yml"
+ - "{{ playbook_dir }}/deploy-hooks/build-after.yml"
# - "{{ playbook_dir }}/deploy-hooks/sites/{{ site }}-build-after.yml"
deploy_finalize_before:
diff --git a/roles/deploy/files/tmp_multisite_constants.php b/roles/deploy/files/tmp_multisite_constants.php
deleted file mode 100644
index e468cc8bfc..0000000000
--- a/roles/deploy/files/tmp_multisite_constants.php
+++ /dev/null
@@ -1,7 +0,0 @@
- 0 or wp_installed_singlesite.rc > 1
+
+ - name: Set "WordPress installed?" result variable (from non-multisite)
+ set_fact:
+ wp_installed: "{{ wp_installed_singlesite }}"
+ when:
+ - not project.multisite.enabled | default(false)
+
+- name: WordPress Installed (multisite)?
+ block:
+ - name: Set variables used in "WordPress Installed (multisite)?" check
+ set_fact:
+ multisite_non_setup_db_error: "WordPress database error Table '{{ site_env.db_name }}.wp_blogs' doesn't exist"
+
+ - name: "Invoke 'wp core is-installed' command"
+ command: wp core is-installed --skip-plugins --skip-themes
+ args:
+ chdir: "{{ deploy_helper.new_release_path }}"
+ register: wp_installed_multisite
+ changed_when: false
+ failed_when: (wp_installed_multisite.stderr | length > 0 and wp_installed_multisite.stderr is not match(multisite_non_setup_db_error)) or wp_installed_multisite.rc > 1
+
+ - name: Set "WordPress installed?" result variable (from multisite)
+ set_fact:
+ wp_installed: "{{ wp_installed_multisite }}"
+ when:
+ - project.multisite.enabled | default(false)
-- name: WordPress Installed?
- command: wp core is-installed --skip-plugins --skip-themes --require={{ deploy_helper.shared_path }}/tmp_multisite_constants.php
- args:
- chdir: "{{ deploy_helper.new_release_path }}"
- register: wp_installed
- changed_when: false
- failed_when: wp_installed.stderr | default("") != "" or wp_installed.rc > 1
- name: Get WP theme template and stylesheet roots
shell: >
diff --git a/roles/deploy/tasks/build.yml b/roles/deploy/tasks/build.yml
index 0151fc0d8c..aa0e043e69 100644
--- a/roles/deploy/tasks/build.yml
+++ b/roles/deploy/tasks/build.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_build_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_build_before_paths
with_items: "{{ deploy_build_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_build_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-build-before
- name: Copy project templates
@@ -19,12 +27,24 @@
with_items: "{{ project.project_copy_folders | default(project_copy_folders) }}"
- name: Copy project folders
- command: cp -rp {{ deploy_helper.current_path }}/{{ item.item }} {{ deploy_helper.new_release_path }}
+ copy:
+ src: "{{ deploy_helper.current_path }}/{{ item.item }}/"
+ dest: "{{ deploy_helper.new_release_path }}/{{ item.item }}"
+ remote_src: true
+ mode: 'preserve'
with_items: "{{ project_folder_paths.results }}"
when: item.stat.exists
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_build_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_build_after_paths
with_items: "{{ deploy_build_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_build_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-build-after
diff --git a/roles/deploy/tasks/finalize.yml b/roles/deploy/tasks/finalize.yml
index 7ec6e32944..824c44f3e5 100644
--- a/roles/deploy/tasks/finalize.yml
+++ b/roles/deploy/tasks/finalize.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_finalize_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_finalize_before_paths
with_items: "{{ deploy_finalize_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_finalize_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-finalize-before
- name: Finalize the deploy
@@ -13,10 +21,18 @@
state: finalize
keep_releases: "{{ project.deploy_keep_releases | default(deploy_keep_releases | default(omit)) }}"
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_finalize_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_finalize_after_paths
with_items: "{{ deploy_finalize_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_finalize_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-finalize-after
- debug:
diff --git a/roles/deploy/tasks/initialize.yml b/roles/deploy/tasks/initialize.yml
index a78fefab90..5b1d48cc16 100644
--- a/roles/deploy/tasks/initialize.yml
+++ b/roles/deploy/tasks/initialize.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_initialize_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_initialize_before_paths
with_items: "{{ deploy_initialize_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_initialize_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-initialize-before
- name: Initialize
@@ -11,8 +19,16 @@
path: "{{ project_root }}"
state: present
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_initialize_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_initialize_after_paths
with_items: "{{ deploy_initialize_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_initialize_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-initialize-after
diff --git a/roles/deploy/tasks/main.yml b/roles/deploy/tasks/main.yml
index 2e6b5c27f7..5478676a7b 100644
--- a/roles/deploy/tasks/main.yml
+++ b/roles/deploy/tasks/main.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_before_paths
with_items: "{{ deploy_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-before
- import_tasks: initialize.yml
@@ -12,8 +20,16 @@
- import_tasks: share.yml
- import_tasks: finalize.yml
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_after_paths
with_items: "{{ deploy_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-after
diff --git a/roles/deploy/tasks/prepare.yml b/roles/deploy/tasks/prepare.yml
index 9181b43f51..2567d7986e 100644
--- a/roles/deploy/tasks/prepare.yml
+++ b/roles/deploy/tasks/prepare.yml
@@ -1,15 +1,18 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_prepare_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_prepare_before_paths
with_items: "{{ deploy_prepare_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_prepare_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-prepare-before
-- name: write unfinished file
- file:
- path: "{{ project_source_path }}/{{ deploy_helper.unfinished_filename }}"
- state: touch
-
- name: Check for project repo subtree
stat:
path: "{{ project_source_path }}/{{ project.repo_subtree_path }}"
@@ -24,22 +27,43 @@
- name: Create new release dir
file:
path: "{{ deploy_helper.new_release_path }}"
+ mode: '0755'
state: directory
- name: Run git archive to populate new build dir
- shell: git archive {{ project_version }} | tar xf - -C {{ deploy_helper.new_release_path }}
+ shell: |
+ set -eo pipefail
+ git archive {{ project_version }} | tar xf - -C {{ deploy_helper.new_release_path }}
args:
chdir: "{{ project_source_path }}"
+ executable: /bin/bash
when: project.repo_subtree_path is not defined
- name: Run git archive with subdirectory to populate new build dir
- shell: git archive {{ project_version }} {{ project.repo_subtree_path }} | tar -x --strip-components {{ project.repo_subtree_path.split('/') | count }} -f - -C {{ deploy_helper.new_release_path }}
+ shell: |
+ set -eo pipefail
+ git archive {{ project_version }} {{ project.repo_subtree_path }} | tar -x --strip-components {{ project.repo_subtree_path.split('/') | count }} -f - -C {{ deploy_helper.new_release_path }}
args:
chdir: "{{ project_source_path }}"
+ executable: /bin/bash
when: project.repo_subtree_path is defined
-- include_tasks: "{{ include_path }}"
+- name: write unfinished file
+ file:
+ path: "{{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }}"
+ mode: '0744'
+ state: touch
+
+- name: Check if deploy_prepare_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_prepare_after_paths
with_items: "{{ deploy_prepare_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_prepare_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-prepare-after
diff --git a/roles/deploy/tasks/share.yml b/roles/deploy/tasks/share.yml
index 294c05d4b9..12da9836c2 100644
--- a/roles/deploy/tasks/share.yml
+++ b/roles/deploy/tasks/share.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_share_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_share_before_paths
with_items: "{{ deploy_share_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_share_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-share-before
- name: Ensure shared sources are present -- directories
@@ -32,6 +40,7 @@
- name: Ensure parent directories for shared paths are present
file:
path: "{{ deploy_helper.new_release_path }}/{{ item.path | dirname }}"
+ mode: '0777'
state: directory
with_items: "{{ project.project_shared_children | default(project_shared_children) }}"
@@ -48,8 +57,16 @@
state: link
with_items: "{{ project.project_shared_children | default(project_shared_children) }}"
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_share_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_share_after_paths
with_items: "{{ deploy_share_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_share_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-share-after
diff --git a/roles/deploy/tasks/update.yml b/roles/deploy/tasks/update.yml
index 4170f658db..92ac949b00 100644
--- a/roles/deploy/tasks/update.yml
+++ b/roles/deploy/tasks/update.yml
@@ -1,8 +1,16 @@
---
-- include_tasks: "{{ include_path }}"
+- name: Check if deploy_update_before scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_update_before_paths
with_items: "{{ deploy_update_before | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_update_before_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-update-before
- name: Add known_hosts
@@ -27,14 +35,32 @@
- name: Failed connection to remote repo
fail:
msg: |
- Git repo {{ project.repo }} cannot be accessed. Please verify the repository exists and you have SSH forwarding set up correctly.
+ Git repo {{ project.repo }} on branch {{ project_version }} cannot be accessed. Please verify the repository/branch are correct and you have SSH forwarding set up correctly.
More info:
> https://roots.io/trellis/docs/deploys/#ssh-keys
> https://roots.io/trellis/docs/ssh-keys/#cloning-remote-repo-using-ssh-agent-forwarding
+
+ Error:
+ {{ git_clone.stderr }}
when: git_clone is failed
-- include_tasks: "{{ include_path }}"
+- name: Remove untracked files from project folder
+ command: git clean -fdx
+ args:
+ chdir: "{{ project_source_path }}"
+ register: git_clean
+ changed_when: not not(git_clean.stdout)
+
+- name: Check if deploy_update_after scripts exist
+ stat:
+ path: "{{ item }}"
+ delegate_to: localhost
+ register: deploy_update_after_paths
with_items: "{{ deploy_update_after | default([]) }}"
+
+- include_tasks: "{{ include_path.item }}"
+ with_items: "{{ deploy_update_after_paths.results }}"
loop_control:
loop_var: include_path
+ when: include_path.stat.exists
tags: deploy-update-after
diff --git a/roles/deploy/vars/main.yml b/roles/deploy/vars/main.yml
index aea2f70a6e..1a7612f9bb 100644
--- a/roles/deploy/vars/main.yml
+++ b/roles/deploy/vars/main.yml
@@ -7,5 +7,9 @@ wordpress_env_defaults:
wp_home: "{{ project.ssl.enabled | default(false) | ternary('https', 'http') }}://{{ project.site_hosts | map(attribute='canonical') | first }}"
wp_siteurl: "{{ project.ssl.enabled | default(false) | ternary('https', 'http') }}://{{ project.site_hosts | map(attribute='canonical') | first }}/wp"
domain_current_site: "{{ project.site_hosts | map(attribute='canonical') | first }}"
+ git_sha: "{{ git_clone.after }}"
+ release_version: "{{ deploy_helper.new_release }}"
+ wp_debug_log: "{{ project_root }}/logs/debug.log"
+ wp_post_revisions: true
site_env: "{{ wordpress_env_defaults | combine(vault_wordpress_env_defaults | default({}), project.env | default({}), vault_wordpress_sites[site].env) }}"
diff --git a/roles/epubcheck/defaults/main.yml b/roles/epubcheck/defaults/main.yml
deleted file mode 100644
index 4b7cd15dc8..0000000000
--- a/roles/epubcheck/defaults/main.yml
+++ /dev/null
@@ -1,2 +0,0 @@
----
-epubcheck_release_url: https://github.com/w3c/epubcheck/releases/download/v4.1.1/epubcheck-4.1.1.zip
diff --git a/roles/epubcheck/tasks/main.yml b/roles/epubcheck/tasks/main.yml
index bd1ce24e01..8a6c13783a 100644
--- a/roles/epubcheck/tasks/main.yml
+++ b/roles/epubcheck/tasks/main.yml
@@ -1,13 +1,4 @@
----
-- name: Download epubcheck
- unarchive:
- src: "{{ epubcheck_release_url }}"
- dest: /tmp
- remote_src: yes
-
-- name: Sync epubcheck into place
- synchronize:
- src: /tmp/{{ epubcheck_release_url[:-4] | basename }}/
- dest: /opt/epubcheck
- delete: yes
- delegate_to: "{{ inventory_hostname }}"
+- name: Install EpubCheck
+ apt:
+ name: epubcheck
+ state: present
diff --git a/roles/fail2ban/README.md b/roles/fail2ban/README.md
index a3aa07b68e..16b707fc8a 100644
--- a/roles/fail2ban/README.md
+++ b/roles/fail2ban/README.md
@@ -1,19 +1,17 @@
-## What is ansible-fail2ban?
+## What is this role?
-It is an [ansible](http://www.ansible.com/home) role to install and configure fail2ban.
+This role installs and configures [Fail2ban](https://github.com/fail2ban/fail2ban).
-### What problem does it solve and why is it useful?
-
-Security is important and fail2ban is an excellent tool to harden your server with minimal or even no configuration.
+Fail2ban is an excellent tool to harden your server with minimal configuration.
## Role variables
-Below is a list of default values along with a description of what they do.
+Below is a list of available variables, their description and their default value within Trellis.
-```
+```yaml
# Which log level should it be output as?
-# Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG. Default: ERROR
-fail2ban_loglevel: WARNING
+# Levels: CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
+fail2ban_loglevel: INFO
# Where should log outputs be sent to?
# SYSLOG, STDERR, STDOUT, file
@@ -56,10 +54,25 @@ fail2ban_chain: INPUT
# action_, action_mw, action_mwl
fail2ban_action: action_
-# What services should fail2ban monitor?
-# You can define fail2ban_services as an empty string to not monitor anything.
+# Trellis by default only monitors SSH connections
+# For available parameters, see fail2ban_services_custom below.
+fail2ban_services_default:
+ - name: ssh
+ port: ssh
+ filter: sshd
+ logpath: /var/log/auth.log
+
+# In which folder did you place custom filters?
+# Filters MUST have .conf.j2 extension to copied to the servers.
+fail2ban_filter_templates_path: fail2ban_filters
+```
+
+The following list variable is available for custom services (to be set up in `group_vars`):
+
+```yaml
+# Which additional services should fail2ban monitor?
# You can define multiple services as a standard yaml list.
-fail2ban_services:
+fail2ban_services_custom:
# The name of the service
# REQUIRED.
- name: ssh
@@ -77,11 +90,11 @@ fail2ban_services:
# OPTIONAL: Defaults to the protocol listed above.
protocol: tcp
- # What filter should it use?
+ # Which filter should it use?
# REQUIRED.
filter: sshd
- # Which log path should it monitor?
+ # Which log file should it monitor?
# REQUIRED.
logpath: /var/log/auth.log
@@ -96,24 +109,29 @@ fail2ban_services:
# How should the ban be applied?
# OPTIONAL: Defaults to the banaction listed above.
banaction: iptables-multiport
+
```
-## Example playbook
+## Custom Settings
-Let's say you want to edit a few values, you can do this by opening `group_vars/all` and then add the following:
+To add services, you might add the following to `group_vars/all/security.yml`, e.g.:
-```
-fail2ban_services:
- - name: ssh
- port: ssh
- filter: sshd
+```yaml
+fail2ban_services_custom:
+ - name: wordpress
+ filter: wordpress
logpath: /var/log/auth.log
- - name: postfix
- port: smtp,ssmtp
- filter: postfix
- logpath: /var/log/mail.log
+ maxretry: 2
```
+To add the corresponding filter, add it to the folder specified in `fail2ban_filter_templates_path`, i.e. `fail2ban_filters` per default (next to the `group_vars` folder). The filter configuration must be of `.conf.j2` extension for Trellis to recognize it.
+
+Filters might be provided by plugins as `.conf` files: it is then enough to simply append the file name with `.j2`. It is not required to modify these provided filters, but you may customize them to your liking.
+
+To develop custom filters, refer to the Fail2ban wiki: [How Fail2ban works](https://github.com/fail2ban/fail2ban/wiki/How-fail2ban-works) and [How to ban something…](https://github.com/fail2ban/fail2ban/wiki/How-to-ban-something-other-as-host-(IP-address),-like-user-or-mail,-etc.) for simple filter rules or [Developing Filters](https://fail2ban.readthedocs.io/en/latest/filters.html) for complex setups.
+
+If you need to edit the default services, copy the `fail2ban_services_default` list from `roles/fail2ban/defaults/main.yml` to `group_vars/all/security.yml` and edit as needed.
+
## Attribution
Many thanks to [nickjj](https://github.com/nickjj/) for creating the [original version](https://github.com/nickjj/ansible-fail2ban/) of this role.
diff --git a/roles/fail2ban/defaults/main.yml b/roles/fail2ban/defaults/main.yml
index 473ae8a0d5..598172cfbd 100644
--- a/roles/fail2ban/defaults/main.yml
+++ b/roles/fail2ban/defaults/main.yml
@@ -5,7 +5,7 @@ fail2ban_loglevel: INFO
fail2ban_logtarget: /var/log/fail2ban.log
fail2ban_socket: /var/run/fail2ban/fail2ban.sock
-fail2ban_ignoreip: 127.0.0.1/8 {{ ip_whitelist | join(' ') }}
+fail2ban_ignoreip: "{{ ip_whitelist | join(' ') }}"
fail2ban_bantime: 600
fail2ban_maxretry: 6
@@ -19,8 +19,13 @@ fail2ban_chain: INPUT
fail2ban_action: action_
-fail2ban_services:
+fail2ban_services_default:
- name: ssh
port: ssh
filter: sshd
logpath: /var/log/auth.log
+
+fail2ban_services_custom: []
+fail2ban_services: "{{ fail2ban_services_default + fail2ban_services_custom }}"
+
+fail2ban_filter_templates_path: fail2ban_filters
diff --git a/roles/fail2ban/tasks/main.yml b/roles/fail2ban/tasks/main.yml
index dcc6adc5ab..2a900a2a87 100644
--- a/roles/fail2ban/tasks/main.yml
+++ b/roles/fail2ban/tasks/main.yml
@@ -11,12 +11,37 @@
template:
src: "{{ item }}.j2"
dest: /etc/fail2ban/{{ item }}
+ mode: '0644'
with_items:
- jail.local
- fail2ban.local
notify:
- restart fail2ban
+- name: build list of fail2ban filter templates
+ find:
+ paths:
+ - "{{ playbook_dir }}/roles/fail2ban/templates/filters"
+ - "{{ fail2ban_filter_templates_path }}"
+ pattern: "*.conf.j2"
+ become: no
+ delegate_to: localhost
+ register: fail2ban_filter_templates
+
+- name: ensure configuration directory exists
+ file:
+ path: /etc/fail2ban/filter.d/
+ state: directory
+ mode: '0755'
+
+- name: template fail2ban filters
+ template:
+ src: "{{ item }}"
+ dest: "/etc/fail2ban/filter.d/{{ item | basename | regex_replace('.j2$', '') }}"
+ mode: '0644'
+ with_items: "{{ fail2ban_filter_templates.files | map(attribute='path') | list | sort(True) }}"
+ notify: restart fail2ban
+
- name: ensure fail2ban starts on a fresh reboot
service:
name: fail2ban
diff --git a/roles/fail2ban/templates/filters/wordpress-wp-login.conf.j2 b/roles/fail2ban/templates/filters/wordpress-wp-login.conf.j2
new file mode 100644
index 0000000000..d0f9271098
--- /dev/null
+++ b/roles/fail2ban/templates/filters/wordpress-wp-login.conf.j2
@@ -0,0 +1,2 @@
+[Definition]
+failregex = ^ .* "POST .*wp-login\.php
diff --git a/roles/fail2ban/templates/filters/wordpress-xmlrpc.conf.j2 b/roles/fail2ban/templates/filters/wordpress-xmlrpc.conf.j2
new file mode 100644
index 0000000000..6d8547146f
--- /dev/null
+++ b/roles/fail2ban/templates/filters/wordpress-xmlrpc.conf.j2
@@ -0,0 +1,2 @@
+[Definition]
+failregex = ^ .* "POST .*xmlrpc\.php
diff --git a/roles/ferm/handlers/main.yml b/roles/ferm/handlers/main.yml
index 16985eb8f5..2af34d8500 100644
--- a/roles/ferm/handlers/main.yml
+++ b/roles/ferm/handlers/main.yml
@@ -1,4 +1,4 @@
---
- name: restart ferm
service: name=ferm state=restarted
- when: ferm_enabled
\ No newline at end of file
+ when: ferm_enabled | bool
diff --git a/roles/ferm/tasks/main.yml b/roles/ferm/tasks/main.yml
index 63b0b0a4d0..2848cb488a 100644
--- a/roles/ferm/tasks/main.yml
+++ b/roles/ferm/tasks/main.yml
@@ -19,7 +19,7 @@
file:
path: "{{ item }}"
state: directory
- mode: 0750
+ mode: '0750'
with_items:
- /etc/ferm/ferm.d
- /etc/ferm/filter-input.d
@@ -28,6 +28,7 @@
template:
src: "{{ item }}.j2"
dest: /{{ item }}
+ mode: '0644'
with_items:
- etc/default/ferm
- etc/ferm/ferm.conf
@@ -55,6 +56,7 @@
{% else %}
dest=/etc/ferm/filter-input.d/{{ item.weight | default('50') }}_{{ item.type }}_{{ item.dport[0] }}.conf
{% endif %}
+ mode=0644
with_flattened:
- "{{ ferm_input_list }}"
- "{{ ferm_input_group_list }}"
@@ -65,7 +67,7 @@
- name: ensure iptables rules are enabled
command: ferm --slow /etc/ferm/ferm.conf
changed_when: false
- when: ferm_enabled
+ when: ferm_enabled | bool
- name: ensure iptables rules are disabled
command: ferm --flush /etc/ferm/ferm.conf
diff --git a/roles/letsencrypt/defaults/main.yml b/roles/letsencrypt/defaults/main.yml
index 4b9d9b26b8..f900239dbb 100644
--- a/roles/letsencrypt/defaults/main.yml
+++ b/roles/letsencrypt/defaults/main.yml
@@ -1,5 +1,5 @@
sites_using_letsencrypt: "[{% for name, site in wordpress_sites.items() | list if site.ssl.enabled and site.ssl.provider | default('manual') == 'letsencrypt' %}'{{ name }}',{% endfor %}]"
-site_uses_letsencrypt: ssl_enabled and item.value.ssl.provider | default('manual') == 'letsencrypt'
+site_uses_letsencrypt: "{{ (ssl_enabled and item.value.ssl.provider | default('manual') == 'letsencrypt') | bool }}"
missing_hosts: "{{ site_hosts | difference((current_hosts.results | selectattr('item.key', 'equalto', item.key) | selectattr('stdout_lines', 'defined') | sum(attribute='stdout_lines', start=[]) | map('trim') | list | join(' ')).split(' ')) }}"
letsencrypt_cert_ids: "{ {% for item in (generate_cert_ids | default({'results':[{'skipped':True}]})).results if item is not skipped %}'{{ item.item.key }}':'{{ item.stdout }}', {% endfor %} }"
@@ -28,6 +28,10 @@ letsencrypt_ca: 'https://acme-v02.api.letsencrypt.org'
letsencrypt_account_key: '{{ acme_tiny_data_directory }}/account.key'
+letsencrypt_intermediate_cert_path: /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem
+letsencrypt_intermediate_cert_url: 'https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem'
+letsencrypt_intermediate_cert_sha256sum: 'e446c5e9dbef9d09ac9f7027c034602492437a05ff6c40011d7235fca639c79a'
+
letsencrypt_keys_dir: "{{ nginx_ssl_path }}/letsencrypt"
letsencrypt_certs_dir: "{{ nginx_ssl_path }}/letsencrypt"
diff --git a/roles/letsencrypt/library/test_challenges.py b/roles/letsencrypt/library/test_challenges.py
index d7f4a8cc2c..8d5899e745 100644
--- a/roles/letsencrypt/library/test_challenges.py
+++ b/roles/letsencrypt/library/test_challenges.py
@@ -1,13 +1,8 @@
-#!/usr/bin/python
+#!/usr/bin/python3
# -*- coding: utf-8 -*-
import socket
-
-try:
- from httplib import HTTPConnection, HTTPException
-except ImportError:
- # Python 3
- from http.client import HTTPConnection, HTTPException
+from http.client import HTTPConnection, HTTPException
DOCUMENTATION = '''
---
diff --git a/roles/letsencrypt/tasks/certificates.yml b/roles/letsencrypt/tasks/certificates.yml
index 480d910253..110af4219e 100644
--- a/roles/letsencrypt/tasks/certificates.yml
+++ b/roles/letsencrypt/tasks/certificates.yml
@@ -9,20 +9,23 @@
- name: Ensure correct permissions on private keys
file:
path: "{{ letsencrypt_keys_dir }}/{{ item.key }}.key"
- mode: 0600
+ mode: '0600'
when: site_uses_letsencrypt
with_dict: "{{ wordpress_sites }}"
- name: Generate Lets Encrypt certificate IDs
shell: |
+ set -eo pipefail
echo "{{ [site_hosts | join(' '), letsencrypt_ca, acme_tiny_commit] | join('\n') }}" |
cat {{ letsencrypt_account_key }} {{ letsencrypt_keys_dir }}/{{ item.key }}.key - |
md5sum | cut -c -7
+ args:
+ executable: /bin/bash
register: generate_cert_ids
changed_when: false
when: site_uses_letsencrypt
with_dict: "{{ wordpress_sites }}"
- tags: [wordpress, wordpress-setup, nginx-includes, nginx-sites]
+ tags: [wordpress, wordpress-setup, wordpress-setup-nginx, nginx-includes]
- name: Generate CSRs
shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ site_hosts | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}-{{ letsencrypt_cert_ids[item.key] }}.csr"
@@ -36,7 +39,8 @@
template:
src: renew-certs.py
dest: "{{ acme_tiny_data_directory }}/renew-certs.py"
- mode: 0700
+ mode: '0700'
+ tags: [wordpress, wordpress-setup, wordpress-setup-nginx, nginx-includes]
- name: Generate the certificates
command: ./renew-certs.py
@@ -45,3 +49,4 @@
register: generate_certs
changed_when: generate_certs.stdout is defined and 'Created' in generate_certs.stdout
notify: reload nginx
+ tags: [wordpress, wordpress-setup, wordpress-setup-nginx, nginx-includes]
diff --git a/roles/letsencrypt/tasks/main.yml b/roles/letsencrypt/tasks/main.yml
index 27c4b86ac7..b65a534087 100644
--- a/roles/letsencrypt/tasks/main.yml
+++ b/roles/letsencrypt/tasks/main.yml
@@ -8,8 +8,8 @@
cron_file: letsencrypt-certificate-renewal
name: letsencrypt certificate renewal
user: root
- job: cd {{ acme_tiny_data_directory }} && ./renew-certs.py && /usr/sbin/service nginx reload
+ job: cd {{ acme_tiny_data_directory }} && ./renew-certs.py ; /usr/sbin/service nginx reload
day: "{{ letsencrypt_cronjob_daysofmonth }}"
- hour: 4
- minute: 30
+ hour: "4"
+ minute: "30"
state: present
diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml
index c0578e4933..877ed09c92 100644
--- a/roles/letsencrypt/tasks/nginx.yml
+++ b/roles/letsencrypt/tasks/nginx.yml
@@ -3,6 +3,7 @@
template:
src: acme-challenge-location.conf.j2
dest: "{{ nginx_path }}/acme-challenge-location.conf"
+ mode: '0644'
- name: Get list of hosts in current Nginx conf
shell: |
@@ -17,6 +18,7 @@
template:
src: nginx-challenge-site.conf.j2
dest: "{{ nginx_path }}/sites-available/letsencrypt-{{ item.key }}.conf"
+ mode: '0644'
register: challenge_site_confs
when:
- site_uses_letsencrypt
@@ -39,10 +41,10 @@
when: challenge_site_confs is changed or challenge_sites_enabled is changed
- name: Create test Acme Challenge file
- shell: touch {{ acme_tiny_challenges_directory }}/ping.txt
- args:
- creates: "{{ acme_tiny_challenges_directory }}/ping.txt"
- warn: false
+ file:
+ path: "{{ acme_tiny_challenges_directory }}/ping.txt"
+ state: touch
+ mode: '0644'
- name: Test Acme Challenges
test_challenges:
diff --git a/roles/letsencrypt/tasks/setup.yml b/roles/letsencrypt/tasks/setup.yml
index 6d3cd762aa..c23ba5918d 100644
--- a/roles/letsencrypt/tasks/setup.yml
+++ b/roles/letsencrypt/tasks/setup.yml
@@ -1,4 +1,26 @@
---
+- name: Fail if letsencrypt_contact_emails is not defined
+ fail:
+ msg: >
+ Error: the required `letsencrypt_contact_emails` variable is not defined or invalid.
+
+
+ Please define it in `groups_vars/all/main.yml` with at least one email (as a list/array, *not* a string):
+
+ letsencrypt_contact_emails:
+ - changeme@example.com
+
+ The contact email is used by Let's Encrypt to send expiry notices when a certificate is coming up for renewal.
+
+
+ See https://letsencrypt.org/docs/expiration-emails/ for more information.
+
+
+ Since Trellis attempts to renew certificates after {{ letsencrypt_min_renewal_age }} days ({{ 90 - letsencrypt_min_renewal_age }} days before renewal),
+ getting an expiry notice email means something has gone wrong giving you enough notice to fix the problem.
+
+ when: (letsencrypt_contact_emails is not defined) or (letsencrypt_contact_emails is string)
+
- name: Create directories and set permissions
file:
mode: "{{ item.mode | default(omit) }}"
@@ -24,12 +46,14 @@
copy:
src: "{{ letsencrypt_account_key_source_file }}"
dest: "{{ letsencrypt_account_key }}"
+ mode: '0700'
when: letsencrypt_account_key_source_file is defined
- name: Copy Lets Encrypt account key source contents
copy:
content: "{{ letsencrypt_account_key_source_content | trim }}"
dest: "{{ letsencrypt_account_key }}"
+ mode: '0700'
when: letsencrypt_account_key_source_content is defined
- name: Generate a new account key
diff --git a/roles/letsencrypt/templates/renew-certs.py b/roles/letsencrypt/templates/renew-certs.py
index 6d4b5f0304..b13ed8efa6 100644
--- a/roles/letsencrypt/templates/renew-certs.py
+++ b/roles/letsencrypt/templates/renew-certs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import os
import sys
@@ -10,38 +10,52 @@
letsencrypt_cert_ids = {{ letsencrypt_cert_ids }}
for site in {{ sites_using_letsencrypt }}:
- bundled_cert_path = os.path.join('{{ letsencrypt_certs_dir }}', site + '-' + letsencrypt_cert_ids[site] + '-bundled.cert')
+ csr_path = os.path.join('{{ acme_tiny_data_directory }}', 'csrs', '{}-{}.csr'.format(site, letsencrypt_cert_ids[site]))
+ bundled_cert_path = os.path.join('{{ letsencrypt_certs_dir }}', '{}-bundled.cert'.format(site))
+ bundled_hashed_cert_path = os.path.join('{{ letsencrypt_certs_dir }}', '{}-{}-bundled.cert'.format(site, letsencrypt_cert_ids[site]))
- if os.access(bundled_cert_path, os.F_OK):
- stat = os.stat(bundled_cert_path)
- print('Certificate file ' + bundled_cert_path + ' already exists')
+ # Generate or update root cert if needed
+ if not os.access(csr_path, os.F_OK):
+ failed = True
+ print('The required CSR file {} does not exist. This could happen if you changed site_hosts and have '
+ 'not yet rerun the letsencrypt role. Create the CSR file by re-provisioning (running the Trellis '
+ 'server.yml playbook) with `--tags letsencrypt`'.format(csr_path), file=sys.stderr)
+ continue
- if time.time() - stat.st_mtime < {{ letsencrypt_min_renewal_age }} * 86400:
- print(' The certificate is younger than {{ letsencrypt_min_renewal_age }} days. Not creating a new certificate.\n')
- continue
+ elif os.access(bundled_hashed_cert_path, os.F_OK) and time.time() - os.stat(bundled_hashed_cert_path).st_mtime < {{ letsencrypt_min_renewal_age }} * 86400:
+ print('Certificate file {} already exists and is younger than {{ letsencrypt_min_renewal_age }} days. '
+ 'Not creating a new certificate.'.format(bundled_hashed_cert_path))
- print('Generating certificate for ' + site)
+ else:
+ cmd = ('/usr/bin/env python3 {{ acme_tiny_software_directory }}/acme_tiny.py '
+ '--quiet '
+ '--ca {{ letsencrypt_ca }} '
+ '--account-key {{ letsencrypt_account_key }} '
+ '--csr {} '
+ '--contact {{ letsencrypt_contact_emails | map('regex_replace', '(^.*$)', 'mailto:\\1') | join (' ') }} '
+ '--acme-dir {{ acme_tiny_challenges_directory }}'
+ ).format(csr_path)
+
+ try:
+ new_bundled_cert = check_output(cmd, stderr=STDOUT, shell=True, universal_newlines=True)
+ except CalledProcessError as e:
+ failed = True
+ print('Error while generating certificate for {}\n{}'.format(site, e.output), file=sys.stderr)
+ continue
+ else:
+ with open(bundled_hashed_cert_path, 'w') as bundled_hashed_cert_file:
+ bundled_hashed_cert_file.write(new_bundled_cert)
+ with open(bundled_cert_path, 'w') as bundled_cert_file:
+ bundled_cert_file.write(new_bundled_cert)
- cmd = (
- '/usr/bin/env python {{ acme_tiny_software_directory }}/acme_tiny.py '
- '--quiet '
- '--ca {{ letsencrypt_ca }} '
- '--account-key {{ letsencrypt_account_key }} '
- '--csr {{ acme_tiny_data_directory }}/csrs/{0}-{1}.csr '
- '--acme-dir {{ acme_tiny_challenges_directory }}'
- ).format(site, letsencrypt_cert_ids[site])
+ if not os.access(bundled_cert_path, os.F_OK):
+ with open(bundled_hashed_cert_path, 'rb') as bundled_hashed_cert_file:
+ bundled_hashed_cert = bundled_hashed_cert_file.read()
- try:
- cert = check_output(cmd, stderr=STDOUT, shell=True)
- except CalledProcessError as e:
- failed = True
- print('Error while generating certificate for ' + site)
- print(e.output)
- else:
- with open(bundled_cert_path, 'w') as cert_file:
- cert_file.write(cert)
+ with open(bundled_cert_path, 'w') as bundled_cert_file:
+ bundled_cert_file.write(bundled_hashed_cert)
+ print('Created bundled certificate {}'.format(bundled_cert_path))
- print('Created certificate for ' + site)
if failed:
sys.exit(1)
diff --git a/roles/local-development/tasks/main.yml b/roles/local-development/tasks/main.yml
index c4275bd270..7495220d06 100644
--- a/roles/local-development/tasks/main.yml
+++ b/roles/local-development/tasks/main.yml
@@ -77,7 +77,7 @@
login_password: "{{ mysql_root_password }}"
with_dict: "{{ wordpress_sites }}"
- name: Install WP-CLI login helper
- command: wp login install --activate
+ command: wp login install --activate --yes
args:
chdir: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/"
with_dict: "{{ wordpress_sites }}"
diff --git a/roles/mariadb/defaults/main.yml b/roles/mariadb/defaults/main.yml
index 89f7b57ea0..d337299bfb 100644
--- a/roles/mariadb/defaults/main.yml
+++ b/roles/mariadb/defaults/main.yml
@@ -1,6 +1,6 @@
mariadb_keyserver: "hkp://keyserver.ubuntu.com:80"
mariadb_keyserver_id: "0xF1656F24C74CD1D8"
-mariadb_ppa: "deb [arch=amd64] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.2/ubuntu {{ ansible_distribution_release }} main"
+mariadb_ppa: "deb http://mariadb.mirror.globo.tech/repo/10.5/ubuntu {{ ansible_distribution_release }} main"
mariadb_client_package: mariadb-client
mariadb_server_package: mariadb-server
diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml
index f4e21e561e..0da81f6f95 100644
--- a/roles/mariadb/tasks/main.yml
+++ b/roles/mariadb/tasks/main.yml
@@ -29,9 +29,18 @@
dest: /etc/mysql/conf.d
owner: root
group: root
- when: mysql_binary_logging_disabled
+ mode: '0644'
+ when: mysql_binary_logging_disabled | bool
notify: restart mysql server
+ - name: Copy .my.cnf file with root password credentials.
+ template:
+ src: my.cnf.j2
+ dest: ~/.my.cnf
+ owner: root
+ group: root
+ mode: '0600'
+
- name: Set root user password
mysql_user:
name: root
@@ -39,25 +48,19 @@
password: "{{ mysql_root_password }}"
check_implicit_admin: yes
state: present
+ no_log: true
with_items:
- "{{ inventory_hostname }}"
- 127.0.0.1
- ::1
- localhost
- - name: Copy .my.cnf file with root password credentials.
- template:
- src: my.cnf.j2
- dest: ~/.my.cnf
- owner: root
- group: root
- mode: 0600
-
- name: Delete anonymous MySQL server users
mysql_user:
user: ""
host: "{{ item }}"
state: absent
+ no_log: true
with_items:
- localhost
- "{{ inventory_hostname }}"
@@ -67,5 +70,6 @@
mysql_db:
name: test
state: absent
+ no_log: true
when: not sites_using_remote_db | count
diff --git a/roles/memcached/defaults/main.yml b/roles/memcached/defaults/main.yml
index 8bdd3e663a..0e9ba87761 100644
--- a/roles/memcached/defaults/main.yml
+++ b/roles/memcached/defaults/main.yml
@@ -8,7 +8,6 @@ memcached_port_udp: 0
memcached_packages_default:
memcached: "{{ apt_package_state }}"
- php-memcached: "{{ apt_package_state }}"
memcached_packages_custom: {}
-memcached_packages: "{{ memcached_packages_default | combine(memcached_packages_custom) }}"
+memcached_packages: "{{ memcached_packages_default | combine(php_memcached_packages, memcached_packages_custom) }}"
diff --git a/roles/memcached/tasks/main.yml b/roles/memcached/tasks/main.yml
index dc62343926..04bec4ffb4 100644
--- a/roles/memcached/tasks/main.yml
+++ b/roles/memcached/tasks/main.yml
@@ -10,12 +10,13 @@
template:
src: memcached.conf.j2
dest: /etc/memcached.conf
+ mode: '0644'
notify: restart memcached
- name: Set the max open file descriptors
sysctl:
name: fs.file-max
- value: "{{ memcached_fs_file_max }}"
+ value: "{{ memcached_fs_file_max | string }}"
state: present
- name: Start the memcached service
diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml
index c82941bcc7..69433b33fb 100644
--- a/roles/nginx/defaults/main.yml
+++ b/roles/nginx/defaults/main.yml
@@ -1,5 +1,7 @@
---
-nginx_ppa: "ppa:nginx/mainline"
+nginx_keyserver: "https://nginx.org/keys/nginx_signing.key"
+nginx_keyserver_id: "ABF5BD827BD9BF62"
+nginx_ppa: "deb http://nginx.org/packages/mainline/ubuntu {{ ansible_distribution_release }} nginx"
nginx_package: nginx
nginx_conf: nginx.conf.j2
nginx_path: /etc/nginx
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index 3f0202792c..676679ce1b 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -1,4 +1,9 @@
---
+- name: Add Nginx APT key
+ apt_key:
+ keyserver: "{{ nginx_keyserver }}"
+ id: "{{ nginx_keyserver_id }}"
+
- name: Add Nginx PPA
apt_repository:
repo: "{{ nginx_ppa }}"
@@ -10,31 +15,33 @@
state: "{{ nginx_package_state | default(apt_package_state) }}"
cache_valid_time: "{{ apt_cache_valid_time }}"
+- name: Ensure site directories exist
+ file:
+ path: "{{ nginx_path }}/{{ item }}"
+ state: directory
+ mode: '0755'
+ with_items:
+ - sites-available
+ - sites-enabled
+
- name: Create SSL directory
file:
- mode: 0700
+ mode: '0700'
path: "{{ nginx_path }}/ssl"
state: directory
-- name: Generate strong unique Diffie-Hellman group.
- command: openssl dhparam -out dhparams.pem 2048
- args:
- chdir: "{{ nginx_path }}/ssl"
- creates: "{{ nginx_path }}/ssl/dhparams.pem"
- when: sites_use_ssl
- notify: reload nginx
- tags: [diffie-hellman, letsencrypt, wordpress, wordpress-setup, nginx-includes, nginx-sites]
-
- name: Copy h5bp configs
copy:
src: templates/h5bp
dest: "{{ nginx_path }}"
+ mode: '0755'
notify: reload nginx
- name: Create nginx.conf
template:
src: "{{ nginx_conf }}"
dest: "{{ nginx_path }}/nginx.conf"
+ mode: '0644'
notify: reload nginx
tags: nginx-includes
diff --git a/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf b/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf
index d15bf972ba..95cc175ce4 100644
--- a/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf
+++ b/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf
@@ -1,9 +1,34 @@
-# OCSP stapling...
+# ----------------------------------------------------------------------
+# | Online Certificate Status Protocol stapling |
+# ----------------------------------------------------------------------
+
+# OCSP is a lightweight, only one record to help clients verify the validity of
+# the server certificate.
+# OCSP stapling allows the server to send its cached OCSP record during the TLS
+# handshake, without the need of 3rd party OCSP responder.
+#
+# https://wiki.mozilla.org/Security/Server_Side_TLS#OCSP_Stapling
+# https://tools.ietf.org/html/rfc6066#section-8
+# https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling
+#
+# (1) Use Cloudflare 1.1.1.1 DNS resolver
+# https://developers.cloudflare.com/1.1.1.1/setting-up-1.1.1.1/
+#
+# (2) Use Google 8.8.8.8 DNS resolver
+# https://developers.google.com/speed/public-dns/docs/using
+#
+# (3) Use Dyn DNS resolver
+# https://help.dyn.com/internet-guide-setup/
+
ssl_stapling on;
ssl_stapling_verify on;
-#trusted cert must be made up of your intermediate certificate followed by root certificate
-#ssl_trusted_certificate /path/to/ca.crt;
-
-resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s;
+resolver
+ # (1)
+ 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]
+ # (2)
+ 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844]
+ # (3)
+ # 216.146.35.35 216.146.36.36
+ valid=60s;
resolver_timeout 2s;
diff --git a/roles/nginx/templates/h5bp/directive-only/ssl.conf b/roles/nginx/templates/h5bp/directive-only/ssl.conf
index cf5cfaa6cc..20d98766b3 100644
--- a/roles/nginx/templates/h5bp/directive-only/ssl.conf
+++ b/roles/nginx/templates/h5bp/directive-only/ssl.conf
@@ -1,10 +1,6 @@
-# Protect against the BEAST and POODLE attacks by not using SSLv3 at all. If you need to support older browsers (IE6) you may need to add
-# SSLv3 to the list of protocols below.
-ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
-
-# Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla (Intermediate Set) - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx
-ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS;
-ssl_prefer_server_ciphers on;
+ssl_protocols TLSv1.2 TLSv1.3;
+ssl_ciphers EECDH+CHACHA20:EECDH+AES;
+ssl_prefer_server_ciphers off;
# Optimize SSL by caching session parameters for 10 minutes. This cuts down on the number of expensive SSL handshakes.
# The handshake is the most CPU-intensive operation, and by default it is re-negotiated on every new/parallel connection.
@@ -25,7 +21,7 @@ ssl_session_timeout 24h;
#
# Note that you'll have to define and rotate the keys securely by yourself. In absence
# of such infrastructure, consider turning off session tickets:
-#ssl_session_tickets off;
+ssl_session_tickets off;
# Use a higher keepalive timeout to reduce the need for repeated handshakes
keepalive_timeout 300s; # up from 75 secs default
diff --git a/roles/nginx/templates/h5bp/mime.types b/roles/nginx/templates/h5bp/mime.types
index 7c3b1e7386..972fd08958 100644
--- a/roles/nginx/templates/h5bp/mime.types
+++ b/roles/nginx/templates/h5bp/mime.types
@@ -14,7 +14,7 @@ types {
# Normalize to standard type.
# https://tools.ietf.org/html/rfc4329#section-7.2
- application/javascript js;
+ application/javascript js mjs;
# Manifest files
diff --git a/roles/php/defaults/main.yml b/roles/php/defaults/main.yml
index f59a03b044..c6ef36e940 100644
--- a/roles/php/defaults/main.yml
+++ b/roles/php/defaults/main.yml
@@ -2,18 +2,19 @@ disable_default_pool: true
memcached_sessions: false
php_extensions_default:
- php7.3-cli: "{{ apt_package_state }}"
- php7.3-common: "{{ apt_package_state }}"
- php7.3-curl: "{{ apt_package_state }}"
- php7.3-dev: "{{ apt_package_state }}"
- php7.3-fpm: "{{ apt_package_state }}"
- php7.3-gd: "{{ apt_package_state }}"
- php7.3-mbstring: "{{ apt_package_state }}"
- php7.3-mysql: "{{ apt_package_state }}"
- php7.3-opcache: "{{ apt_package_state }}"
- php7.3-xml: "{{ apt_package_state }}"
- php7.3-xmlrpc: "{{ apt_package_state }}"
- php7.3-zip: "{{ apt_package_state }}"
+ php7.4-cli: "{{ apt_package_state }}"
+ php7.4-common: "{{ apt_package_state }}"
+ php7.4-curl: "{{ apt_package_state }}"
+ php7.4-dev: "{{ apt_package_state }}"
+ php7.4-fpm: "{{ apt_package_state }}"
+ php7.4-gd: "{{ apt_package_state }}"
+ php7.4-mbstring: "{{ apt_package_state }}"
+ php7.4-mysql: "{{ apt_package_state }}"
+ php7.4-opcache: "{{ apt_package_state }}"
+ php7.4-redis: "{{ apt_package_state }}"
+ php7.4-xml: "{{ apt_package_state }}"
+ php7.4-xmlrpc: "{{ apt_package_state }}"
+ php7.4-zip: "{{ apt_package_state }}"
php_extensions_custom: {}
php_extensions: "{{ php_extensions_default | combine(php_extensions_custom) }}"
@@ -25,6 +26,7 @@ php_max_execution_time: 120
php_max_input_time: 300
php_max_input_vars: 1000
php_memory_limit: 96M
+php_cli_memory_limit: -1
php_mysqlnd_collect_memory_statistics: 'Off'
php_post_max_size: 25M
php_sendmail_path: /usr/sbin/ssmtp -t
@@ -32,7 +34,6 @@ php_session_save_path: /tmp
php_session_cookie_httponly: 'On'
php_session_cookie_secure: 'Off'
php_upload_max_filesize: 25M
-php_track_errors: 'Off'
php_timezone: '{{ ntp_timezone }}'
php_output_buffering: 'Off'
diff --git a/roles/php/tasks/main.yml b/roles/php/tasks/main.yml
index ceaf843c05..eaec4b4f16 100644
--- a/roles/php/tasks/main.yml
+++ b/roles/php/tasks/main.yml
@@ -1,38 +1,62 @@
---
-- name: Add PHP 7.3 PPA
+- name: Add PHP PPA
apt_repository:
repo: "ppa:ondrej/php"
update_cache: yes
-- name: Install PHP 7.3
+- name: Install PHP and extensions
apt:
name: "{{ item.key }}"
state: "{{ item.value }}"
cache_valid_time: "{{ apt_cache_valid_time }}"
+ install_recommends: no
with_dict: "{{ php_extensions }}"
-- name: Start php7.3-fpm service
+- name: Ensure correct PHP version selected
+ community.general.alternatives:
+ name: php
+ path: /usr/bin/php{{ php_version }}
+
+- name: Start php fpm service
service:
- name: php7.3-fpm
+ name: "php{{ php_version }}-fpm"
state: started
enabled: true
-- name: Check for existing php7.2-fpm service
- stat:
- path: /etc/init.d/php7.2-fpm
- register: php72_status
+- name: Find existing php fpm services
+ find:
+ paths: /etc/init.d
+ patterns: "^php((?!{{ php_version }})(\\d\\.\\d))-fpm$"
+ use_regex: true
+ register: old_php_fpm_services
-- name: Stop php7.2-fpm service if it exists
+- name: Stop old php-fpm services
service:
- name: php7.2-fpm
+ name: "{{ item.path | basename }}"
state: stopped
enabled: false
- register: service_stopped
- when: php72_status.stat.exists
+ loop: "{{ old_php_fpm_services.files }}"
+ loop_control:
+ label: "{{ item.path | basename }}"
notify: reload php-fpm
-- name: PHP configuration file
+- name: Copy PHP-FPM configuration file
template:
- src: php.ini.j2
- dest: /etc/php/7.3/fpm/php.ini
+ src: php-fpm.ini.j2
+ dest: /etc/php/{{ php_version }}/fpm/php.ini
+ mode: '0644'
+ notify: reload php-fpm
+
+- name: Copy PHP CLI configuration file
+ template:
+ src: php-cli.ini.j2
+ dest: /etc/php/{{ php_version }}/cli/php.ini
+ mode: '0644'
+
+- name: Change ImageMagick policy.xml to allow for PDFs
+ replace:
+ path: /etc/ImageMagick-6/policy.xml
+ regexp: ''
+ replace: ''
+ backup: no
notify: reload php-fpm
diff --git a/roles/php/templates/php-cli.ini.j2 b/roles/php/templates/php-cli.ini.j2
new file mode 100644
index 0000000000..44360aa80d
--- /dev/null
+++ b/roles/php/templates/php-cli.ini.j2
@@ -0,0 +1,20 @@
+; {{ ansible_managed }}
+
+[PHP]
+error_reporting = {{ php_error_reporting }}
+sendmail_path = {{ php_sendmail_path }}
+expose_php = Off
+date.timezone = {{ php_timezone }}
+memory_limit = {{ php_cli_memory_limit }}
+
+[mysqlnd]
+mysqlnd.collect_memory_statistics = {{ php_mysqlnd_collect_memory_statistics }}
+
+[opcache]
+opcache.enable = {{ php_opcache_enable }}
+opcache.enable_cli = {{ php_opcache_enable_cli }}
+opcache.memory_consumption = {{ php_opcache_memory_consumption }}
+opcache.interned_strings_buffer = {{ php_opcache_interned_strings_buffer }}
+opcache.max_accelerated_files = {{ php_opcache_max_accelerated_files }}
+opcache.revalidate_freq = {{ php_opcache_revalidate_freq }}
+opcache.fast_shutdown = {{ php_opcache_fast_shutdown }}
diff --git a/roles/php/templates/php.ini.j2 b/roles/php/templates/php-fpm.ini.j2
similarity index 97%
rename from roles/php/templates/php.ini.j2
rename to roles/php/templates/php-fpm.ini.j2
index 84fe207c2e..8ff098f656 100644
--- a/roles/php/templates/php.ini.j2
+++ b/roles/php/templates/php-fpm.ini.j2
@@ -13,7 +13,6 @@ sendmail_path = {{ php_sendmail_path }}
session.save_path = {{ php_session_save_path }}
session.cookie_httponly = {{ php_session_cookie_httponly }}
session.cookie_secure = {{ php_session_cookie_secure }}
-track_errors = {{ php_track_errors }}
upload_max_filesize = {{ php_upload_max_filesize }}
expose_php = Off
date.timezone = {{ php_timezone }}
diff --git a/roles/php/vars/7.4.yml b/roles/php/vars/7.4.yml
new file mode 100644
index 0000000000..562b7468c2
--- /dev/null
+++ b/roles/php/vars/7.4.yml
@@ -0,0 +1,19 @@
+php_extensions_default:
+ php7.4-bcmath: "{{ apt_package_state }}"
+ php7.4-cli: "{{ apt_package_state }}"
+ php7.4-curl: "{{ apt_package_state }}"
+ php7.4-dev: "{{ apt_package_state }}"
+ php7.4-gd: "{{ apt_package_state }}"
+ php7.4-fpm: "{{ apt_package_state }}"
+ php7.4-imagick: "{{ apt_package_state }}"
+ php7.4-intl: "{{ apt_package_state }}"
+ php7.4-mbstring: "{{ apt_package_state }}"
+ php7.4-mysql: "{{ apt_package_state }}"
+ php7.4-xml: "{{ apt_package_state }}"
+ php7.4-xmlrpc: "{{ apt_package_state }}"
+ php7.4-zip: "{{ apt_package_state }}"
+
+php_memcached_packages:
+ php7.4-memcached: "{{ apt_package_state }}"
+
+php_xdebug_package: php7.4-xdebug
diff --git a/roles/php/vars/8.0.yml b/roles/php/vars/8.0.yml
new file mode 100644
index 0000000000..02eb376120
--- /dev/null
+++ b/roles/php/vars/8.0.yml
@@ -0,0 +1,18 @@
+php_extensions_default:
+ php8.0-bcmath: "{{ apt_package_state }}"
+ php8.0-cli: "{{ apt_package_state }}"
+ php8.0-curl: "{{ apt_package_state }}"
+ php8.0-dev: "{{ apt_package_state }}"
+ php8.0-fpm: "{{ apt_package_state }}"
+ php8.0-imagick: "{{ apt_package_state }}"
+ php8.0-intl: "{{ apt_package_state }}"
+ php8.0-mbstring: "{{ apt_package_state }}"
+ php8.0-mysql: "{{ apt_package_state }}"
+ php8.0-xml: "{{ apt_package_state }}"
+ php8.0-xmlrpc: "{{ apt_package_state }}"
+ php8.0-zip: "{{ apt_package_state }}"
+
+php_memcached_packages:
+ php8.0-memcached: "{{ apt_package_state }}"
+
+php_xdebug_package: php8.0-xdebug
diff --git a/roles/php/vars/8.1.yml b/roles/php/vars/8.1.yml
new file mode 100644
index 0000000000..1b15ae2d4e
--- /dev/null
+++ b/roles/php/vars/8.1.yml
@@ -0,0 +1,18 @@
+php_extensions_default:
+ php8.1-bcmath: "{{ apt_package_state }}"
+ php8.1-cli: "{{ apt_package_state }}"
+ php8.1-curl: "{{ apt_package_state }}"
+ php8.1-dev: "{{ apt_package_state }}"
+ php8.1-fpm: "{{ apt_package_state }}"
+ php8.1-imagick: "{{ apt_package_state }}"
+ php8.1-intl: "{{ apt_package_state }}"
+ php8.1-mbstring: "{{ apt_package_state }}"
+ php8.1-mysql: "{{ apt_package_state }}"
+ php8.1-xml: "{{ apt_package_state }}"
+ php8.1-xmlrpc: "{{ apt_package_state }}"
+ php8.1-zip: "{{ apt_package_state }}"
+
+php_memcached_packages:
+ php8.1-memcached: "{{ apt_package_state }}"
+
+php_xdebug_package: php8.1-xdebug
diff --git a/roles/princexml/.travis.yml b/roles/princexml/.travis.yml
new file mode 100644
index 0000000000..24e14eb011
--- /dev/null
+++ b/roles/princexml/.travis.yml
@@ -0,0 +1,30 @@
+---
+dist: xenial
+language: python
+python: "2.7"
+
+# Use the new container infrastructure
+sudo: false
+
+# Install ansible
+addons:
+ apt:
+ packages:
+ - python-pip
+
+install:
+ # Install ansible
+ - pip install ansible
+
+ # Check ansible version
+ - ansible --version
+
+ # Create ansible.cfg with correct roles_path
+ - printf '[defaults]\nroles_path=../' >ansible.cfg
+
+script:
+ # Basic role syntax check
+ - ansible-playbook tests/test.yml -i tests/inventory --syntax-check
+
+notifications:
+ webhooks: https://galaxy.ansible.com/api/v1/notifications/
\ No newline at end of file
diff --git a/roles/princexml/README.md b/roles/princexml/README.md
new file mode 100644
index 0000000000..ed45eab366
--- /dev/null
+++ b/roles/princexml/README.md
@@ -0,0 +1,48 @@
+PrinceXML
+=========
+
+[](https://travis-ci.org/pressbooks/ansible-role-princexml) [](https://github.com/pressbooks/ansible-role-princexml/releases/latest)
+
+Installs [PrinceXML](https://princexml.com) on Ubuntu 16.04 or 18.04.
+
+Requirements
+------------
+
+Optionally, a PrinceXML `license.dat` file.
+
+Role Variables
+--------------
+
+Available variables are listed below, along with default values (see defaults/main.yml):
+
+ # PrinceXML package URI for Ubuntu 16.04 Xenial. Defaults to latest stable 64-bit.
+ prince_package_uri_ubuntu_xenial: https://www.princexml.com/download/prince_12.5-1_ubuntu16.04_amd64.deb
+
+ # PrinceXML package URI for Ubuntu 18.04 Bionic. Defaults to latest stable 64-bit.
+ prince_package_uri_ubuntu_bionic: https://www.princexml.com/download/prince_12.5-1_ubuntu18.04_amd64.deb
+
+ # The local path to your PrinceXML `license.dat` file (optional).
+ prince_license: ""
+
+
+Dependencies
+------------
+
+None.
+
+Example Playbook
+----------------
+
+ - hosts: servers
+ roles:
+ - pressbooks.princexml
+
+License
+-------
+
+MIT
+
+Author Information
+------------------
+
+This role was created in 2016 by [Ned Zimmerman](https://github.com/greatislander) for [Pressbooks](https://pressbooks.org).
diff --git a/roles/princexml/defaults/main.yml b/roles/princexml/defaults/main.yml
new file mode 100644
index 0000000000..b32becf192
--- /dev/null
+++ b/roles/princexml/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+prince_package_uri_ubuntu_xenial: https://www.princexml.com/download/prince_12.5-1_ubuntu16.04_amd64.deb
+prince_package_uri_ubuntu_bionic: https://www.princexml.com/download/prince_12.5-1_ubuntu18.04_amd64.deb
+prince_package_uri_ubuntu_focal: https://www.princexml.com/download/prince_14.2-1_ubuntu20.04_amd64.deb
+prince_license: ""
diff --git a/roles/princexml/meta/.galaxy_install_info b/roles/princexml/meta/.galaxy_install_info
new file mode 100644
index 0000000000..fbada44785
--- /dev/null
+++ b/roles/princexml/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Wed Oct 13 20:58:21 2021
+version: 12.5.0
diff --git a/roles/princexml/meta/main.yml b/roles/princexml/meta/main.yml
new file mode 100644
index 0000000000..8ba54e17b8
--- /dev/null
+++ b/roles/princexml/meta/main.yml
@@ -0,0 +1,176 @@
+galaxy_info:
+ author: greatislander
+ description: Installs PrinceXML.
+ company: Pressbooks (Book Oven Inc.)
+ license: MIT
+ min_ansible_version: 2.5
+ platforms:
+ #- name: OpenBSD
+ # versions:
+ # - all
+ # - 5.6
+ # - 5.7
+ # - 5.8
+ # - 5.9
+ # - 6.0
+ #- name: Fedora
+ # versions:
+ # - all
+ # - 16
+ # - 17
+ # - 18
+ # - 19
+ # - 20
+ # - 21
+ # - 22
+ # - 23
+ # - 24
+ #- name: DellOS
+ # versions:
+ # - all
+ # - 10
+ # - 6
+ # - 9
+ #- name: MacOSX
+ # versions:
+ # - all
+ # - 10.10
+ # - 10.11
+ # - 10.12
+ # - 10.7
+ # - 10.8
+ # - 10.9
+ #- name: Junos
+ # versions:
+ # - all
+ # - any
+ #- name: GenericBSD
+ # versions:
+ # - all
+ # - any
+ #- name: Void Linux
+ # versions:
+ # - all
+ # - any
+ #- name: GenericLinux
+ # versions:
+ # - all
+ # - any
+ #- name: NXOS
+ # versions:
+ # - all
+ # - any
+ #- name: IOS
+ # versions:
+ # - all
+ # - any
+ #- name: Amazon
+ # versions:
+ # - all
+ # - 2013.03
+ # - 2013.09
+ # - 2016.03
+ #- name: ArchLinux
+ # versions:
+ # - all
+ # - any
+ #- name: FreeBSD
+ # versions:
+ # - all
+ # - 10.0
+ # - 10.1
+ # - 10.2
+ # - 10.3
+ # - 8.0
+ # - 8.1
+ # - 8.2
+ # - 8.3
+ # - 8.4
+ # - 9.0
+ # - 9.1
+ # - 9.1
+ # - 9.2
+ # - 9.3
+ - name: Ubuntu
+ versions:
+ # - all
+ # - lucid
+ # - maverick
+ # - natty
+ # - oneiric
+ # - precise
+ # - quantal
+ # - raring
+ # - saucy
+ # - trusty
+ # - utopic
+ # - vivid
+ # - wily
+ - xenial
+ - bionic
+ #- name: Debian
+ # versions:
+ # - all
+ # - etch
+ # - jessie
+ # - lenny
+ # - sid
+ # - squeeze
+ # - stretch
+ # - wheezy
+ #- name: EL
+ # versions:
+ # - all
+ # - 5
+ # - 6
+ # - 7
+ #- name: Windows
+ # versions:
+ # - all
+ # - 2012R2
+ #- name: SmartOS
+ # versions:
+ # - all
+ # - any
+ #- name: opensuse
+ # versions:
+ # - all
+ # - 12.1
+ # - 12.2
+ # - 12.3
+ # - 13.1
+ # - 13.2
+ #- name: SLES
+ # versions:
+ # - all
+ # - 10SP3
+ # - 10SP4
+ # - 11
+ # - 11SP1
+ # - 11SP2
+ # - 11SP3
+ # - 11SP4
+ # - 12
+ # - 12SP1
+ #- name: GenericUNIX
+ # versions:
+ # - all
+ # - any
+ #- name: Solaris
+ # versions:
+ # - all
+ # - 10
+ # - 11.0
+ # - 11.1
+ # - 11.2
+ # - 11.3
+ #- name: eos
+ # versions:
+ # - all
+ # - Any
+ galaxy_tags:
+ - eprdctn
+ - publishing
+ - authoring
+ - xhtml
+ - pdf
diff --git a/roles/princexml/tasks/amd64.yml b/roles/princexml/tasks/amd64.yml
new file mode 100644
index 0000000000..c77983df8e
--- /dev/null
+++ b/roles/princexml/tasks/amd64.yml
@@ -0,0 +1,35 @@
+---
+- name: 'Check for supported environment'
+ fail: msg="PrinceXML can only be installed on Ubuntu Xenial, Bionic or Focal."
+ when: (ansible_distribution|string != 'Ubuntu') or
+ (ansible_distribution_release|string != 'xenial' and ansible_distribution_release|string != 'bionic' and ansible_distribution_release|string != 'focal')
+
+- name: Determine PrinceXML version
+ set_fact:
+ prince_package_uri: "prince_package_uri_ubuntu_{{ ansible_distribution_release|string }}"
+
+- name: Install PrinceXML
+ apt:
+ deb: "{{ lookup('vars', prince_package_uri) }}"
+
+- name: Upload PrinceXML license
+ copy:
+ src: "{{ prince_license }}"
+ dest: /usr/lib/prince/license/license.dat.new
+ owner: root
+ group: root
+ mode: "u=rw,g=r,o=r"
+ when: prince_license != ""
+
+- name: Check for existing PrinceXML license
+ stat: path=/usr/lib/prince/license/license.dat
+ register: existing_license
+ when: prince_license != ""
+
+- name: Backup old PrinceXML license
+ command: mv /usr/lib/prince/license/license.dat /usr/lib/prince/license/license.dat.{{ lookup('pipe', 'date +%Y%m%d') }}.old
+ when: prince_license != "" and existing_license.stat.exists
+
+- name: Install new PrinceXML license
+ command: mv /usr/lib/prince/license/license.dat.new /usr/lib/prince/license/license.dat
+ when: prince_license != ""
diff --git a/roles/princexml/tasks/arm64.yml b/roles/princexml/tasks/arm64.yml
new file mode 100644
index 0000000000..7ebd8451e7
--- /dev/null
+++ b/roles/princexml/tasks/arm64.yml
@@ -0,0 +1,3 @@
+---
+- name: 'Skip PrinceXML for now until there is an arm .deb'
+ action: command echo "Arm detected. Skipping PrinceXML"
diff --git a/roles/princexml/tasks/main.yml b/roles/princexml/tasks/main.yml
new file mode 100644
index 0000000000..e51a84945b
--- /dev/null
+++ b/roles/princexml/tasks/main.yml
@@ -0,0 +1,9 @@
+---
+ - name: AMD64/x86_64
+ import_tasks: amd64.yml
+ when: ansible_architecture == "x86_64"
+
+ - name: ARM64
+ import_tasks: arm64.yml
+ when: ansible_architecture == "aarch64"
+
diff --git a/roles/princexml/tests/inventory b/roles/princexml/tests/inventory
new file mode 100644
index 0000000000..d18580b3c3
--- /dev/null
+++ b/roles/princexml/tests/inventory
@@ -0,0 +1 @@
+localhost
\ No newline at end of file
diff --git a/roles/princexml/tests/test.yml b/roles/princexml/tests/test.yml
new file mode 100644
index 0000000000..ae87d45c38
--- /dev/null
+++ b/roles/princexml/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - ansible-role-princexml
diff --git a/roles/python_interpreter/tasks/main.yml b/roles/python_interpreter/tasks/main.yml
deleted file mode 100644
index 8935abe901..0000000000
--- a/roles/python_interpreter/tasks/main.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-- block:
- - name: Get Ubuntu release
- raw: lsb_release -cs
- register: ubuntu_release
- changed_when: false
- - name: Set ansible_python_interpreter for Ubuntu 18.04 Bionic
- set_fact:
- ansible_python_interpreter: python3
- when: ubuntu_release.stdout | trim == 'bionic'
- when: ansible_python_interpreter is not defined
- tags: always
diff --git a/roles/redis/tasks/main.yml b/roles/redis/tasks/main.yml
index 8c039c0c64..4e7bc44c3c 100644
--- a/roles/redis/tasks/main.yml
+++ b/roles/redis/tasks/main.yml
@@ -22,7 +22,7 @@
- name: Load Disable THP service into systemd
command: "/bin/systemctl daemon-reload"
- when: disable_thp_service_added | changed
+ when: disable_thp_service_added.changed
- name: Enable and start Disable THP service
service:
diff --git a/roles/rollback/tasks/main.yml b/roles/rollback/tasks/main.yml
index 4185f2729b..6ceb07dc30 100644
--- a/roles/rollback/tasks/main.yml
+++ b/roles/rollback/tasks/main.yml
@@ -1,22 +1,33 @@
---
+- name: Get real path of current symlinked release
+ command: "readlink {{ project_current_path }}"
+ args:
+ chdir: "{{ project_root }}"
+ register: current_release_readlink_result
+ changed_when: false
+
+- name: Clean up old and failed releases
+ deploy_helper:
+ state: clean
+ path: "{{ project_root }}"
+ current_path: "{{ project_current_path }}"
+ release: "{{ current_release_readlink_result.stdout }}"
+ keep_releases: "{{ project.deploy_keep_releases | default(deploy_keep_releases | default(omit)) }}"
+
- import_tasks: user-release.yml
when: release is defined
- import_tasks: prior-release.yml
when: release is not defined
-- name: Check whether target release was from a successful deploy
- stat:
- path: "{{ new_release_path }}/DEPLOY_UNFINISHED"
- register: target
-
-- name: Fail if target release was from failed deploy
- fail:
- msg: "Cannot switch to release at {{ new_release_path }}. It is from an unfinished deploy. You may manually specify a different release using --extra-vars='release=12345678901234'."
- when: target.stat.exists | default(False)
-
- name: Link 'current' directory to target release
file:
path: "{{ project_root }}/{{ project_current_path }}"
src: "{{ new_release_path }}"
state: link
+
+- name: Write unfinished file to old symlinked release
+ file:
+ path: "{{ current_release_readlink_result.stdout }}/DEPLOY_UNFINISHED"
+ state: touch
+ mode: '0644'
diff --git a/roles/rollback/tasks/prior-release.yml b/roles/rollback/tasks/prior-release.yml
index 1788aeebe8..25a51ca884 100644
--- a/roles/rollback/tasks/prior-release.yml
+++ b/roles/rollback/tasks/prior-release.yml
@@ -1,9 +1,13 @@
---
- name: Get list position of current symlinked release
- shell: "ls releases | grep -n $(basename $(readlink {{ project_current_path }})) | cut -f1 -d:"
+ shell: |
+ set -eo pipefail
+ ls releases | grep -n $(basename $(readlink {{ project_current_path }})) | cut -f1 -d:
args:
chdir: "{{ project_root }}"
+ executable: /bin/bash
register: current_release_position
+ changed_when: false
- name: Fail if current release is the oldest available release
fail:
@@ -15,6 +19,7 @@
args:
chdir: "{{ project_root }}"
register: releases
+ changed_when: false
- name: Create new_release_path variable
set_fact:
diff --git a/roles/rollback/tasks/user-release.yml b/roles/rollback/tasks/user-release.yml
index 6e356e1bc0..d360cccda3 100644
--- a/roles/rollback/tasks/user-release.yml
+++ b/roles/rollback/tasks/user-release.yml
@@ -9,11 +9,12 @@
args:
chdir: "{{ project_root }}"
register: current_release
+ changed_when: false
- name: Fail if user-specified release doesn't exist or is already active
fail:
msg: "Cannot switch to release {{ release }}. Either it does not exist or it is the active release."
- when: specified.stat.isdir | default(False) == False or current_release.stdout_lines[0] == release
+ when: not (specified.stat.isdir | default(False)) or current_release.stdout_lines[0] == release
- name: Create new_release_path variable
set_fact:
diff --git a/roles/sshd/tasks/main.yml b/roles/sshd/tasks/main.yml
index acabc23938..152b4f7fd3 100644
--- a/roles/sshd/tasks/main.yml
+++ b/roles/sshd/tasks/main.yml
@@ -11,7 +11,7 @@
template:
src: "{{ sshd_config }}"
dest: /etc/ssh/sshd_config
- mode: 0600
+ mode: '0600'
validate: '/usr/sbin/sshd -T -f %s'
notify: restart ssh
@@ -19,7 +19,7 @@
template:
src: "{{ ssh_config }}"
dest: /etc/ssh/ssh_config
- mode: 0644
+ mode: '0644'
- name: Remove Diffie-Hellman moduli of size < 2000
lineinfile:
diff --git a/roles/ssmtp/tasks/main.yml b/roles/ssmtp/tasks/main.yml
index 9d62055d4d..c0e78ba3ec 100644
--- a/roles/ssmtp/tasks/main.yml
+++ b/roles/ssmtp/tasks/main.yml
@@ -9,8 +9,10 @@
template:
src: ssmtp.conf.j2
dest: /etc/ssmtp/ssmtp.conf
+ mode: '0644'
- name: ssmtp revaliases configuration
template:
src: revaliases.j2
dest: /etc/ssmtp/revaliases
+ mode: '0644'
diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml
index 8c1403c0be..09b84830df 100644
--- a/roles/users/tasks/main.yml
+++ b/roles/users/tasks/main.yml
@@ -33,7 +33,7 @@
name: "{{ item.name }}"
group: "{{ item.groups[0] }}"
groups: "{{ item.groups | join(',') }}"
- password: '{% for user in vault_users | default([]) if user.name == item.name and user.password is defined %}{{ user.password | password_hash("sha512", (user.salt | default(""))[:16] | regex_replace("[^\.\/a-zA-Z0-9]", "x")) }}{% else %}{{ None }}{% endfor %}'
+ password: '{% for user in vault_users | default([]) if user.name == item.name and user.password is defined %}{{ user.password | password_hash("sha512", (user.salt | default(""))[:16] | regex_replace("[^\.\/a-zA-Z0-9]", "x")) }}{% else %}{{ "!" }}{% endfor %}'
state: present
shell: /bin/bash
update_password: "{{ item.update_password | default('always') }}"
@@ -43,13 +43,13 @@
template:
src: sudoers.d.j2
dest: "/etc/sudoers.d/{{ web_user }}-services"
- mode: 0440
+ mode: '0440'
owner: root
group: root
validate: "/usr/sbin/visudo -cf %s"
- when: web_sudoers
+ when: web_sudoers[0] is defined
-- name: Add SSH keys
+- name: Add user SSH keys
authorized_key:
user: "{{ item.0.name }}"
key: "{{ item.1 }}"
@@ -57,8 +57,15 @@
- "{{ users | default([]) }}"
- keys
+- name: Add deploy SSH keys
+ authorized_key:
+ user: "{{ web_user }}"
+ key: "{{ lookup('file', item) }}"
+ with_fileglob: 'public_keys/*.pub'
+
- name: Check whether Ansible can connect as admin_user
- local_action: command ansible {{ inventory_hostname }} -m ping -u {{ admin_user }} {{ cli_options | default('') }}
+ command: ansible {{ inventory_hostname }} -m ping -u {{ admin_user }} {{ cli_options | default('') }}
+ delegate_to: localhost
failed_when: false
changed_when: false
become: no
diff --git a/roles/wordpress-install/tasks/composer-authentications.yml b/roles/wordpress-install/tasks/composer-authentications.yml
index 9d0a8368d3..2b2e8649e2 100644
--- a/roles/wordpress-install/tasks/composer-authentications.yml
+++ b/roles/wordpress-install/tasks/composer-authentications.yml
@@ -7,10 +7,10 @@
no_log: true
changed_when: false
when:
- - composer_authentication.hostname is defined and composer_authentication.hostname != ""
- - composer_authentication.username is defined and composer_authentication.username != ""
- - composer_authentication.password is defined and composer_authentication.password != ""
- loop: "{{ composer_authentications }}"
+ - not (not composer_authentication.hostname)
+ - not (not composer_authentication.username)
+ - not (not composer_authentication.password)
+ loop: "{{ composer_authentications | default([]) }}"
loop_control:
loop_var: composer_authentication
label: "{{ composer_authentication.hostname }}"
diff --git a/roles/wordpress-install/tasks/directories.yml b/roles/wordpress-install/tasks/directories.yml
index 093d3212a6..591255688b 100644
--- a/roles/wordpress-install/tasks/directories.yml
+++ b/roles/wordpress-install/tasks/directories.yml
@@ -1,10 +1,10 @@
---
- name: Create web root of sites
file:
- path: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/web"
+ path: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/{{ item.value.public_path | default('web') }}"
owner: "{{ web_user }}"
group: "{{ web_group }}"
- mode: 0755
+ mode: '0755'
state: directory
with_dict: "{{ wordpress_sites }}"
@@ -13,7 +13,7 @@
path: "{{ www_root }}/{{ item.key }}/shared"
owner: "{{ web_user }}"
group: "{{ web_group }}"
- mode: 0755
+ mode: '0755'
state: directory
with_dict: "{{ wordpress_sites }}"
diff --git a/roles/wordpress-install/tasks/dotenv.yml b/roles/wordpress-install/tasks/dotenv.yml
new file mode 100644
index 0000000000..5397096992
--- /dev/null
+++ b/roles/wordpress-install/tasks/dotenv.yml
@@ -0,0 +1,17 @@
+---
+- name: Create .env file
+ template:
+ src: "env.j2"
+ dest: "/tmp/{{ item.key }}.env"
+ mode: '0644'
+ owner: "{{ web_user }}"
+ group: "{{ web_group }}"
+ with_dict: "{{ wordpress_sites }}"
+
+- name: Copy .env file into web root
+ synchronize:
+ src: "/tmp/{{ item.key }}.env"
+ dest: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/.env"
+ checksum: true
+ with_dict: "{{ wordpress_sites }}"
+ delegate_to: "{{ inventory_hostname }}"
diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml
index fd6fbb223b..bdebc1d3f4 100644
--- a/roles/wordpress-install/tasks/main.yml
+++ b/roles/wordpress-install/tasks/main.yml
@@ -2,19 +2,8 @@
- import_tasks: directories.yml
tags: wordpress-install-directories
-- name: Create .env file
- template:
- src: "env.j2"
- dest: "/tmp/{{ item.key }}.env"
- owner: "{{ web_user }}"
- group: "{{ web_group }}"
- with_dict: "{{ wordpress_sites }}"
-
-- name: Copy .env file into web root
- command: rsync -ac --info=NAME /tmp/{{ item.key }}.env {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/.env
- with_dict: "{{ wordpress_sites }}"
- register: env_file
- changed_when: env_file.stdout == item.key + '.env'
+- import_tasks: dotenv.yml
+ tags: dotenv
- name: Add known_hosts
known_hosts:
diff --git a/roles/wordpress-setup/defaults/main.yml b/roles/wordpress-setup/defaults/main.yml
index 5b2e1768ee..4684d17a67 100644
--- a/roles/wordpress-setup/defaults/main.yml
+++ b/roles/wordpress-setup/defaults/main.yml
@@ -5,7 +5,7 @@ nginx_ssl_path: "{{ nginx_path }}/ssl"
ssl_default_site:
no_default:
site_hosts:
- - canonical: example.com
+ - canonical: request.is.invalid
ssl:
enabled: true
provider: self-signed
@@ -34,8 +34,7 @@ nginx_cache_background_update: "on"
# Nginx includes
nginx_includes_templates_path: nginx-includes
-nginx_includes_deprecated: roles/wordpress-setup/templates/includes.d
-nginx_includes_pattern: "^({{ nginx_includes_templates_path | regex_escape }}|{{ nginx_includes_deprecated | regex_escape }})/(.*)\\.j2$"
+nginx_includes_pattern: "^({{ nginx_includes_templates_path | regex_escape }})/(.*)\\.j2$"
nginx_includes_d_cleanup: true
# h5bp helpers
@@ -50,6 +49,11 @@ h5bp_cross_domain_fonts_enabled: "{{ h5bp.cross_domain_fonts | default(true) }}"
h5bp_expires_enabled: "{{ h5bp.expires | default(false) }}"
h5bp_protect_system_files_enabled: "{{ h5bp.protect_system_files | default(true) }}"
+# X-Robots-Tag Header helpers
+not_prod: "{{ env != 'production' }}"
+robots_tag_header: "{{ item.value.robots_tag_header | default({}) }}"
+robots_tag_header_enabled: "{{ robots_tag_header.enabled | default(not_prod) }}"
+
# PHP FPM
php_fpm_pm_max_children: 10
php_fpm_pm_start_servers: 1
diff --git a/roles/wordpress-setup/tasks/database.yml b/roles/wordpress-setup/tasks/database.yml
index 61c74a5475..ad10a40026 100644
--- a/roles/wordpress-setup/tasks/database.yml
+++ b/roles/wordpress-setup/tasks/database.yml
@@ -7,6 +7,7 @@
login_host: "{{ site_env.db_host }}"
login_user: "{{ mysql_root_user }}"
login_password: "{{ mysql_root_password }}"
+ no_log: true
with_dict: "{{ wordpress_sites }}"
- name: Create/assign database user to db and grant permissions
@@ -20,6 +21,7 @@
login_host: "{{ site_env.db_host }}"
login_user: "{{ mysql_root_user }}"
login_password: "{{ mysql_root_password }}"
+ no_log: true
with_dict: "{{ wordpress_sites }}"
when: site_uses_local_db and item.value.db_create | default(true)
diff --git a/roles/wordpress-setup/tasks/main.yml b/roles/wordpress-setup/tasks/main.yml
index 59e3dc60f4..ac1fd8a735 100644
--- a/roles/wordpress-setup/tasks/main.yml
+++ b/roles/wordpress-setup/tasks/main.yml
@@ -11,7 +11,7 @@
path: "{{ www_root }}"
owner: "{{ web_user }}"
group: "{{ web_group }}"
- mode: 0755
+ mode: '0755'
state: directory
- name: Create logs folder of sites
@@ -19,20 +19,21 @@
path: "{{ www_root }}/{{ item.key }}/logs"
owner: "{{ web_user }}"
group: "{{ web_group }}"
- mode: 0755
+ mode: '0755'
state: directory
with_dict: "{{ wordpress_sites }}"
- name: Create WordPress php-fpm configuration file
template:
src: php-fpm.conf.j2
- dest: /etc/php/7.3/fpm/pool.d/wordpress.conf
+ dest: /etc/php/{{ php_version }}/fpm/pool.d/wordpress.conf
+ mode: '0644'
notify: reload php-fpm
- name: Disable default PHP-FPM pool
- command: mv /etc/php/7.3/fpm/pool.d/www.conf /etc/php/7.3/fpm/pool.d/www.disabled
+ command: mv /etc/php/{{ php_version }}/fpm/pool.d/www.conf /etc/php/{{ php_version }}/fpm/pool.d/www.disabled
args:
- creates: /etc/php/7.3/fpm/pool.d/www.disabled
+ creates: /etc/php/{{ php_version }}/fpm/pool.d/www.disabled
when: disable_default_pool | default(true)
notify: reload php-fpm
@@ -45,7 +46,7 @@
- name: Setup WP system cron
cron:
name: "{{ item.key }} WordPress cron"
- minute: "*/15"
+ minute: "{{ item.value.cron_interval | default('*/15') }}"
user: "{{ web_user }}"
job: "cd {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }} && wp cron event run --due-now > /dev/null 2>&1"
cron_file: "wordpress-{{ item.key | replace('.', '_') }}"
@@ -55,7 +56,7 @@
- name: Setup WP Multisite system cron
cron:
name: "{{ item.key }} WordPress network cron"
- minute: "*/30"
+ minute: "{{ item.value.cron_interval_multisite | default('*/30') }}"
user: "{{ web_user }}"
job: "cd {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }} && wp site list --field=url | xargs -n1 -I \\% wp --url=\\% cron event run --due-now > /dev/null 2>&1"
cron_file: "wordpress-multisite-{{ item.key | replace('.', '_') }}"
diff --git a/roles/wordpress-setup/tasks/nginx-client-cert.yml b/roles/wordpress-setup/tasks/nginx-client-cert.yml
index 49d810eee5..69f7026a22 100644
--- a/roles/wordpress-setup/tasks/nginx-client-cert.yml
+++ b/roles/wordpress-setup/tasks/nginx-client-cert.yml
@@ -3,6 +3,6 @@
get_url:
url: "{{ item.value.ssl.client_cert_url }}"
dest: "{{ nginx_ssl_path }}/client-{{ (item.value.ssl.client_cert_url | hash('md5'))[:7] }}.crt"
- mode: 0640
+ mode: '0640'
with_dict: "{{ wordpress_sites }}"
when: ssl_enabled and item.value.ssl.client_cert_url is defined
diff --git a/roles/wordpress-setup/tasks/nginx-includes.yml b/roles/wordpress-setup/tasks/nginx-includes.yml
index 960a82fdf1..a18e2e1851 100644
--- a/roles/wordpress-setup/tasks/nginx-includes.yml
+++ b/roles/wordpress-setup/tasks/nginx-includes.yml
@@ -3,23 +3,17 @@
find:
paths:
- "{{ nginx_includes_templates_path }}"
- - "{{ nginx_includes_deprecated }}"
pattern: "*.conf.j2"
recurse: yes
become: no
delegate_to: localhost
register: nginx_includes_templates
-- name: Warn about deprecated Nginx includes directory
- debug:
- msg: "[DEPRECATION WARNING]: The `{{ nginx_includes_deprecated }}` directory for Trellis Nginx includes templates is deprecated and will no longer function beginning with Trellis 1.0. Please move these templates to a directory named `{{ nginx_includes_templates_path }}` in the root of this project. For more information, see https://roots.io/trellis/docs/nginx-includes/"
- when: True in nginx_includes_templates.files | map(attribute='path') | map('search', nginx_includes_deprecated | regex_escape) | list
-
- name: Create includes.d directories
file:
path: "{{ nginx_path }}/includes.d/{{ item }}"
state: directory
- mode: 0755
+ mode: '0755'
with_items: "{{ nginx_includes_templates.files | map(attribute='path') |
map('regex_replace', nginx_includes_pattern, '\\2') |
map('dirname') | unique | list | sort
@@ -30,6 +24,7 @@
template:
src: "{{ item }}"
dest: "{{ nginx_path }}/includes.d/{{ item | regex_replace(nginx_includes_pattern, '\\2') }}"
+ mode: '0644'
with_items: "{{ nginx_includes_templates.files | map(attribute='path') | list | sort(True) }}"
notify: reload nginx
@@ -39,7 +34,7 @@
pattern: "*.conf"
recurse: yes
register: nginx_includes_existing
- when: nginx_includes_d_cleanup
+ when: nginx_includes_d_cleanup | bool
- name: Remove unmanaged files from includes.d
file:
diff --git a/roles/wordpress-setup/tasks/nginx.yml b/roles/wordpress-setup/tasks/nginx.yml
index ffb9fcb11f..ebeb80c49b 100644
--- a/roles/wordpress-setup/tasks/nginx.yml
+++ b/roles/wordpress-setup/tasks/nginx.yml
@@ -3,7 +3,7 @@
copy:
src: "{{ item.value.ssl.cert }}"
dest: "{{ nginx_ssl_path }}/{{ item.value.ssl.cert | basename }}"
- mode: 0640
+ mode: '0640'
with_dict: "{{ wordpress_sites }}"
when: ssl_enabled and item.value.ssl.cert is defined
notify: reload nginx
@@ -12,7 +12,7 @@
copy:
src: "{{ item.value.ssl.key }}"
dest: "{{ nginx_ssl_path }}/{{ item.value.ssl.key | basename }}"
- mode: 0600
+ mode: '0600'
with_dict: "{{ wordpress_sites }}"
when: ssl_enabled and item.value.ssl.key is defined
notify: reload nginx
@@ -23,17 +23,28 @@
template:
src: "{{ item.src }}"
dest: "{{ nginx_path }}/sites-available/{{ item.src | basename | regex_replace('.j2$', '') }}"
+ mode: '0644'
with_items: "{{ nginx_sites_confs }}"
when: item.enabled | default(true)
notify: reload nginx
tags: nginx-sites
-- name: Enable or disable Nginx sites
+- name: Enable Nginx sites
file:
path: "{{ nginx_path }}/sites-enabled/{{ item.src | basename | regex_replace('.j2$', '') }}"
src: "{{ nginx_path }}/sites-available/{{ item.src | basename | regex_replace('.j2$', '') }}"
- state: "{{ item.enabled | default(true) | ternary('link', 'absent') }}"
+ state: link
force: yes
+ when: item.enabled | default(true)
+ with_items: "{{ nginx_sites_confs }}"
+ notify: reload nginx
+ tags: nginx-sites
+
+- name: Disable Nginx sites
+ file:
+ path: "{{ nginx_path }}/sites-enabled/{{ item.src | basename | regex_replace('.j2$', '') }}"
+ state: absent
+ when: not(item.enabled | default(true))
with_items: "{{ nginx_sites_confs }}"
notify: reload nginx
tags: nginx-sites
@@ -42,12 +53,14 @@
template:
src: "{{ playbook_dir }}/roles/letsencrypt/templates/acme-challenge-location.conf.j2"
dest: "{{ nginx_path }}/acme-challenge-location.conf"
+ mode: '0644'
notify: reload nginx
- name: Create WordPress configuration for Nginx
template:
src: "{{ item.value.nginx_wordpress_site_conf | default(nginx_wordpress_site_conf) }}"
dest: "{{ nginx_path }}/sites-available/{{ item.key }}.conf"
+ mode: '0644'
with_dict: "{{ wordpress_sites }}"
notify: reload nginx
tags: nginx-includes
diff --git a/roles/wordpress-setup/tasks/self-signed-certificate.yml b/roles/wordpress-setup/tasks/self-signed-certificate.yml
index 64e355fce2..fea0d9cc99 100644
--- a/roles/wordpress-setup/tasks/self-signed-certificate.yml
+++ b/roles/wordpress-setup/tasks/self-signed-certificate.yml
@@ -3,31 +3,31 @@
file:
path: "{{ nginx_ssl_path }}/self-signed-openssl-configs/"
state: directory
- mode: "0755"
+ mode: '0755'
- name: Template openssl configs
template:
src: self-signed-openssl-config.j2
dest: "{{ nginx_ssl_path }}/self-signed-openssl-configs/{{ item.key }}.cnf"
+ mode: '0644'
with_dict: "{{ wordpress_sites | combine(ssl_default_site) }}"
when:
- - sites_use_ssl
- - ssl_enabled
+ - sites_use_ssl | bool
+ - ssl_enabled | bool
- item.value.ssl.provider | default('manual') == 'self-signed'
- name: Generate self-signed certificates
- shell: "openssl req -new -newkey rsa:2048 \
+ command: "openssl req -new -newkey rsa:2048 \
-days 3650 -nodes -x509 -sha256 \
-extensions req_ext -config {{ nginx_ssl_path }}/self-signed-openssl-configs/{{ item.key }}.cnf \
-keyout {{ item.key | quote }}.key -out {{ item.key | quote }}.cert"
args:
- executable: "/bin/bash"
chdir: "{{ nginx_ssl_path }}"
creates: "{{ item.key }}.*"
with_dict: "{{ wordpress_sites | combine(ssl_default_site) }}"
when:
- - sites_use_ssl
- - ssl_enabled
+ - sites_use_ssl | bool
+ - ssl_enabled | bool
- item.value.ssl.provider | default('manual') == 'self-signed'
notify: reload nginx
diff --git a/roles/wordpress-setup/templates/self-signed-openssl-config.j2 b/roles/wordpress-setup/templates/self-signed-openssl-config.j2
index 8babe10d98..9ba1054aef 100644
--- a/roles/wordpress-setup/templates/self-signed-openssl-config.j2
+++ b/roles/wordpress-setup/templates/self-signed-openssl-config.j2
@@ -4,4 +4,4 @@ distinguished_name = req_dn
[req_dn]
commonName = {{ item.value.site_hosts[0].canonical }}
[req_ext]
-subjectAltName = {{ site_hosts | union(multisite_subdomains_wildcards) | map('regex_replace', '(.*)', 'DNS:\\1') | join(',') }}
+subjectAltName = {{ site_hosts | union(multisite_subdomains_wildcards) | map('regex_replace', '(^.*$)', 'DNS:\\1') | join(',') }}
diff --git a/roles/wordpress-setup/templates/ssl.no-default.conf.j2 b/roles/wordpress-setup/templates/ssl.no-default.conf.j2
index a29b113dbd..479979c710 100644
--- a/roles/wordpress-setup/templates/ssl.no-default.conf.j2
+++ b/roles/wordpress-setup/templates/ssl.no-default.conf.j2
@@ -12,7 +12,6 @@ server {
listen 443 ssl default_server deferred;
include h5bp/directive-only/ssl.conf;
- ssl_dhparam /etc/nginx/ssl/dhparams.pem;
ssl_certificate {{ nginx_path }}/ssl/no_default.cert;
ssl_trusted_certificate {{ nginx_path }}/ssl/no_default.cert;
diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2
index c951c49340..2096c6d45b 100644
--- a/roles/wordpress-setup/templates/wordpress-site.conf.j2
+++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2
@@ -15,7 +15,7 @@ server {
{% endblock %}
{% block server_basic -%}
- root {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/web;
+ root {{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/{{ item.value.public_path | default('web') }};
index index.php index.htm index.html;
add_header Fastcgi-Cache $upstream_cache_status;
@@ -48,6 +48,7 @@ server {
{% endif -%}
{% endblock -%}
+
{% block multisite_rewrites -%}
{% if item.value.multisite.enabled | default(false) -%}
# Multisite rewrites
@@ -74,7 +75,6 @@ server {
include h5bp/directive-only/ssl-stapling.conf;
{% endif -%}
- ssl_dhparam /etc/nginx/ssl/dhparams.pem;
ssl_buffer_size 1400; # 1400 bytes to fit in one MTU
{% if item.value.ssl.provider | default('manual') != 'self-signed' -%}
@@ -91,7 +91,7 @@ server {
ssl_certificate_key {{ nginx_path }}/ssl/{{ item.value.ssl.key | basename }};
{% elif item.value.ssl.provider | default('manual') == 'letsencrypt' -%}
- ssl_certificate {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}-{{ letsencrypt_cert_ids[item.key] }}-bundled.cert;
+ ssl_certificate {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}-bundled.cert;
ssl_certificate_key {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}.key;
{% elif item.value.ssl.provider | default('manual') == 'self-signed' -%}
@@ -120,7 +120,7 @@ server {
deny all;
}
{% endblock %}
-
+
{% block blade_twig_templates -%}
# Prevent Blade and Twig templates from being accessed directly.
location ~* \.(blade\.php|twig)$ {
@@ -128,6 +128,40 @@ server {
}
{% endblock %}
+ {% block dependency_managers -%}
+ # composer
+ location ~* composer\.(json|lock)$ {
+ deny all;
+ }
+
+ location ~* composer/installed\.json$ {
+ deny all;
+ }
+
+ location ~* auth\.json$ {
+ deny all;
+ }
+
+ # npm
+ location ~* package(-lock)?\.json$ {
+ deny all;
+ }
+
+ # yarn
+ location ~* yarn\.lock$ {
+ deny all;
+ }
+
+ # bundler
+ location ~* Gemfile(\.lock)?$ {
+ deny all;
+ }
+
+ location ~* gems\.(rb|locked)?$ {
+ deny all;
+ }
+ {% endblock %}
+
{% block location_primary -%}
location / {
try_files $uri $uri/ /index.php?$args;
@@ -172,13 +206,14 @@ server {
{% block embed_security -%}
{% if item.value.nginx_embed_security | default(nginx_embed_security | default(true)) -%}
add_header Content-Security-Policy "frame-ancestors 'self'" always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ {% endif -%}
+ {% endblock -%}
- # Conditional X-Frame-Options until https://core.trac.wordpress.org/ticket/40020 is resolved
- set $x_frame_options SAMEORIGIN;
- if ($arg_customize_changeset_uuid) {
- set $x_frame_options "";
- }
- add_header X-Frame-Options $x_frame_options always;
+ {% block robots_tag_header -%}
+ {% if robots_tag_header_enabled -%}
+ # Prevent search engines from indexing non-production environments
+ add_header X-Robots-Tag "noindex, nofollow" always;
{% endif -%}
{% endblock -%}
diff --git a/roles/wp-cli/defaults/main.yml b/roles/wp-cli/defaults/main.yml
index 6abe35ebe1..b27b963495 100644
--- a/roles/wp-cli/defaults/main.yml
+++ b/roles/wp-cli/defaults/main.yml
@@ -1,5 +1,5 @@
gpg2_package: gnupg2
-wp_cli_version: 2.5.0
+wp_cli_version: 2.6.0
wp_cli_bin_path: /usr/bin/wp
wp_cli_phar_url: "https://github.com/wp-cli/wp-cli/releases/download/v{{ wp_cli_version }}/wp-cli-{{ wp_cli_version }}.phar"
wp_cli_phar_asc_url: "https://github.com/wp-cli/wp-cli/releases/download/v{{ wp_cli_version }}/wp-cli-{{ wp_cli_version }}.phar.asc"
diff --git a/roles/wp-cli/tasks/main.yml b/roles/wp-cli/tasks/main.yml
index 675596e141..dab02c6ea8 100644
--- a/roles/wp-cli/tasks/main.yml
+++ b/roles/wp-cli/tasks/main.yml
@@ -19,10 +19,24 @@
copy:
src: "{{ wp_cli_pgp_public_key }}"
dest: /tmp/wp-cli.pgp.gpg
+ mode: '0744'
-- name: Verify WP-CLI Phar Signature
- command: gpg2 --lock-never --no-default-keyring --keyring /tmp/wp-cli.pgp.gpg --verify /tmp/wp-cli-{{ wp_cli_version }}.phar.asc /tmp/wp-cli-{{ wp_cli_version }}.phar
- changed_when: false
+- name: Verify WP-CLI
+ block:
+ - name: Check GPG signature
+ command: gpg2 --lock-never --no-default-keyring --keyring /tmp/wp-cli.pgp.gpg --verify /tmp/wp-cli-{{ wp_cli_version }}.phar.asc /tmp/wp-cli-{{ wp_cli_version }}.phar
+ changed_when: false
+ rescue:
+ - name: Delete invalid WP-CLI Phar file
+ file:
+ path: "{{ item }}"
+ state: absent
+ with_items:
+ - "/tmp/wp-cli-{{ wp_cli_version }}.phar"
+ - "/tmp/wp-cli-{{ wp_cli_version }}.phar.asc"
+ - name: Fail verification
+ fail:
+ msg: "WP-CLI Phar signature could not be verified. Please try again."
- name: Install WP-CLI
command: rsync -c --chmod=0755 --info=name /tmp/wp-cli-{{ wp_cli_version }}.phar {{ wp_cli_bin_path }}
@@ -46,6 +60,7 @@
- name: Install WP-CLI packages
command: wp package install {{ item }}
+ become: true
become_user: "{{ web_user }}"
register: wp_cli_packages_installed
changed_when:
diff --git a/roles/xdebug-tunnel/defaults/main.yml b/roles/xdebug-tunnel/defaults/main.yml
index 764f6a43fa..3623b4ac55 100644
--- a/roles/xdebug-tunnel/defaults/main.yml
+++ b/roles/xdebug-tunnel/defaults/main.yml
@@ -1,6 +1,6 @@
-xdebug_tunnel_remote_port: 9000
+xdebug_tunnel_remote_port: 9003
xdebug_tunnel_host: localhost
-xdebug_tunnel_local_port: 9000
+xdebug_tunnel_local_port: 9003
xdebug_tunnel_control_socket: /tmp/trellis-xdebug-{{ xdebug_tunnel_inventory_host }}
xdebug_tunnel_control_identity: "{{ ansible_user_id }}"
diff --git a/roles/xdebug-tunnel/tasks/main.yml b/roles/xdebug-tunnel/tasks/main.yml
index 86a84909c0..84ec403faf 100644
--- a/roles/xdebug-tunnel/tasks/main.yml
+++ b/roles/xdebug-tunnel/tasks/main.yml
@@ -2,7 +2,7 @@
- name: Create or close Xdebug SSH tunnel
command: |
{% if xdebug_remote_enable | bool %}
- ssh -M -S '{{ xdebug_tunnel_control_socket }}' -fnNT -R {{ xdebug_tunnel_port_mapping }} {{ xdebug_tunnel_user_at_host}} '{{ xdebug_tunnel_control_identity }}'
+ ssh -M -S '{{ xdebug_tunnel_control_socket }}' -fnNT -R {{ xdebug_tunnel_port_mapping }} {{ xdebug_tunnel_user_at_host }} '{{ xdebug_tunnel_control_identity }}'
{% else %}
ssh -S '{{ xdebug_tunnel_control_socket }}' -O exit '{{ xdebug_tunnel_control_identity }}'
{% endif %}
@@ -10,6 +10,7 @@
become: no
register: xdebug_tunnel
ignore_errors: true
+ changed_when: true
- name: Interpret and present Xdebug SSH tunnel errors
fail:
diff --git a/roles/xdebug/defaults/main.yml b/roles/xdebug/defaults/main.yml
index 3efa2ccfb2..cb7be83bf7 100644
--- a/roles/xdebug/defaults/main.yml
+++ b/roles/xdebug/defaults/main.yml
@@ -1,6 +1,14 @@
-php_xdebug_package: php-xdebug
+# XDebug Generic
+xdebug_output_dir: /tmp
+xdebug_trigger_value:
# XDebug Remote Debugging
+xdebug_mode: 'off'
+xdebug_start_with_request: 'no'
+xdebug_discover_client_host: 0
+xdebug_client_host: localhost
+xdebug_client_port: 9003
+xdebug_log: /tmp/xdebug.log
xdebug_remote_enable: 0
xdebug_remote_connect_back: 0
xdebug_remote_autostart: 0
@@ -8,7 +16,6 @@ xdebug_remote_host: localhost
xdebug_remote_port: 9000
xdebug_remote_log: /tmp/xdebug.log
xdebug_idekey: XDEBUG
-xdebug_extended_info: 1
xdebug_max_nesting_level: 200
# XDebug Display Settings
@@ -21,24 +28,13 @@ xdebug_var_display_max_depth: 3
# XDebug Function/Stack Traces
xdebug_collect_assignments: 0
-xdebug_collect_includes: 1
-xdebug_collect_params: 0
xdebug_collect_return: 0
-xdebug_collect_vars: 0
xdebug_show_exception_trace: 0
xdebug_show_local_vars: 0
-xdebug_show_mem_delta: 0
-xdebug_trace_enable_trigger: 0
-xdebug_trace_enable_trigger_value:
xdebug_trace_format: 0
xdebug_trace_options: 0
-xdebug_trace_output_dir: /tmp
xdebug_trace_output_name: trace.%c
# XDebug Profiler
xdebug_profiler_append: 0
-xdebug_profiler_enable: 0
-xdebug_profiler_enable_trigger: 0
-xdebug_profiler_enable_trigger_value:
-xdebug_profiler_output_dir: /tmp
xdebug_profiler_output_name: cachegrind.out.%p
diff --git a/roles/xdebug/tasks/main.yml b/roles/xdebug/tasks/main.yml
index deadc88c50..2b52f58420 100644
--- a/roles/xdebug/tasks/main.yml
+++ b/roles/xdebug/tasks/main.yml
@@ -1,34 +1,25 @@
---
-- block:
- - name: Install Xdebug
- apt:
- name: "{{ php_xdebug_package }}"
- state: "{{ php_xdebug_package_state | default(apt_dev_package_state) }}"
- cache_valid_time: "{{ apt_cache_valid_time }}"
+- name: Install Xdebug
+ apt:
+ name: "{{ php_xdebug_package }}"
+ state: "{{ php_xdebug_package_state | default(apt_dev_package_state) }}"
+ cache_valid_time: "{{ apt_cache_valid_time }}"
- - name: Template the Xdebug configuration file
- template:
- src: xdebug.ini.j2
- dest: /etc/php/7.3/mods-available/xdebug.ini
- notify: reload php-fpm
-
- - name: Ensure 20-xdebug.ini is present
- file:
- src: /etc/php/7.3/mods-available/xdebug.ini
- dest: /etc/php/7.3/fpm/conf.d/20-xdebug.ini
- state: link
- notify: reload php-fpm
-
- when: xdebug_remote_enable | bool
+- name: Template the Xdebug configuration file
+ template:
+ src: xdebug.ini.j2
+ dest: /etc/php/{{ php_version }}/mods-available/xdebug.ini
+ mode: '0644'
+ notify: reload php-fpm
-- name: Disable Xdebug
+- name: Ensure 20-xdebug.ini is present
file:
- path: /etc/php/7.3/fpm/conf.d/20-xdebug.ini
- state: absent
- when: not xdebug_remote_enable | bool
+ src: /etc/php/{{ php_version }}/mods-available/xdebug.ini
+ dest: /etc/php/{{ php_version }}/fpm/conf.d/20-xdebug.ini
+ state: link
notify: reload php-fpm
- name: Disable Xdebug CLI
file:
- path: /etc/php/7.3/cli/conf.d/20-xdebug.ini
+ path: /etc/php/{{ php_version }}/cli/conf.d/20-xdebug.ini
state: absent
diff --git a/roles/xdebug/templates/xdebug.ini.j2 b/roles/xdebug/templates/xdebug.ini.j2
index 3490bc1d9b..a4c2644c58 100644
--- a/roles/xdebug/templates/xdebug.ini.j2
+++ b/roles/xdebug/templates/xdebug.ini.j2
@@ -3,7 +3,17 @@
[XDebug]
zend_extension=xdebug.so
+; Generic
+xdebug.output_dir={{ xdebug_output_dir }}
+xdebug.trigger_value={{ xdebug_trigger_value }}
+
; Remote Debugging
+xdebug.mode={{ xdebug_mode }}
+xdebug.start_with_request={{ xdebug_start_with_request }}
+xdebug.discover_client_host={{ xdebug_discover_client_host }}
+xdebug.client_host={{ xdebug_client_host }}
+xdebug.client_port={{ xdebug_client_port }}
+xdebug.log={{ xdebug_log }}
xdebug.remote_enable={{ xdebug_remote_enable }}
xdebug.remote_connect_back={{ xdebug_remote_connect_back }}
xdebug.remote_autostart={{ xdebug_remote_autostart }}
@@ -12,7 +22,6 @@ xdebug.remote_port={{ xdebug_remote_port }}
xdebug.remote_handler=dbgp
xdebug.remote_log={{ xdebug_remote_log }}
xdebug.idekey={{ xdebug_idekey }}
-xdebug.extended_info={{ xdebug_extended_info }}
xdebug.max_nesting_level={{ xdebug_max_nesting_level }}
; Display Settings
@@ -25,24 +34,13 @@ xdebug.var_display_max_depth={{ xdebug_var_display_max_depth }}
; Function/Stack Traces
xdebug.collect_assignments={{ xdebug_collect_assignments }}
-xdebug.collect_includes={{ xdebug_collect_includes }}
-xdebug.collect_params={{ xdebug_collect_params }}
xdebug.collect_return={{ xdebug_collect_return }}
-xdebug.collect_vars={{ xdebug_collect_vars }}
xdebug.show_exception_trace={{ xdebug_show_exception_trace }}
xdebug.show_local_vars={{ xdebug_show_local_vars }}
-xdebug.show_mem_delta={{ xdebug_show_mem_delta }}
-xdebug.trace_enable_trigger={{ xdebug_trace_enable_trigger }}
-xdebug.trace_enable_trigger_value={{ xdebug_trace_enable_trigger_value }}
xdebug.trace_format={{ xdebug_trace_format }}
xdebug.trace_options={{ xdebug_trace_options }}
-xdebug.trace_output_dir={{ xdebug_trace_output_dir }}
xdebug.trace_output_name={{ xdebug_trace_output_name }}
; Profiler
xdebug.profiler_append={{ xdebug_profiler_append }}
-xdebug.profiler_enable={{ xdebug_profiler_enable }}
-xdebug.profiler_enable_trigger={{ xdebug_profiler_enable_trigger }}
-xdebug.profiler_enable_trigger_value={{ xdebug_profiler_enable_trigger_value }}
-xdebug.profiler_output_dir={{ xdebug_profiler_output_dir }}
xdebug.profiler_output_name={{ xdebug_profiler_output_name }}
diff --git a/server.yml b/server.yml
index e15d0e1f73..77f7ef0926 100644
--- a/server.yml
+++ b/server.yml
@@ -9,14 +9,7 @@
roles:
- { role: connection, tags: [connection, always] }
-- name: Set ansible_python_interpreter
- hosts: web:&{{ env }}
- gather_facts: false
- become: yes
- roles:
- - { role: python_interpreter, tags: [always] }
-
-- name: WordPress Server - Install LEMP Stack with PHP 7.3 and MariaDB MySQL
+- name: WordPress Server - Install LEMP Stack with PHP and MariaDB MySQL
hosts: web:&{{ env }}
become: yes
roles:
diff --git a/vagrant.default.yml b/vagrant.default.yml
index 64b48f7dfe..85df0b0622 100644
--- a/vagrant.default.yml
+++ b/vagrant.default.yml
@@ -1,10 +1,14 @@
---
-vagrant_ip: '192.168.50.5'
+vagrant_ip: '192.168.56.5'
vagrant_cpus: 1
vagrant_memory: 1024 # in MB
-vagrant_box: 'bento/ubuntu-18.04'
-vagrant_box_version: '>= 201807.12.0'
-vagrant_ansible_version: '2.7.12'
+vagrant_box: 'bento/ubuntu-20.04'
+vagrant_box_version: '>= 202012.23.0'
+## Uncomment below for use on Apple M1/Arm hardware, and comment out the two lines above this
+#vagrant_box: 'jeffnoxon/ubuntu-20.04-arm64'
+#vagrant_box_version: '>= 1.0.0'
+#vagrant_ansible_python_interpreter: '/usr/bin/python3'
+vagrant_ansible_version: '2.10.7'
vagrant_skip_galaxy: false
vagrant_mount_type: 'nfs'
diff --git a/xdebug-tunnel.yml b/xdebug-tunnel.yml
index 2c8a975438..014c1f0d41 100644
--- a/xdebug-tunnel.yml
+++ b/xdebug-tunnel.yml
@@ -15,5 +15,5 @@
handlers:
- name: reload php-fpm
service:
- name: php7.3-fpm
+ name: php{{ php_version }}-fpm
state: reloaded