diff --git a/tools/ansible/deploy.yml b/tools/ansible/deploy.yml new file mode 100644 index 0000000..87bdd72 --- /dev/null +++ b/tools/ansible/deploy.yml @@ -0,0 +1,12 @@ +--- +- hosts: all + + vars_files: + - vars/vars.yml + - vars/provision_vault.yml + - vars/provision_vars.yml + - vars/deploy_vault.yml + - vars/deploy_vars.yml + + roles: + - name: ansistrano.deploy diff --git a/tools/ansible/deploy/after-symlink-shared.yml b/tools/ansible/deploy/after-symlink-shared.yml new file mode 100644 index 0000000..b131981 --- /dev/null +++ b/tools/ansible/deploy/after-symlink-shared.yml @@ -0,0 +1,10 @@ +--- +- name: Setup directory permissions for files directories + become: true + file: + path: '{{ ansistrano_shared_path }}/{{ project_web_dir }}/sites/default/files' + state: directory + owner: www-data + group: root + mode: u=rwx,g=rw,o= + recurse: true diff --git a/tools/ansible/deploy/after-update-code.yml b/tools/ansible/deploy/after-update-code.yml new file mode 100644 index 0000000..f5ba334 --- /dev/null +++ b/tools/ansible/deploy/after-update-code.yml @@ -0,0 +1,9 @@ +--- +- name: Install Composer dependencies + composer: + command: install + working_dir: '{{ ansistrano_release_path.stdout }}' + +- name: Generate settings.php file + include_role: + name: './roles/drupal-settings' diff --git a/tools/ansible/id_deploy b/tools/ansible/id_deploy new file mode 100644 index 0000000..8f187bc --- /dev/null +++ b/tools/ansible/id_deploy @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAvj6ZFxvDdMz/nXNR3mw10Lmq/VB/ZeYS9Z8TWRGoZOb1xTHhLsvA +LNMrWEeu473MYLOGPKzOaUzw4sev61AWZ71mnKtt3VAE+DkuMJ+SXGql5oshSWNwYcivcM +aCmaW6VC/BfTlPemi4bb1KybEwH5vMMHxbsKBhUL3GKiNYnKAeb/QrdP/ZCahYJnOHJVda +FfEatd7dE+nZwEtzAKuxu6lyYIzDXd4xW6B2Zf1NhROXB1p99VwayEZ+S7Jaih2FFIQn6m +74/vcd451XpMevxpwX0qAkSZIPx+owzQmM7+ei9yuJQ0LO52ECkbMu914gUsiJISE0Fp1D ++4pBjnBgTwAAA9BOMZjITjGYyAAAAAdzc2gtcnNhAAABAQC+PpkXG8N0zP+dc1HebDXQua +r9UH9l5hL1nxNZEahk5vXFMeEuy8As0ytYR67jvcxgs4Y8rM5pTPDix6/rUBZnvWacq23d +UAT4OS4wn5JcaqXmiyFJY3BhyK9wxoKZpbpUL8F9OU96aLhtvUrJsTAfm8wwfFuwoGFQvc +YqI1icoB5v9Ct0/9kJqFgmc4clV1oV8Rq13t0T6dnAS3MAq7G7qXJgjMNd3jFboHZl/U2F +E5cHWn31XBrIRn5LslqKHYUUhCfqbvj+9x3jnVekx6/GnBfSoCRJkg/H6jDNCYzv56L3K4 +lDQs7nYQKRsy73XiBSyIkhITQWnUP7ikGOcGBPAAAAAwEAAQAAAQEAgYUIoOzr8vcmB8Hd +OPqe4M2nFfLZ6TvsKID2oSseZCPBq1E8J6nb2iiiV+XlsXMkU6mleGWF2bbiQOMGZ8QMnP +AbjgpAL+4sk/oJ8lyRPo31CLIsJVSnzEre9n6Pp59m7a3doy2DaKkm9r9qzUnuo3ZkW5Yu +rl9iIaAx67pIt8g2shG3cHgfLMgyr3MNXTzKOHJyFcvOsf+BLFOkj5dUcYf7iKQPtd9ffP +2fcOSHuj/JfQgaMokEwbjE1lUHw0MTvSzCR/wT1t2VpqAgcKG6ywkW5OQzhpfzvCLpGSKF +8vtlVxAjM44uHHojJPQINDnwarkfmiRTjIQmJnxcoBfHsQAAAIBKbDbv9mQ8dWroZ0Qilz ++qQ2tluIeGCu+GX/SW0DtxxXZom5mM2oZi6pZTZsvKJnvYwMvwI6AYXPDMHdmIIn5tvyio +kz7ghjitE8KV2VaR+f5Sx0sw1XRQS5Nhc1whIXW74/+ePz9PH2WIf8OIQ6kT4Kw+VEukbq +IOLujsHWJ0eQAAAIEA3PwD5w3v18wt96yy6Dkb9EPVhk7lvlagFlUzqFY6bxAI05mjiFOB +jksDTQXPj1NgRIp8iRUFORlMXlvGA6S8ZTnZxfTUfFJYzZCWmbrzrnDZRi2BFE0/css2PP +DFmYkd2wLHOYEOEv4NlDa0+4tn7mnyyDnv9okzCaiftPRmX00AAACBANxjpyN9nSD2v6H3 +biHpEGFfa74IaB3kMBkdsxd/H8xAxJydt6rSF8FMyI7tnE2GuRKilpu138vOu0t9np4QPv +IRE/r/7dgC6kbMeDPRDV98CvyepJ6p4GA2yWGTDk89hAWEM7CNuNacRMGouBqQorWScN+O +uwNqDNbC8g8hH2gLAAAAFG9wZGF2aWVzQE9saXZlcnMtTUJQAQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/tools/ansible/id_deploy.pub b/tools/ansible/id_deploy.pub new file mode 100644 index 0000000..febca1a --- /dev/null +++ b/tools/ansible/id_deploy.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+PpkXG8N0zP+dc1HebDXQuar9UH9l5hL1nxNZEahk5vXFMeEuy8As0ytYR67jvcxgs4Y8rM5pTPDix6/rUBZnvWacq23dUAT4OS4wn5JcaqXmiyFJY3BhyK9wxoKZpbpUL8F9OU96aLhtvUrJsTAfm8wwfFuwoGFQvcYqI1icoB5v9Ct0/9kJqFgmc4clV1oV8Rq13t0T6dnAS3MAq7G7qXJgjMNd3jFboHZl/U2FE5cHWn31XBrIRn5LslqKHYUUhCfqbvj+9x3jnVekx6/GnBfSoCRJkg/H6jDNCYzv56L3K4lDQs7nYQKRsy73XiBSyIkhITQWnUP7ikGOcGBP Ansistrano deployment \ No newline at end of file diff --git a/tools/ansible/requirements.yml b/tools/ansible/requirements.yml index bffd45c..b3a7785 100644 --- a/tools/ansible/requirements.yml +++ b/tools/ansible/requirements.yml @@ -1,4 +1,8 @@ --- +- name: ansistrano.deploy + version: 3.4.0 +- name: ansistrano.rollback + version: 3.0.0 - name: geerlingguy.composer version: 1.7.3 - name: geerlingguy.firewall diff --git a/tools/ansible/roles/drupal-settings/LICENSE b/tools/ansible/roles/drupal-settings/LICENSE new file mode 100644 index 0000000..53f4980 --- /dev/null +++ b/tools/ansible/roles/drupal-settings/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Oliver Davies + +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 the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/ansible/roles/drupal-settings/README.md b/tools/ansible/roles/drupal-settings/README.md new file mode 100644 index 0000000..f3cd913 --- /dev/null +++ b/tools/ansible/roles/drupal-settings/README.md @@ -0,0 +1,27 @@ +# Ansible Role: Drupal settings + +```yaml +drupal_settings: + - drupal_root: /var/www/web + sites: + - name: default + filename: settings.php # Optional, defaults to 'settings.php' + settings: + base_url: https://www.example.com # Optional, Drupal 7 + hash_salt: '' # Optional + databases: + default: # The database key + default: # The database target + driver: mysql # Optional, defaults to 'mysql' + host: localhost # Optional, defaults to 'localhost' + database: mydatabase + username: user + password: secret + config_directories: # Optional, Drupal 8 + sync: path/to/config + trusted_hosts: # Optional, Drupal 8 + - '^example\.com$' + - '^.+\.example\.com$' + - '^example\.org$' + - '^.+\.example\.org$' +``` diff --git a/tools/ansible/roles/drupal-settings/defaults/main.yml b/tools/ansible/roles/drupal-settings/defaults/main.yml new file mode 100644 index 0000000..cfd76d5 --- /dev/null +++ b/tools/ansible/roles/drupal-settings/defaults/main.yml @@ -0,0 +1,2 @@ +--- +drupal_settings: [] diff --git a/tools/ansible/roles/drupal-settings/tasks/main.yml b/tools/ansible/roles/drupal-settings/tasks/main.yml new file mode 100644 index 0000000..71ebba5 --- /dev/null +++ b/tools/ansible/roles/drupal-settings/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: Ensure directory exists + file: + state: directory + path: '{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}' + with_subelements: + - '{{ drupal_settings }}' + - sites + no_log: true + +- name: Create settings files + template: + src: settings.php.j2 + dest: '{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}/{{ item.1.filename|default("settings.php") }}' + with_subelements: + - '{{ drupal_settings }}' + - sites + no_log: true diff --git a/tools/ansible/roles/drupal-settings/templates/settings.php.j2 b/tools/ansible/roles/drupal-settings/templates/settings.php.j2 new file mode 100644 index 0000000..22c4344 --- /dev/null +++ b/tools/ansible/roles/drupal-settings/templates/settings.php.j2 @@ -0,0 +1,42 @@ + '{{ values.driver|default('mysql') }}', + 'host' => '{{ values.host|default('localhost') }}', + 'database' => '{{ values.database }}', + 'username' => '{{ values.username }}', + 'password' => '{{ values.password }}', +); + +{% endfor %} +{% endfor %} + +{% if item.1.settings.base_url is defined %} +$base_url = '{{ item.1.settings.base_url }}'; +{% endif %} + +{% if item.1.settings.hash_salt is defined %} +$settings['hash_salt'] = '{{ item.1.settings.hash_salt }}'; +{% endif %} + +{% if item.1.settings.config_directories is defined %} +{% for name, value in item.1.settings.config_directories.items() %} +$config_directories['{{ name }}'] = '{{ value }}'; +{% endfor %} +{% endif %} + +{% if item.1.settings.trusted_hosts is defined %} +$settings['trusted_host_patterns'] = array( +{% for host in item.1.settings.trusted_hosts %} + '{{ host }}', +{% endfor %} +); +{% endif %} + +{% if item.1.settings.extra_parameters is defined %} + {{ item.1.settings.extra_parameters|indent(0) }} +{% endif %} diff --git a/tools/ansible/vars/deploy_vars.yml b/tools/ansible/vars/deploy_vars.yml new file mode 100644 index 0000000..1aebbf9 --- /dev/null +++ b/tools/ansible/vars/deploy_vars.yml @@ -0,0 +1,36 @@ +--- +ansistrano_allow_anonymous_stats: false +ansistrano_deploy_via: git +ansistrano_deploy_to: '{{ project_root_path }}' +ansistrano_git_identity_key_path: '{{ playbook_dir }}/id_deploy' +ansistrano_git_repo: git@github.com:opdavies/oliverdavies-uk.git +ansistrano_git_branch: master +ansistrano_keep_releases: 5 +ansistrano_shared_paths: + - '{{ project_web_dir }}/sites/default/files' + +# Hooks +ansistrano_after_symlink_shared_tasks_file: '{{ playbook_dir }}/deploy/after-symlink-shared.yml' +ansistrano_after_update_code_tasks_file: '{{ playbook_dir }}/deploy/after-update-code.yml' + +app_hash_salt: '{{ vault_app_hash_salt }}' + +drupal_settings: + - drupal_root: '{{ ansistrano_release_path.stdout }}/{{ project_web_dir }}' + sites: + - name: default + settings: + base_url: http://d8.oliverdavies.uk + hash_salt: '{{ app_hash_salt }}' + databases: + default: + default: + driver: mysql + host: localhost + database: oliverdavies_uk + username: '{{ app_mysql_user }}' + password: '{{ app_mysql_password }}' + config_directories: + sync: ../config/sync + trusted_hosts: + - '^d8\.oliverdavies\.uk$' diff --git a/tools/ansible/vars/deploy_vault.yml b/tools/ansible/vars/deploy_vault.yml new file mode 100644 index 0000000..f1b03a1 --- /dev/null +++ b/tools/ansible/vars/deploy_vault.yml @@ -0,0 +1,8 @@ +$ANSIBLE_VAULT;1.1;AES256 +39323539326634383533336262633230666630366666363935643462306538366338666436663235 +6231616134366432633965376535303635396134333661640a393961633431383165653439343436 +39303333346162386436393133303633636565323730643732396431623464623631396138343434 +3166346130643937340a343433393831396638643434653933343033353430326633376333396462 +66363461363435373462643037353661346435383336336137613837633933393931613833313037 +33393064383939666335323733346164643036366232656362326461373031303365386266663133 +343633356331333831633730396562616634 diff --git a/tools/ansible/vars/provision_vars.yml b/tools/ansible/vars/provision_vars.yml index 8b42af2..6bbd352 100644 --- a/tools/ansible/vars/provision_vars.yml +++ b/tools/ansible/vars/provision_vars.yml @@ -34,27 +34,91 @@ nginx_vhosts: root: '{{ project_root_path }}/{{ ansistrano_current_dir }}/{{ project_web_dir }}' index: index.php extra_parameters: | + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Very rarely should these ever be accessed outside of your lan + location ~* \.(txt|log)$ { + allow 192.168.0.0/16; + deny all; + } + + location ~ \..*/.*\.php$ { + return 403; + } + + location ~ ^/sites/.*/private/ { + return 403; + } + + # Block access to scripts in site files directory + location ~ ^/sites/[^/]+/files/.*\.php$ { + deny all; + } + + # Allow "Well-Known URIs" as per RFC 5785 + location ~* ^/.well-known/ { + allow all; + } + + # Block access to "hidden" files and directories whose names begin with a + # period. This includes directories used by version control systems such + # as Subversion or Git to store control files. + location ~ (^|/)\. { + return 403; + } + location / { - try_files $uri /index.php?$query_string; + try_files $uri /index.php?$query_string; # For Drupal >= 7 } location @rewrite { - rewrite ^/(.*)$ /index.php?q=$1; + rewrite ^/(.*)$ /index.php?q=$1; } # Don't allow direct access to PHP files in the vendor directory. location ~ /vendor/.*\.php$ { - deny all; - return 404; + deny all; + return 404; } location ~ '\.php$|^/update.php' { - try_files $uri =404; - fastcgi_split_path_info ^(.+?\.php)(|/.*)$; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param QUERY_STRING $query_string; - fastcgi_intercept_errors on; - fastcgi_pass localhost:9000; + try_files $uri =404; + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors on; + fastcgi_pass localhost:9000; + } + + # Fighting with Styles? This little gem is amazing. + # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6 + location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7 + try_files $uri @rewrite; + } + + # Handle private files through Drupal. + location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7 + try_files $uri /index.php?$query_string; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + try_files $uri @rewrite; + expires max; + log_not_found off; + } + + # Enforce clean URLs + if ($request_uri ~* "^(.*/)index\.php(.*)") { + return 307 $1$2; }