serversideup / docker-php

🐳 Production-ready Docker images for PHP. Optimized for Laravel, WordPress, and more!
https://serversideup.net/open-source/docker-php/
GNU General Public License v3.0
1.74k stars 119 forks source link

Change execution to be unprivileged #311

Closed jaydrogers closed 6 months ago

jaydrogers commented 6 months ago

πŸ‘‰ What this PR does

This PR improves security, user experience, and compatibility with other hosts and container orchestartors.

[!CAUTION] Due to the permission changes, this PR will likely include breaking changes for some people. Please help test this release before it gets merged.

⚠️ Breaking Changes

Getting the images to accept requests on 80 and 443

Simply use Docker's port methods to forward correctly:

docker run --rm -e SSL_MODE=mixed -p 80:8080 -p 443:8443 serversideup/php-dev:311-8.3-fpm-nginx

πŸ‘¨β€πŸ”¬ How to Test

We have a serversideup/php-dev repository that houses our unstable images. Everything is based on the PR number.

For example, here is how you can test.

Let's say you're using:

serversideup/php:beta-8.3-fpm-nginx

You would switch to:

serversideup/php-dev:311-8.3-fpm-nginx

It follows the pattern of serversideup/php-dev, followed by the PR Number, the PHP Version, then the Variation.

πŸ“• Reading the Docs for this PR

We have a special preview site that is updated on every commit for this PR. These docs will obviously change a lot during the PR stage, so be sure to expect changes or suggest where improvements should be made.

Check out the docs here:

Related issues

RadeJR commented 6 months ago

We have a dozen of applications in development that I will start testing on tomorrow. 3 wordpress sites running on apache, 2 laravel apps running on unit and the rest laravel apps are actually just fpm with nginx in separate containers that i set up because i was tired solving permission issues. I will move some of them to new dev fp-nginx images and will keep you posted. Thank you for awesome work, you make my work easier :D

jaydrogers commented 6 months ago

We have a dozen of applications in development that I will start testing on tomorrow. 3 wordpress sites running on apache, 2 laravel apps running on unit and the rest laravel apps are actually just fpm with nginx in separate containers that i set up because i was tired solving permission issues. I will move some of them to new dev fp-nginx images and will keep you posted. Thank you for awesome work, you make my work easier :D

@RadeJR: Thanks! I put together a migration a migration guide with a checklist: https://improve-file-permissions.docker-php.pages.dev/docs/guide/migrating-from-v2-to-v3

Keep me posted how your experience goes!

jeremynz21 commented 6 months ago

Awesome stuff! I will give it a go with some of ours

sneycampos commented 6 months ago

@jaydrogers when using volumes to sync the container files, the files into the container will belong to my host UID/GID (in my case 1000) To solve this problem i need to create a Dockerfile and use the built-in script to change the UID, right?

Would be great to set this UID/GID using environment variables, what you think?

Thanks and great work!

jaydrogers commented 6 months ago

Thanks for the comments!

Check this page out and let me know if this makes sense. I might refactor and add more detail in there if you feel anything is missing.

https://improve-file-permissions.docker-php.pages.dev/docs/guide/understanding-file-permissions

I’d love to do the UID/GID, but that requires the container to be privileged. I talk about it in that doc

sneycampos commented 6 months ago

Thanks for the comments!

Check this page out and let me know if this makes sense. I might refactor and add more detail in there if you feel anything is missing.

https://improve-file-permissions.docker-php.pages.dev/docs/guide/understanding-file-permissions

Was exactly what i was trying to do and didn't noticed that to change the UID/GID was necessary running as root, like in this Dockerfile in the documentation.

Thanks! Agree 100% and hope this should be released soonβ„’

jeremynz21 commented 6 months ago

Is there any way to run initialisation scripts as the root user? We mount AWS EFS volumes into the containers for the assets and run checks on startup that they have the right file permissions and if not change them.

I haven't managed to make it work with these new file permissions.

jsayer101 commented 6 months ago

Hello, I've started from scratch so see if all my problems were solved, but I found that I had problems using composer.

I got a project with this Dockerfile and this docker-compose.yml :

############################################
# Base Image
############################################
FROM serversideup/php-dev:311-8.2-fpm-nginx-alpine as base

############################################
# Development Image
############################################
FROM base as development

# Switch to root so we can do root things
USER root

# Install the necessary packages for our development environment
RUN apk update && apk add libraw libjpeg
RUN install-php-extensions gd imagick bcmath intl

# Save the build arguments as a variable
ARG USER_ID
ARG GROUP_ID

# Use the build arguments to change the UID
# and GID of www-data while also changing
# the file permissions for NGINX
RUN docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID && \
    \
    # Update the file permissions for our NGINX service to match the new UID/GID
    docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID --service nginx

# Drop back to our unprivileged user
USER www-data

############################################
# Production Image
############################################

# Since we're calling "base", production isn't
# calling any of that permission stuff
FROM base as production

# Switch to root so we can do root things
USER root
RUN apk update && apk add libraw libjpeg
RUN install-php-extensions gd imagick bcmath intl

# Drop back to our unprivileged user
USER www-data

