diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2a78f433d9b7d86a16a545f32a6d5b84752611ce..a30af8098d3f865f849dc2eeecd952fc812d4d00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,45 +16,69 @@ before_script: .build: &build stage: build - only: &build-only + script: + - TARGET=${CI_JOB_NAME##*:} + - BUILD_TAG=${CI_REGISTRY_IMAGE}/${TARGET}/build:${CI_PIPELINE_ID} + - docker build . + --pull=true + --tag=${BUILD_TAG} + --target=${TARGET} + ${NGINX_VERSION:+--build-arg=nginx_version=$NGINX_VERSION} + ${PHP_VERSION:+--build-arg=php_version=$PHP_VERSION} + ${WORDPRESS_VERSION:+--build-arg=wp_version=$WORDPRESS_VERSION} + - docker push ${BUILD_TAG} + +.changes: &only-changes + only: &change-files changes: + - .gitlab-ci.yml - Dockerfile - data/* - plugins/* - scripts/* - script: - - docker build . - --pull=true - --tag=${CI_REGISTRY_IMAGE}/${CI_JOB_NAME#build-}/build:${CI_PIPELINE_ID} - ${TARGET+--target=$TARGET} - after_script: - - docker push - ${CI_REGISTRY_IMAGE}/${CI_JOB_NAME#build-}/build:${CI_PIPELINE_ID} - -build-fastcgi: - <<: *build -build-nginx: - <<: *build - variables: - TARGET: nginx + +.merge-requests: &only-merge-requests + only: + << : *change-files + refs: + - merge_requests + +build-master:fastcgi: + << : [ *build ] + only: [ master, schedules ] +build-master:nginx: + << : [ *build ] + only: [ master, schedules ] + +build-mr:fastcgi: + << : [ *build, *only-merge-requests ] +build-mr:nginx: + << : [ *build, *only-merge-requests ] + +build:fastcgi: + << : [ *build, *only-changes ] + except: [ merge_requests, master, schedules ] +build:nginx: + << : [ *build, *only-changes ] + except: [ merge_requests, master, schedules ] .push-tags: &push-tags stage: deploy only: - <<: *build-only - refs: [master, develop] + << : *change-files + refs: [ master, develop, schedules ] script: | - BUILD_REPO=${CI_REGISTRY_IMAGE}/${CI_JOB_NAME#push-}/build:${CI_PIPELINE_ID} - DEPLOY_REPO=${CI_REGISTRY_IMAGE}/${CI_JOB_NAME#push-} + BUILD_REPO=${CI_REGISTRY_IMAGE}/${CI_JOB_NAME##*:}/build:${CI_PIPELINE_ID} + DEPLOY_REPO=${CI_REGISTRY_IMAGE}/${CI_JOB_NAME##*:} VERSION=`eval "docker run --rm ${BUILD_REPO} ${GET_VERSION}"` . scripts/deploy.sh -push-fastcgi: +push:fastcgi: <<: *push-tags variables: GET_VERSION: wp core version -push-nginx: +push:nginx: <<: *push-tags variables: GET_VERSION: nginx -V 2>&1 | sed -n '/nginx version:/s/.*nginx\///p' diff --git a/Dockerfile b/Dockerfile index 6ac80d3b9ae9d48dd8d2e35989a0650c678b4906..6c38af46ca8ae4e853189838b29fd43b53a25ee8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,15 +4,19 @@ ARG nginx_version=latest FROM nginx:${nginx_version} as nginx LABEL uk.org.kodo.maintainer = "Dom Sekotill " COPY data/nginx.conf /etc/nginx/conf.d/default.conf +COPY data/fastcgi.nginx.conf /etc/nginx/fastcgi.conf +COPY data/cache-bust.nginx.conf /etc/nginx/cache-bust.conf +COPY data/5*.html /app/html/ -FROM php:7.3-fpm-alpine as deps +ARG php_version= +FROM php:${php_version:+$php_version-}fpm-alpine as deps RUN --mount=type=bind,source=scripts/install-deps.sh,target=/stage /stage FROM deps as compile RUN --mount=type=bind,source=scripts/compile.sh,target=/stage /stage -FROM deps +FROM deps as fastcgi LABEL uk.org.kodo.maintainer "Dom Sekotill " @@ -20,13 +24,14 @@ ARG wp_version=latest WORKDIR /app ENV WORDPRESS_ROOT=/app -COPY scripts/wp.sh /usr/local/bin/wp COPY --from=compile /usr/local/etc/php /usr/local/etc/php COPY --from=compile /usr/local/lib/php /usr/local/lib/php +COPY scripts/wp.sh /usr/local/bin/wp +COPY data/composer.json /app/composer.json RUN --mount=type=bind,source=scripts/install-wp.sh,target=/stage \ /stage ${wp_version} -COPY plugins/probe.php wp-content/mu-plugins/ +COPY plugins/* wp-content/mu-plugins/ COPY data/fpm.conf /usr/local/etc/php-fpm.d/image.conf COPY data/opcache.ini /usr/local/etc/php/conf.d/opcache-recommended.ini COPY data/wp-config.php /usr/share/wordpress/wp-config.php diff --git a/data/502.html b/data/502.html new file mode 100644 index 0000000000000000000000000000000000000000..3e722061a0597d604be80189cd5b651b43010207 --- /dev/null +++ b/data/502.html @@ -0,0 +1,107 @@ + + + + + + + + + + Site Unavailable + + + + + + +
+
+ +
+
+

Site Unavailable

+

This site is currently unavailable

+ +
+
+ +
+
+ + + diff --git a/data/cache-bust.nginx.conf b/data/cache-bust.nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..fa5c8ed0e95b4eefc612d99cedec7feb77528b39 --- /dev/null +++ b/data/cache-bust.nginx.conf @@ -0,0 +1 @@ +add_header X-Clacks-Overhead "GNU Terry Pratchett"; diff --git a/data/composer.json b/data/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..c028f78b051f0f31074739ee76689ab6e3514fbd --- /dev/null +++ b/data/composer.json @@ -0,0 +1,21 @@ +{ + "type": "project", + "license": "MPL-2.0", + "repositories": [ + { + "type": "gitlab", + "url": "https://code.kodo.org.uk/singing-chimes.co.uk/s3-uploads" + } + ], + "require": { + "humanmade/s3-uploads": "dev-develop" + }, + "config": { + "gitlab-domains": [ "code.kodo.org.uk" ] + }, + "extra": { + "installer-paths": { + "wp-content/mu-plugins/{$name}/": [ "type:wordpress-plugin" ] + } + } +} diff --git a/data/fastcgi.nginx.conf b/data/fastcgi.nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..321efe261dbb98bcc6ff0c943739c2c04a504e7e --- /dev/null +++ b/data/fastcgi.nginx.conf @@ -0,0 +1,26 @@ +fastcgi_pass upstream:9000; + +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_FILENAME /app$fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT /app; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param REQUEST_SCHEME $http_x_forwarded_proto; +fastcgi_param HTTPS $forwarded_https; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_NAME $server_name; +fastcgi_param SERVER_ADDR $http_x_forwarded_host; +fastcgi_param SERVER_PORT $http_x_forwarded_port; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param REDIRECT_STATUS 200; diff --git a/data/fpm.conf b/data/fpm.conf index 20bd27fc785cfb90c09360d2d5f3ef88a32d9eb7..407f3aed220fdb0a150f33e66811ad2031718ecd 100644 --- a/data/fpm.conf +++ b/data/fpm.conf @@ -1 +1,4 @@ access.format = "[%{%Y-%m-%dT%H:%M:%S%z}t] %{REMOTE_ADDR}e %m %{REQUEST_URI}e %s time=%{mili}d ms;" + +ping.path = "/.probe" +ping.response = "OK" diff --git a/data/nginx.conf b/data/nginx.conf index 8f5fb750aeaa53137c31a6574d458a33085212ff..ebf21c51c875fd858cffb6372655db73af37d530 100644 --- a/data/nginx.conf +++ b/data/nginx.conf @@ -5,9 +5,12 @@ map $http_x_forwarded_proto $forwarded_https { log_format clear '[$time_iso8601] $remote_addr ' '$request_method $request_uri $status ' - ' sent=$body_bytes_sent bytes;' - ' referrer=$http_referer;' - ' user-agent=$http_user_agent'; + ' sent=$body_bytes_sent bytes;' + ' referrer=$http_referer;' + ' user-agent=$http_user_agent'; + +fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=ERR:1m inactive=1d; +fastcgi_cache_key "$scheme$request_method$host$request_uri"; server { listen 80; @@ -25,16 +28,31 @@ server { # Add Cache-Control headers for static files, removed in *.php location add_header Cache-Control "public, max-age=7776000, stale-while-revalidate=86400, stale-if-error=604800"; - location ~ \.php$ { - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /app$fastcgi_script_name; - fastcgi_param DOCUMENT_ROOT /app; - fastcgi_param REQUEST_SCHEME $http_x_forwarded_proto; - fastcgi_param HTTPS $forwarded_https; - fastcgi_param SERVER_ADDR $http_x_forwarded_host; - fastcgi_param SERVER_PORT $http_x_forwarded_port; - add_header X-Clacks-Overhead "GNU Terry Pratchett"; - fastcgi_pass upstream:9000; + error_page 404 /errors/404; + error_page 502 /errors/502.html; + + location /errors { + alias /app/html; + internal; + + location /errors/404 { + include fastcgi.conf; + fastcgi_param SCRIPT_FILENAME /app/index.php; + fastcgi_cache ERR; + fastcgi_cache_valid 404 1d; + } + } + + location @index { + include fastcgi.conf; + include cache-bust.conf; + fastcgi_param SCRIPT_FILENAME /app/index.php; + } + + location = /.probe { + include fastcgi.conf; + fastcgi_param SCRIPT_NAME /.probe; + access_log off; } # Don't return 200 for a missing favicon @@ -49,25 +67,22 @@ server { try_files $uri =404; } - # block the XMLRPC script - location = /xmlrpc.php { - return 404; - } - - # block external cron triggers - location = /wp-cron.php { - return 404; - } - # allow the new JSON REST API location /wp-json/ { - rewrite ^ /index.php$is_args$args last; + include fastcgi.conf; + include cache-bust.conf; + fastcgi_param SCRIPT_FILENAME /app/index.php; } # use /index.php as a front controller if the base of the URI path does # not exist location / { - try_files $uri /index.php$is_args$args; + try_files $uri @index; + } + + location = /wp-login.php { + include fastcgi.conf; + include cache-bust.conf; } # wp-admin uses lots of directly accessed PHP scripts, unfortunately @@ -76,28 +91,19 @@ server { } location /wp-admin/ { try_files $uri $uri/index.php; - } - # serve only static files from wp-includes - location ^~ /wp-includes/ { - # don't serve PHP source code from wp-includes - location ~ \.php { - return 404; + location ~ \.php$ { + include fastcgi.conf; + include cache-bust.conf; } } - # serve only static files from wp-content - location ^~ /wp-content/ { - # don't serve PHP source code from plugins, etc - location ~ \.php { - return 404; - } - } - - # limit the usefulness of malicious HTML/JS hosted in /media/ by serving - # only media & common data files with their correct mime-type + # Limit the usefulness of malicious HTML/JS hosted in /media/ by serving + # only media & common data files with their correct mime-type. + # Don't allow missing paths to be delegated to the PHP controller. location /media/ { root /app; + try_files $uri =404; default_type application/octet-stream; types { # images # diff --git a/data/wp-config.php b/data/wp-config.php index 78063c294ec38bbcb32a6a03434f031e5a70a670..a9cdd92cbc9519416c4e95d54ad7956c554945f5 100644 --- a/data/wp-config.php +++ b/data/wp-config.php @@ -4,13 +4,6 @@ * Additional configurations required by the docker image ****/ -/** - * Plugins, themes and language packs cannot be configured through the admin - * interface; modify the configuration in /etc/wordpress/ according to the - * documentation for PLUGINS[_LIST], THEMES[_LIST] and LANGUAGES[_LIST] - **/ -define('DISALLOW_FILE_MODS', true); - /** * Disable running wp-cron.php on every page load. A sidecar process * examines the status of cron jobs and handles scheduling and running them. @@ -22,3 +15,20 @@ define('DISABLE_WP_CRON', true); * installation. **/ define('UPLOADS', 'media'); + +/** + * Stop the site-health tool from complaining about unwritable filesystems. + * Background upgrades are performed by a user with write privileges via the + * wp-cli tool. + **/ +if ( !defined( 'WP_CLI' ) ): +define('FTP_USER', 'nemo'); +define('FTP_PASS', '****'); +endif; + +/** + * Run the Composer autoloader, if available + * Assume the CWD is always /app and vendor is always in it. + **/ +if ( is_file(ABSPATH . 'vendor/autoload.php') ) + require_once ABSPATH . 'vendor/autoload.php'; diff --git a/plugins/media-url.php b/plugins/media-url.php new file mode 100644 index 0000000000000000000000000000000000000000..955cc97efdfeb5932785cb6b042e549ef08c74b3 --- /dev/null +++ b/plugins/media-url.php @@ -0,0 +1,40 @@ +/usr/local/lib/wp-cli.phar @@ -21,3 +27,7 @@ mkdir -p wp-content/mu-plugins # Install non-optional plugins curl ${WP_PASSWORD_HASH}/wp-php-password-hash.php \ >wp-content/mu-plugins/password-hash.php + +# Install composer managed dependencies +export COMPOSER_ALLOW_SUPERUSER=1 +composer install --prefer-dist