Skip to content

Commit 527ca44

Browse files
committed
BREAKING CHANGE: Migrate to alpine+nginx+php8
1 parent cdfef05 commit 527ca44

20 files changed

+355
-214
lines changed
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: Docker Publish
1+
name: CI
22

33
on:
44
push:
5-
tags: [ 'cp*.*.*-rev*' ]
5+
tags: [ '*.*.*-r*' ]
66
pull_request:
77
branches: [ master ]
88

@@ -62,11 +62,11 @@ jobs:
6262
with:
6363
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
6464
tags: |
65-
type=match,pattern=cp(\d+\.\d+),group=1
66-
type=match,pattern=cp\d+\.\d+
67-
type=match,pattern=cp([^-]+),group=1
68-
type=match,pattern=cp[^-]+
69-
type=match,pattern=cp.+
65+
type=match,pattern=(\d+\.\d+),group=1
66+
type=match,pattern=\d+\.\d+
67+
type=match,pattern=([^-]+),group=1
68+
type=match,pattern=[^-]+
69+
type=match,pattern=.+
7070
7171
# Build and push Docker image with Buildx (don't push on PR)
7272
# https://github.com/docker/build-push-action

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Changelog
2+
3+
## 1.7.1-rc1
4+
5+
* **BREAKING CHANGES**:
6+
7+
* Migrated to Alpine + Nginx + php-fpm. This allowed to reduce memory usage from ~300MB to ~40MB!
8+
* Changed versioning convention from `cpXXX-revYYY` to `CLASSICPRESS_VERSION-rRELEASE_NUMBER`
9+
* Migrated from `APACHE_RUN_USER_ID` and `APACHE_RUN_GROUP_ID` to shared between host and container group `press(gid=2048)`.
10+
* `wp-config.template.php` now contains `define('WP_AUTO_UPDATE_CORE', false);` which should stop CP from auto-updating (this is added only to new installations, so it may be that you must add it manually to your config)

DEVELOPMENT.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ The simplest way to build this image locally, it's to use Docker Compose.
44
To do so, you will need to create 2 files: `CP_DB_PASSWORD.secret` and `DB_ROOT_PASSWORD.secret` (files with `.secret` extension are git-ignored).
55

66
```sh
7-
export UID=$(id -u)
8-
export GID=$(id -g)
97
echo "my_secret_password" > secrets/CP_DB_PASSWORD.secret
108
echo "turbo_secret_password" > secrets/DB_ROOT_PASSWORD.secret
119
```
1210

11+
Remamber to have a group `press(gid=2048)` and your user is assigned to it.
12+
1313
Now, you simply run:
1414

1515
```sh
1616
docker compose -f docker-compose.dev.yaml up
1717
```
1818

19-
The container will be accessible via http://localhost:8000
19+
The container will be accessible via http://localhost:8080