# Copy our app files as www-data (33:33)
COPY --chown=www-data:www-data . /var/www/html
services:
  api:
    build:
      dockerfile: ./Dockerfile
      context: .
      target: development
      args:
        # UID and GID must be set as environment variables on the host machine
        USER_ID: ${UID:-1000}
        GROUP_ID: ${GID:-1000}
    container_name: pix-api
    restart: unless-stopped
    environment:
      PHP_FPM_POOL_NAME: "pixsell_api"
      AUTORUN_ENABLED: "true"
      AUTORUN_LARAVEL_ROUTE_CACHE: "false"
      AUTORUN_LARAVEL_VIEW_CACHE: "false"
      AUTORUN_LARAVEL_CONFIG_CACHE: "false"
      AUTORUN_LARAVEL_EVENT_CACHE: "false"
      PHP_DATE_TIMEZONE: "Europe/Paris"
      PHP_DISPLAY_ERROR: "On"
      PHP_DISPLAY_STARTUP_ERRORS: "On"
      PHP_ERROR_REPORTING: "32767"
      PHP_MEMORY_LIMIT: "4G"
    volumes:
      - .:/var/www/html
    ports:
      - "8000:8080"
    networks:
      - pix-network
    depends_on:
      - db
# .....

Composer got errors while trying to write to vendor/ and it can't install the dependencies. Running composer install in the container get me this error :

var/www/html $ composer install
Composer could not detect the root package (laravel/laravel) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`.
Package operations: 138 installs, 0 updates, 0 removals

In Filesystem.php line 260:

  /var/www/html/vendor does not exist and could not be created:  

install [--prefer-source] [--prefer-dist] [--prefer-install PREFER-INSTALL] [--dry-run] [--download-only] [--dev] [--no-suggest] [--no-dev] [--no-autoloader] [--no-progress] [--no-install] [--audit] [--audit-format AUDIT-FORMAT] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--apcu-autoloader-prefix APCU-AUTOLOADER-PREFIX] [--ignore-platform-req IGNORE-PLATFORM-REQ] [--ignore-platform-reqs] [--] [<packages>...]
jsayer101 commented 6 months ago

Edit : So i've figured out on how to be able to execute composer install inside the container. I've commented "USER www-data" in the dev stage of the dockerfile, logged in into the container and ran composer install.

I wonder how to implement this automatically using this technique as in the dev stage we don't copy the app as it is mounted using compose πŸ€”

I tried using entrypoints but I couldn't make it work

jaydrogers commented 6 months ago

Repsonse to @jeremynz21

@jeremynz21: You would would need to adjust the permissions in your container and set:

USER root

Since your scripts require root permissions, you just need to be extra cautious that you potentially could be creating files as root that will not be modified by www-data in the future. Most of the time its just running a simple chown command and you're set, but it can cause confusion on first set up.

Check this out for more detail: https://improve-file-permissions.docker-php.pages.dev/docs/guide/understanding-file-permissions

Response to @jsayer101

@jsayer101 Thanks for the detailed report! I moved this discussion to:

jaydrogers commented 6 months ago

πŸš€ This has been merged

This has been merged into main and is now part of the beta release!

Be sure to change your images accordingly. We're hoping the v3 images will hit stable soon: https://github.com/serversideup/docker-php/releases/tag/v3.0.0-beta4

moisish commented 5 months ago

@jaydrogers is it possible to run this conditionally ? I am mounting the folder /var/www/ to the container from the host because I am using the container with multiple sites but I can't change the permissions of all sites to ww-data

    chown -R www-data:www-data /var/www && \
    chmod -R 755 /var/www && \
jaydrogers commented 5 months ago

@jaydrogers is it possible to run this conditionally ? I am mounting the folder /var/www/ to the container from the host because I am using the container with multiple sites but I can't change the permissions of all sites to ww-data

I would suggest providing one container each user, you'll likely get better management that way. You can change the www-data ID to match each user on your host if you need multi-tenancy.

moisish commented 5 months ago

What about having a variable for www-data ? That will allow us to override that

jaydrogers commented 5 months ago

That's totally possible. In your own Dockerfile, you can use Build Arguments to customize it to your liking https://docs.docker.com/build/guide/build-args/

moisish commented 5 months ago

That will require a change on your containers tho to make it possible.

Basically i was asking if you will accept such a change l On 15 May 2024, at 22:49, Jay Rogers @.***> wrote:

ο»Ώ

That's totally possible. In your own Dockerfile, you can use Build Arguments to customize it to your liking https://docs.docker.com/build/guide/build-args/

β€” Reply to this email directly, view it on GitHubhttps://github.com/serversideup/docker-php/pull/311#issuecomment-2113340044, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADSZZRZ75YOCZENU75OOV73ZCO325AVCNFSM6AAAAABGJUL3OWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMJTGM2DAMBUGQ. You are receiving this because you commented.Message ID: @.***>

jaydrogers commented 5 months ago

I don't think it will. You can set what user to run as and even provide your own configurations if you want.

Example

############################################
# Base Image
############################################

# Learn more about the Server Side Up PHP Docker Images at:
# https://serversideup.net/open-source/docker-php/
FROM serversideup/php:8.3-fpm-nginx as base

# Run as root for all image stages
USER root

# Set `www-data` as the user to start FPM (or whatever user you want)
RUN echo "user = www-data" >> /usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf && \
    echo "group = www-data" >> /usr/local/etc/php-fpm.d/docker-php-serversideup-pool.conf

# Add multiple users
useradd one
useradd two
useradd three

Because I put USER root in there, it escalates the permissions back to root by default and you can do whatever you'd like in that image.