--- /dev/null
+# Wordpress-Installation
+
+## Installation
+```
+ansible-playbook playbooks/wordpress_create -e domain=www.example.de -e wp_version=6.8.2
+ansible-playbook playbooks/webapp_create -e webapp_name=www.example.de \
+ -e db_name=wpexample -e db_user=example -e db_password=TopSecret
+ansible-playbook playbooks/ssl_mult_cert.yaml domains=example.de,www.example.de
+# ODER (wenn kein zusätzliches www.*)
+ansible-playbook playbooks/ssl_create_certificate.yaml domain=example.de
+```
+- Kontrolle von /etc/nginx/sites-available/www.example.de
+- Wenn Domäne erreichbar:
+```
+ansible-playbook playbooks/lets_multi_cert.yaml domains=example.de,www.example.de
+# ODER (wenn kein zusätzliches www.*)
+ansible-playbook playbooks/lets_create.yaml domain=example.de
--- /dev/null
+---
+# Creates a wordpress website for a domain
+# needed facts (variables) from commandline: (e.g. -e domain=example.com
+# domain: the site domain name
+# wp_version: the version of the wordpress to install
+# wp_shortname: the short name of the wordpress site. Used for log file names.
+# optional:
+# wp_php_version: the php version to use. Default: 8.3
+- hosts: all
+ vars_files:
+ - ../vars/common.yaml
+ - ../vars/ssl-certificate.yaml
+ tasks:
+ - name: Check pre-requisites
+ fail:
+ msg: "The variable 'domain', 'wp_version' and 'wp_shortname' must be defined: use -e domain=mydomain.com"
+ when: domain is not defined or domain == "" or wp_version is not defined or wp_version == "" or wp_shortname is not defined or wp_shortname == ""
+ - name: Is the zip file available?
+ ansible.builtin.stat:
+ path: "{{ remote_www_directory }}/packages/wordpress-{{ wp_version }}.zip"
+ register: wp_zip_file
+ - name: Fail if the zip file is not available
+ ansible.builtin.fail:
+ msg: "The wordpress zip file {{ remote_www_directory }}/packages/wordpress-{{ wp_version }}.zip is not available. Please download it first."
+ when: not wp_zip_file.stat
+ - name: Check whether the target directory already exists
+ ansible.builtin.stat:
+ path: "{{ remote_www_directory }}/{{ domain }}"
+ register: target_directory
+ - name: "Unarchive wordpress. Note: there is a wordpress subdirectory in the zip file"
+ ansible.builtin.unarchive:
+ src: "{{ remote_www_directory }}/packages/wordpress-{{ wp_version }}.zip"
+ dest: "{{ remote_www_directory }}"
+ remote_src: true
+ owner: www-data
+ group: www-data
+ mode: '0775'
+ when: not target_directory.stat.exists
+ - name: Rename the directory to the domain name
+ ansible.builtin.command:
+ cmd: mv {{ remote_www_directory }}/wordpress {{ remote_www_directory }}/{{ domain }}
+ when: not target_directory.stat.exists
+ - name: Create nginx site configuration
+ ansible.builtin.template:
+ src: ../templates.fix/nginx/wordpress.j2
+ dest: /etc/nginx/sites-available/{{ domain }}
+ owner: root
+ group: root
+ mode: '0644'
+ vars:
+ domain: "{{ domain }}"
+ shortname: "{{ wp_shortname }}"
+ php_version: "{{ wp_php_version | default('8.3') }}"
+
+ - name: Create symlink to sites-enabled
+ ansible.builtin.file:
+ src: "../sites-available/{{ domain }}"
+ dest: "/etc/nginx/sites-enabled/{{ domain }}"
+ state: link
+ force: true
+ follow: false
+
--- /dev/null
+---
+- name: Download WordPress page and extract version
+ hosts: all
+ gather_facts: false
+
+ tasks:
+ - name: Define the package directory
+ ansible.builtin.set_fact:
+ package_dir: "/srv/www/packages"
+ url_zip: "https://de.wordpress.org/latest-de_DE.zip"
+ url_page: "https://de.wordpress.org/download"
+ - name: Download the wordpress.org download page
+ ansible.builtin.uri:
+ url: "{{ url_page }}"
+ return_content: true
+ register: wordpress_page_content
+ delegate_to: localhost
+
+ - name: Extract the version number
+ ansible.builtin.set_fact:
+ wp_version: "{{ wordpress_page_content.content | regex_search('WordPress (\\d+\\.\\d+\\.\\d+) herunterladen') | regex_replace('[^.0-9]', '') }}"
+ - name: Display the extracted version
+ ansible.builtin.debug:
+ msg: "The latest WordPress version is {{ wp_version }}"
+ - name: Test wether the zip file is available
+ ansible.builtin.stat:
+ path: "{{ package_dir }}/wordpress-{{ wp_version }}.zip"
+ register: wp_zip_stat
+ - name: log the result of the stat
+ ansible.builtin.debug:
+ msg: "The file already exists: {{ package_dir }}/wordpress-{{ wp_version }}.zip"
+ when : wp_zip_stat.stat.exists
+ - name: Download the WordPress zip file if not already present
+ ansible.builtin.get_url:
+ url: "{{ url_zip }}"
+ dest: "{{ package_dir }}/wordpress-{{ wp_version }}.zip"
+ when: not wp_zip_stat.stat.exists
+
--- /dev/null
+server {
+ listen 80;
+ server_name {{ domain }} www.{{ domain }};
+ include snippets/letsencrypt.conf;
+ location / {
+ return 301 https://{{ domain }}$request_uri; # enforce https
+ }
+}
+server {
+ listen 443 ssl;
+ http2 on;
+ ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
+# ssl_certificate /etc/ssl/certs/{{ domain }}.pem;
+# ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
+
+ server_name {{ domain }};
+ location / {
+ return 301 https://www.{{ domain }}$request_uri;
+ }
+}
+server {
+ listen 443 ssl;
+ http2 on;
+ ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
+# ssl_certificate /etc/ssl/certs/{{ domain }}.pem;
+# ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
+
+ server_name www.{{ domain }};
+ root /srv/www/{{ domain }}/;
+
+
+ access_log /var/log/nginx/a_{{ shortname }}.log;
+ error_log /var/log/nginx/e_{{ shortname }}.log;
+
+ index index.php;
+ client_max_body_size 1G;
+ autoindex on;
+
+ # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
+ # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
+
+ location ^~ /.well-known {
+ allow all;
+ if ($request_method = 'OPTIONS') {
+ add_header 'Access-Control-Allow-Origin' '*';
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
+ add_header 'Access-Control-Max-Age' 1728000;
+ add_header 'Content-Type' 'text/plain; charset=utf-8';
+ add_header 'Content-Length' 0;
+ return 204;
+ }
+ if ($request_method = 'POST') {
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
+ add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
+ }
+ if ($request_method = 'GET') {
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
+ add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
+ }
+
+ }
+ #location ~ /\. {
+ # deny all;
+ #}
+
+
+ # Deny access to any files with a .php extension in the uploads directory
+ # Works in sub-directory installs and also in multisite network
+ # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
+ location ~* /(?:uploads|files)/.*\.php$ {
+ deny all;
+ }
+
+ location = /favicon.ico {
+ log_not_found off;
+ access_log off;
+ }
+
+ location = /robots.txt {
+ allow all;
+ log_not_found off;
+ access_log off;
+ }
+
+ location / {
+ # This is cool because no php is touched for static content.
+ # include the "?$args" part so non-default permalinks doesn't break when using query string
+ try_files $uri $uri/ /index.php?$args;
+ }
+
+ location ~ \.php$ {
+ #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
+ include fastcgi.conf;
+ fastcgi_intercept_errors on;
+ fastcgi_pass unix:/var/run/php/php{{ php_version }}-fpm.sock;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param HTTPS $fastcgi_https;
+ fastcgi_param HTTP_AUTHORIZATION $http_authorization;
+
+ fastcgi_buffers 16 16k;
+ fastcgi_buffer_size 32k;
+ }
+
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
+ expires max;
+ log_not_found off;
+ }
+}