README.md

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,25 @@ Quote from [www.classicpress.net](https://www.classicpress.net/):
44

55
> ClassicPress is a community-led open source content management system and a fork of WordPress that preserves the TinyMCE classic editor as the default option.
66
7+
## Changelog
8+
9+
[Open changelog](https://github.com/marverix/classicpress-docker/blob/master/CHANGELOG.md)
10+
711
## Tags
12+
813
### Convention
914

10-
Tagging convention is: `cpXXX-revYYY`
15+
Tagging convention is: `CLASIC_PRESS_VERSION-rRELEASE`
16+
17+
`CLASIC_PRESS_VERSION` is ClassicPress version, `RELEASE` is Docker Image release number. Eg. `1.7.1-r1`.
1118

12-
`XXX` is ClassicPress version. `YYY` is Docker Image revision number.
1319
## Basic Information
1420

15-
* The image is based on [`php:7.4-apache-bullseye`](https://hub.docker.com/_/php?tab=tags&name=7.4-apache-bullseye)
21+
* The image is based on Alpine 3.16 and php 8.0 (3.16 is a bit old, but it's last version having php8.0 which is required by ClassicPress 1.x)
22+
* Some code taken from [`TrafeX/docker-php-nginx:2.5.0`](https://github.com/TrafeX/docker-php-nginx) which I highly recommend! Unfortunatelly I coudln't use it (inherit) because Docker has no mechanism to "unexpose" port and remove health check
23+
* Thanks to Alpine + Nginx + php-fpm, the image is using only around ~40MB of RAM
1624
* Has enabled all required and recommended php extensions for WordPress
17-
* Has installed [`apache2-mod-security2`](https://github.com/SpiderLabs/ModSecurity) with [enabled OWASP CSR](https://owasp.org/www-project-modsecurity-core-rule-set/)
25+
* Basic security hardening done
1826
* Support for Docker Secrets via env variables with `_FILE` suffix
1927

2028
Note: Even with basic hardening done, it's highly recommended to not to expose a container directly to the outside world. Consider using a reverse proxy like [traefik](https://doc.traefik.io/traefik/) or [Nginx Proxy Manager](https://nginxproxymanager.com/).
@@ -28,35 +36,22 @@ https://hub.docker.com/r/marverix/classicpress/tags
2836
Good Docker practice is that one service/server == one docker container, this is why you will still need to run separate container
2937
with a database (MySQL/MariaDB).
3038

31-
### Privilages
32-
33-
Apache server inside is using two environment variables to set privilages:
34-
35-
* `APACHE_RUN_USER_ID`
36-
* `APACHE_RUN_GROUP_ID`
37-
38-
By default Docker runs everything with _root_ privilages. There are many great articles describing the impications of this solution, like:
39+
### Write Permission
3940

40-
* [File Permissions: the painful side of Docker](https://blog.gougousis.net/file-permissions-the-painful-side-of-docker/)
41-
* [Permission problems in bind mount in Docker Volume](https://techflare.blog/permission-problems-in-bind-mount-in-docker-volume/)
42-
* [File permissions on Docker volumes](https://ikriv.com/blog/?p=4698)
41+
This image deals with write access shared between host and the container by group `press` (and user) with ID _2048_. This is why your user running the container must be in this group.
4342

44-
This is why this image is running the Apache server as `apache:apache`.
45-
The trick here is, that Apache's user ID (`APACHE_RUN_USER_ID`) and group ID (`APACHE_RUN_GROUP_ID`) are set on fly, to user ID and group ID of the docker container runner. Only downside of this solution is that
46-
you need to set `UID` and `GID` env variables (for example in `~/.bashrc`) like this:
43+
If you are running Debian/Ubuntu-based run on your host machine:
4744

4845
```sh
49-
export UID=$(id -u)
50-
export GID=$(id -g)
46+
sudo groupadd -g 2048 press
47+
sudo usermod $(whoami) -aG press
5148
```
5249

53-
With this, you can use it in Docker Compose like this:
50+
Read more:
5451

55-
```yaml
56-
environment:
57-
- "APACHE_RUN_USER_ID=${UID}"
58-
- "APACHE_RUN_GROUP_ID=${GID}"
59-
```
52+
* [File Permissions: the painful side of Docker](https://blog.gougousis.net/file-permissions-the-painful-side-of-docker/)
53+
* [Permission problems in bind mount in Docker Volume](https://techflare.blog/permission-problems-in-bind-mount-in-docker-volume/)
54+
* [File permissions on Docker volumes](https://ikriv.com/blog/?p=4698)
6055

6156
### With Docker Compose
6257

@@ -105,8 +100,6 @@ docker-compose -f docker-compose.example.yaml --env-file=myblog-env-example up
105100
--expose 80:80 \
106101
--name myblog \
107102
--volume myblog_data:/data \
108-
--env APACHE_RUN_USER_ID=$UID \
109-
--env APACHE_RUN_GROUP_ID=$GID \
110103
--env CP_DB_NAME=myblog_db \
111104
--env CP_DB_USER=myblog_user \
112105
--env CP_DB_PASSWORD=my_secret_passowrd \

classicpress/000-default.conf

Lines changed: 0 additions & 15 deletions
This file was deleted.

classicpress/Dockerfile

Lines changed: 64 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
FROM php:8.1-apache-bullseye
1+
FROM alpine:3.16
2+
3+
LABEL Maintainer="Marek Sierociński"
4+
LABEL Description="ClassicPress image based on trafex/php-nginx"
25

36
# Version of ClassicPress
4-
ARG version=1.5.3
5-
ARG corerules_version=3.3.4
7+
ARG version=1.7.1
8+
# ARG corerules_version=3.3.4
69
ARG www_dir=/var/www/html
710

811
ARG WORKDIR_BUILD=/tmp/build
@@ -13,45 +16,59 @@ ENV WWW_DIR=${www_dir}
1316
ENV DATA_DIR=/data
1417
ENV WP_CONFIG=${DATA_DIR}/wp-config.php
1518
ENV WP_CONTENT=${DATA_DIR}/wp-content
16-
ENV BACKUP_WP_CONTENT="${WWW_DIR}/../wp-content-backup"
19+
ENV PRESS_USER=press
20+
ENV PRESS_HOME=/home/${PRESS_USER}
21+
ENV BACKUP_WP_CONTENT="${PRESS_HOME}/wp-content-backup"
1722

18-
ENV APACHE_RUN_USER=apache
19-
ENV APACHE_RUN_GROUP=www-data
23+
WORKDIR /var/www/html
2024

21-
ENV LC_ALL=en_US.UTF-8
22-
ENV LANG=en_US.UTF-8
23-
ENV LANGUAGE=en_US.UTF-8
25+
# Update packages index
26+
RUN apk update \
27+
# Install packages
28+
&& apk add --no-cache \
29+
bash \
30+
curl \
31+
nginx \
32+
php8 \
33+
php8-ctype \
34+
php8-curl \
35+
php8-dom \
36+
php8-fpm \
37+
php8-gd \
38+
php8-intl \
39+
php8-mbstring \
40+
php8-mysqli \
41+
php8-opcache \
42+
php8-openssl \
43+
php8-phar \
44+
php8-session \
45+
php8-xml \
46+
php8-xmlreader \
47+
php8-zlib \
48+
supervisor
2449

2550
COPY ./ ${WORKDIR_FILES}/
2651

27-
# Install packages
28-
RUN apt-get update \
29-
&& apt-get install -y \
30-
apt-transport-https lsb-release ca-certificates wget gnupg2 \
31-
&& wget -qO - https://modsecurity.digitalwave.hu/archive.key | apt-key add - \
32-
&& sh -c 'echo "deb http://modsecurity.digitalwave.hu/debian/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/dwmodsec.list' \
33-
&& sh -c 'echo "deb http://modsecurity.digitalwave.hu/debian/ $(lsb_release -sc)-backports main" >> /etc/apt/sources.list.d/dwmodsec.list' \
34-
&& apt-get update \
35-
&& apt-get install -y \
36-
libapache2-mod-security2 libmodsecurity3 \
37-
zlib1g-dev libpng16-16 libpng-dev libzip4 libzip-dev locales \
38-
# Ensure UTF-8
39-
&& sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
40-
&& locale-gen \
41-
# Change working directory
42-
&& mkdir -p ${WORKDIR_DOWNLOADS} \
43-
&& cd ${WORKDIR_DOWNLOADS} \
44-
# Download ClassicPress
52+
# Prepare users
53+
RUN adduser -D -u 2048 press \
54+
&& addgroup press press \
55+
&& addgroup press nginx
56+
57+
# Configure services
58+
RUN cp ${WORKDIR_FILES}/conf/php.ini /etc/php8/conf.d/custom.ini \
59+
&& cp ${WORKDIR_FILES}/conf/fpm-pool.conf /etc/php8/php-fpm.d/www.conf \
60+
&& cp ${WORKDIR_FILES}/conf/nginx.conf /etc/nginx/nginx.conf \
61+
&& cp ${WORKDIR_FILES}/conf/supervisord.conf /etc/supervisord.conf \
62+
&& rm -rf ${www_dir}/*
63+
64+
# Install ClassicPress
65+
RUN mkdir -p ${WORKDIR_DOWNLOADS} && cd ${WORKDIR_DOWNLOADS} \
4566
&& wget -qO classicpress.tar.gz https://github.com/ClassicPress/ClassicPress-release/archive/refs/tags/${version}.tar.gz \
46-
# Download corerules
47-
&& wget -qO corerules.tar.gz https://github.com/coreruleset/coreruleset/archive/refs/tags/v${corerules_version}.tar.gz \
48-
# Clean www_dir
49-
&& rm -rf ${www_dir}/* \
50-
# Unpack ClassicPress to www_dir
51-
&& tar -xf classicpress.tar.gz -C ${www_dir} --strip-components=1 \
67+
&& tar -xf classicpress.tar.gz -C ${www_dir} --strip-components=1
68+
69+
# Setup
5270
# Create /data
53-
&& mkdir ${DATA_DIR} \
54-
&& cd ${DATA_DIR} \
71+
RUN mkdir ${DATA_DIR} && cd ${DATA_DIR} \
5572
# Move wp-content to /data
5673
&& mv ${www_dir}/wp-content ${WP_CONTENT} \
5774
&& ln -s ${WP_CONTENT} ${www_dir}/wp-content \
@@ -60,53 +77,24 @@ RUN apt-get update \
6077
&& touch ${WP_CONFIG} \
6178
&& ln -s ${WP_CONFIG} ${www_dir}/wp-config.php \
6279
# Copy wp-config.template.php
63-
&& cp ${WORKDIR_FILES}/wp-config.template.php ${www_dir}/../wp-config.template.php \
64-
# Copy php.ini
65-
&& cp ${WORKDIR_FILES}/php.ini "${PHP_INI_DIR}/php.ini" \
66-
# Install missing php extensions
67-
&& EXTRA_CFLAGS="-I/usr/src/php" docker-php-ext-install \
68-
exif gd mysqli zip \
69-
# Enable apache mod-rewrite
70-
&& a2enmod rewrite \
71-
# Enable apache mod-headers
72-
&& a2enmod headers \
73-
# Enable mod-security2
74-
&& a2enmod security2 \
75-
&& cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf \
76-
&& sed -Ei "s/Sec([A-Z][a-z]+)Engine .+/Sec\1Engine On/g" /etc/modsecurity/modsecurity.conf \
77-
&& cp ${WORKDIR_FILES}/security2.conf /etc/apache2/mods-available/security2.conf \
78-
# Setup mod-security2
79-
&& cd /usr/share/modsecurity-crs \
80-
&& rm -rf ./* \
81-
&& tar -xf ${WORKDIR_DOWNLOADS}/corerules.tar.gz -C ./ --strip-components=1 \
82-
&& mv crs-setup.conf.example crs-setup.conf \
83-
&& mv ./rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ./rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf \
84-
# Copy default site conf
85-
&& cp ${WORKDIR_FILES}/000-default.conf /etc/apache2/sites-available/000-default.conf \
86-
# Copy security conf
87-
&& cp ${WORKDIR_FILES}/security.conf /etc/apache2/conf-available/security.conf \
88-
# Copy htaccess
89-
&& cp ${WORKDIR_FILES}/htaccess ${DATA_DIR}/.htaccess \
90-
&& ln -s ${DATA_DIR}/.htaccess ${www_dir}/.htaccess \
80+
&& cp ${WORKDIR_FILES}/wp-config.template.php ${PRESS_HOME}/wp-config.template.php \
9181
# Copy startup script
92-
&& cp ${WORKDIR_FILES}/classicpress.sh /opt/classicpress.sh \
93-
# Create user
94-
&& useradd -rMUG daemon,www-data apache \
82+
&& cp ${WORKDIR_FILES}/classicpress.sh ${PRESS_HOME}/classicpress.sh \
83+
# Privilages
84+
&& cp ${WORKDIR_FILES}/ch/chmod /bin/schmod \
85+
&& cp ${WORKDIR_FILES}/ch/chgrp /bin/schgrp \
86+
&& chmod +s /bin/schmod /bin/schgrp \
87+
&& chmod +x ${PRESS_HOME}/classicpress.sh \
88+
&& chown -R ${PRESS_USER}:${PRESS_USER} /data ${www_dir} \
89+
&& chmod -R 777 /run /var/log \
9590
# Clean
96-
&& apt-get purge -y --auto-remove \
97-
wget zlib1g-dev \
98-
&& apt-get autoclean \
99-
&& rm -r /var/lib/apt/lists/* \
10091
&& rm -rf /tmp/*
10192

102-
# Set back www_dir as workdir
103-
WORKDIR ${www_dir}
93+
USER press
10494

105-
# Expose port 80
10695
EXPOSE 80
10796

108-
# It's important to NOT change user with USER. We want to have root permissions
109-
# in startup script, so we can dynamcally change UID/GID and ownership.
97+
HEALTHCHECK --timeout=10s CMD curl --silent --fail http://127.0.0.1/fpm-ping
11098

11199
# Set startup command
112-
CMD [ "/opt/classicpress.sh" ]
100+
CMD [ "/home/press/classicpress.sh" ]

classicpress/ch/chgrp

339 KB
Binary file not shown.

classicpress/ch/chmod

331 KB
Binary file not shown.

classicpress/ch/chown

351 KB
Binary file not shown.

classicpress/classicpress.sh

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env bash
22

3+
schgrp -R press /data
4+
35
# ClassicPress Startup Script
46
TMP_WP_CONFIG=/tmp/wp-config.php
57

@@ -33,16 +35,15 @@ function store_env() {
3335
sed -i "s/$1/${!1}/g" "${TMP_WP_CONFIG}"
3436
}
3537

36-
3738
# Checking wp-config.php
3839
if [ ! -f "${WP_CONFIG}" ]; then
3940
echo "Notice: File ${WP_CONFIG} not found - touching"
4041
touch "${WP_CONFIG}"
4142
fi
4243

4344
if [ ! -w "${WP_CONFIG}" ]; then
44-
echo "Error: File ${WP_CONFIG} found, but not writable. Have you mounted the /data as a volume?"
45-
exit 1
45+
echo "Notice: File ${WP_CONFIG} found, but not writable. Fixing"
46+
schmod g+w "${WP_CONFIG}"
4647
fi
4748

4849
if [ -s "${WP_CONFIG}" ]; then
@@ -71,7 +72,7 @@ else
7172
random_env "CP_NONCE_SALT"
7273

7374
echo "Preparing wp-config.php ..."
74-
cp "${WWW_DIR}/../wp-config.template.php" "${TMP_WP_CONFIG}"
75+
cp "${PRESS_HOME}/wp-config.template.php" "${TMP_WP_CONFIG}"
7576
store_env "CP_DB_NAME"
7677
store_env "CP_DB_USER"
7778
store_env "CP_DB_PASSWORD"
@@ -99,14 +100,7 @@ else
99100
cp -r "${BACKUP_WP_CONTENT}" "${WP_CONTENT}"
100101
fi
101102

103+
schmod -R g+w "${WP_CONTENT}"
102104

103-
# Chanigin ownership
104-
echo "Changing ownership..."
105-
sed -Ei "s/$APACHE_RUN_USER:x:[0-9]+:[0-9]+/$APACHE_RUN_USER:x:$APACHE_RUN_USER_ID:$APACHE_RUN_GROUP_ID/g" /etc/passwd
106-
chown -R ${APACHE_RUN_USER}:${APACHE_RUN_GROUP} ${WWW_DIR} ${DATA_DIR}
107-
108-
109-
# Starting apache
110-
echo "Starting Apache in foreground ..."
111-
# https://github.com/docker-library/php/blob/master/7.4/bullseye/apache/apache2-foreground
112-
apache2-foreground
105+
# Starting
106+
supervisord

0 commit comments

Comments
 (0)