Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions .github/actions/docker/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ inputs:
registry_password:
description: "Password for image registry"
required: true
target:
description: "Dockerfile build target (defaults to the last stage)"
required: false
default: ""
tag_suffix:
description: "Suffix appended to every image tag (e.g. -rootless)"
required: false
default: ""

tag_latest:
description: "Whether to also publish the latest tag"
required: false
default: "true"

runs:
using: "composite"
Expand All @@ -31,8 +44,13 @@ runs:
version=$(grep -E '^\s*(ENV\s+)?VERSION=' '${{ inputs.dockerfile }}' | tail -1 | sed -E 's/^.*VERSION=([^ \\]+).*/\1/')
vcs_ref=$(git rev-parse --short HEAD)
image_name=$(cd $(dirname ${{ inputs.dockerfile }}) && echo "${PWD##*/}")
tags=bdossantos/${image_name}:latest,bdossantos/${image_name}:${version},bdossantos/${image_name}:${version}-${vcs_ref},bdossantos/${image_name}:${version%.*}
cache_image=bdossantos/${image_name}:buildcache
suffix='${{ inputs.tag_suffix }}'
tags=""
if [ '${{ inputs.tag_latest }}' = 'true' ]; then
tags=bdossantos/${image_name}:latest${suffix},
fi
tags=${tags}bdossantos/${image_name}:${version}${suffix},bdossantos/${image_name}:${version}-${vcs_ref}${suffix},bdossantos/${image_name}:${version%.*}${suffix}
cache_image=bdossantos/${image_name}:buildcache${suffix}
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
echo "cache_image=${cache_image}" >> $GITHUB_OUTPUT
echo "path=$(dirname ${{ inputs.dockerfile }})" >> $GITHUB_OUTPUT
Expand All @@ -49,6 +67,7 @@ runs:
cache-to: type=registry,ref=${{ steps.context.outputs.cache_image }},mode=max
context: ${{ steps.context.outputs.path }}
file: ${{ inputs.dockerfile }}
target: ${{ inputs.target }}
push: ${{ github.event_name != 'pull_request' }}
sbom: true
tags: ${{ steps.context.outputs.tags }}
22 changes: 22 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ jobs:
contents: read
outputs:
dockerfiles: ${{ steps.discover.outputs.dockerfiles }}
rootless: ${{ steps.discover.outputs.rootless }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/[email protected]
- name: Discover Dockerfiles
id: discover
run: |
dockerfiles=$(find dockerfiles -type f -name 'Dockerfile*' | sort | jq -R -s -c 'split("\n")[:-1]')
rootless=$(find dockerfiles/php-lol -type f -name 'Dockerfile*' | sort | jq -R -s -c 'split("\n")[:-1]')
echo "dockerfiles=${dockerfiles}" >> $GITHUB_OUTPUT
echo "rootless=${rootless}" >> $GITHUB_OUTPUT

build:
runs-on: ubuntu-latest
Expand All @@ -39,3 +42,22 @@ jobs:
with:
dockerfile: ${{ matrix.dockerfile }}
registry_password: ${{ secrets.DOCKERHUB_TOKEN }}

build-rootless:
runs-on: ubuntu-latest
permissions:
contents: read
needs: discover
strategy:
matrix:
dockerfile: ${{ fromJSON(needs.discover.outputs.rootless) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/[email protected]
- name: Build and push rootless variant
uses: ./.github/actions/docker
with:
dockerfile: ${{ matrix.dockerfile }}
registry_password: ${{ secrets.DOCKERHUB_TOKEN }}
target: rootless

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a CI step that builds each rootless variant (8.3/8.4/8.5), checks the user is 65534:65534, runs it read-only and waits for nginx on 8080 (e5bcb70). I couldn't run Docker here, so this step hasn't run yet.

tag_suffix: -rootless

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latest-rootless is now published only from PHP 8.5, via a new tag_latest action input. Version-specific rootless tags are still published for all versions (e5bcb70).

tag_latest: ${{ matrix.dockerfile == 'dockerfiles/php-lol/Dockerfile.8.5' }}
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,22 @@ jobs:
- name: Lint and test
run: |
make test
- name: Test rootless php-lol variants
run: |
for version in 8.3 8.4 8.5; do
image="bdossantos/php-lol:rootless-${version}"
docker build --target rootless -f "dockerfiles/php-lol/Dockerfile.${version}" -t "${image}" dockerfiles/php-lol
test "$(docker image inspect --format '{{.Config.User}}' "${image}")" = "65534:65534"
docker run -d --name "php-lol-rootless-${version}" --read-only --tmpfs /tmp --tmpfs /run --tmpfs /var/log --tmpfs /var/lib/php/session --tmpfs /var/cache/nginx "${image}"
ok=false
for _ in $(seq 1 30); do
if docker exec "php-lol-rootless-${version}" curl -sf http://127.0.0.1:8080/nginx_status >/dev/null; then
ok=true
break
fi
sleep 1
done
docker logs "php-lol-rootless-${version}"
docker rm -f "php-lol-rootless-${version}"
test "${ok}" = "true"
done
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ multi-stage builds, pinned dependencies, and non-root users.
| [dnscrypt-proxy](dockerfiles/dnscrypt-proxy) | [DNSCrypt](https://dnscrypt.info/) proxy with encrypted DNS support |
| [memcached](dockerfiles/memcached) | [Memcached](https://memcached.org/) with built-in proxy support and embedded [routelib](https://github.com/memcached/memcached-proxylibs) |
| [paperless-ngx](dockerfiles/paperless-ngx) | [Paperless-ngx](https://docs.paperless-ngx.com/) document management system |
| [php-lol](dockerfiles/php-lol) | PHP-FPM with Nginx and common extensions (8.3, 8.4, 8.5) |
| [php-lol](dockerfiles/php-lol) | PHP-FPM with Nginx and common extensions (8.3, 8.4, 8.5); `-rootless` tags run as 65534 on port 8080 |
| [pingdom-exporter](dockerfiles/pingdom-exporter) | Prometheus exporter for Pingdom metrics |
| [pint](dockerfiles/pint) | [Pint](https://cloudflare.github.io/pint/) – Prometheus rule linter by Cloudflare |
| [python-github-backup](dockerfiles/python-github-backup) | [python-github-backup](https://github.com/josegonzalez/python-github-backup) – GitHub repository backup tool |
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@ services:
build:
context: dockerfiles/php-lol
dockerfile: Dockerfile.8.3
target: run
image: bdossantos/php-lol
container_name: php-lol83
php-lol84:
<<: *default
build:
context: dockerfiles/php-lol
dockerfile: Dockerfile.8.4
target: run
image: bdossantos/php-lol
container_name: php-lol84
php-lol85:
<<: *default
build:
context: dockerfiles/php-lol
dockerfile: Dockerfile.8.5
target: run
image: bdossantos/php-lol
container_name: php-lol85
pingdom-exporter:
Expand Down
36 changes: 34 additions & 2 deletions dockerfiles/php-lol/Dockerfile.8.3
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,11 @@ RUN chmod 0444 \
/usr/local/etc/php/php.ini

#
# Run
# Runtime
#
# Shared by the "rootless" and "run" stages, ports are declared per stage.
#
FROM foundation AS run
FROM foundation AS runtime

ARG BUILD_DATE
ARG VCS_REF
Expand All @@ -250,6 +252,36 @@ LABEL org.label-schema.build-date="$BUILD_DATE" \

WORKDIR /usr/src/app

#
# Rootless
#
# Same as "run", but the whole process tree (supervisord, php-fpm, nginx) runs
# as 65534:65534 and nginx listens on unprivileged port 8080.
#
FROM runtime AS rootless

RUN set -eux \
&& sed -i -e '/^user /d' -e 's/listen \[::\]:80 /listen [::]:8080 /' -e 's/listen 80 /listen 8080 /' /etc/nginx/nginx.conf \
&& sed -i -e '/^chown = /d' -e '/^user = /d' /etc/supervisor/supervisord.conf \
&& sed -i -e '/^user = /d' /etc/supervisor/conf.d/app.conf \
&& chmod 0444 /etc/nginx/nginx.conf /etc/supervisor/supervisord.conf /etc/supervisor/conf.d/app.conf \
&& mkdir -p /var/cache/nginx /var/log/nginx /var/lib/php/session \
&& chown -R 65534:65534 /var/cache/nginx /var/log/nginx /var/lib/php/session

EXPOSE 8080/tcp

USER 65534:65534

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

#
# Run
#
# Last stage: plain "docker build" keeps producing the port 80 image.
#
FROM runtime AS run

EXPOSE 80/tcp 443/tcp

ENTRYPOINT ["/entrypoint.sh"]
Expand Down
36 changes: 34 additions & 2 deletions dockerfiles/php-lol/Dockerfile.8.4
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,11 @@ RUN chmod 0444 \
/usr/local/etc/php/php.ini

#
# Run
# Runtime
#
# Shared by the "rootless" and "run" stages, ports are declared per stage.
#
FROM foundation AS run
FROM foundation AS runtime

ARG BUILD_DATE
ARG VCS_REF
Expand All @@ -248,6 +250,36 @@ LABEL org.label-schema.build-date="$BUILD_DATE" \

WORKDIR /usr/src/app

#
# Rootless
#
# Same as "run", but the whole process tree (supervisord, php-fpm, nginx) runs
# as 65534:65534 and nginx listens on unprivileged port 8080.
#
FROM runtime AS rootless

RUN set -eux \
&& sed -i -e '/^user /d' -e 's/listen \[::\]:80 /listen [::]:8080 /' -e 's/listen 80 /listen 8080 /' /etc/nginx/nginx.conf \
&& sed -i -e '/^chown = /d' -e '/^user = /d' /etc/supervisor/supervisord.conf \
&& sed -i -e '/^user = /d' /etc/supervisor/conf.d/app.conf \
&& chmod 0444 /etc/nginx/nginx.conf /etc/supervisor/supervisord.conf /etc/supervisor/conf.d/app.conf \
&& mkdir -p /var/cache/nginx /var/log/nginx /var/lib/php/session \
&& chown -R 65534:65534 /var/cache/nginx /var/log/nginx /var/lib/php/session

EXPOSE 8080/tcp

USER 65534:65534

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

#
# Run
#
# Last stage: plain "docker build" keeps producing the port 80 image.
#
FROM runtime AS run

EXPOSE 80/tcp 443/tcp

ENTRYPOINT ["/entrypoint.sh"]
Expand Down
36 changes: 34 additions & 2 deletions dockerfiles/php-lol/Dockerfile.8.5
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,11 @@ RUN chmod 0444 \
/usr/local/etc/php/php.ini

#
# Run
# Runtime
#
# Shared by the "rootless" and "run" stages, ports are declared per stage.
#
FROM foundation AS run
FROM foundation AS runtime

ARG BUILD_DATE
ARG VCS_REF
Expand All @@ -314,6 +316,36 @@ LABEL org.label-schema.build-date="$BUILD_DATE" \

WORKDIR /usr/src/app

#
# Rootless
#
# Same as "run", but the whole process tree (supervisord, php-fpm, nginx) runs
# as 65534:65534 and nginx listens on unprivileged port 8080.
#
FROM runtime AS rootless

RUN set -eux \
&& sed -i -e '/^user /d' -e 's/listen \[::\]:80 /listen [::]:8080 /' -e 's/listen 80 /listen 8080 /' /etc/nginx/nginx.conf \
&& sed -i -e '/^chown = /d' -e '/^user = /d' /etc/supervisor/supervisord.conf \
&& sed -i -e '/^user = /d' /etc/supervisor/conf.d/app.conf \
&& chmod 0444 /etc/nginx/nginx.conf /etc/supervisor/supervisord.conf /etc/supervisor/conf.d/app.conf \
&& mkdir -p /var/cache/nginx /var/log/nginx /var/lib/php/session \
&& chown -R 65534:65534 /var/cache/nginx /var/log/nginx /var/lib/php/session

EXPOSE 8080/tcp

USER 65534:65534

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

#
# Run
#
# Last stage: plain "docker build" keeps producing the port 80 image.
#
FROM runtime AS run

EXPOSE 80/tcp 443/tcp

ENTRYPOINT ["/entrypoint.sh"]
Expand Down
Loading