Monday, November 15, 2021

[SOLVED] How do I give right permissions to a system user to run commands in docker container?

Issue

Containerized a Laravel application using Docker Compose. It simply has 2 services: app (running PHP8.0-FPM) and nginx.

docker-compose.yml:

version: "3"
services:
    app:
        build:
            args:
                USER: novruz
            context: ./
            dockerfile: Dockerfile
        image: blogger
        container_name: blogger-app
        restart: unless-stopped
        working_dir: /var/www/
        volumes:
            - ./:/var/www/
        networks:
            - laravel

    nginx:
        image: nginx:alpine
        container_name: blogger-nginx
        restart: unless-stopped
        ports:
            - 8000:80
        volumes:
            - ./:/var/www/
            - ./.docker/nginx/:/etc/nginx/conf.d/
        networks:
            - laravel

networks:
    laravel:
        driver: bridge

Dockerfile:

FROM php:8.0-fpm

ARG USER

RUN apt-get update && apt-get install -y --no-install-recommends \
        curl \
        git \
        zip \
        unzip \
        libpng-dev \
        libonig-dev \
        libxml2-dev \
        libzip-dev \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install bcmath

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Create a system user to run Composer and Artisan commands.
RUN useradd -G www-data,root -d /home/$USER $USER
RUN mkdir -p /home/$USER/.composer \
    && chown -R $USER:$USER /home/$USER

WORKDIR /var/www

USER $USER

These configurations build (docker-compose build app) and run (docker-compose up -d) the application successfully.

Problem

But when I run composer install --optimize-autoloader --no-dev in the app service container, it throws an exception:

[RuntimeException]                               
/var/www/vendor does not exist and could not be created.

This is a permission issue, but how can I give right permissions to the user novruz which was created at build time (and added to the group www-data)?

Debug

Here's the interesting part. Everything works correctly on my local machine (MacBook Air M1 2020) but it does not on Ubuntu 20.04 (Focal Fossa).

ls -l output on my local machine:

-rw-r--r--  1 www-data www-data    556 Sep 14 13:53 Dockerfile
-rw-r--r--  1 www-data www-data     17 Sep  9 07:00 README.md
drwxr-xr-x  7 www-data www-data    224 Sep  7 14:33 app
-rwxr-xr-x  1 www-data www-data   1686 Sep  7 14:33 artisan
drwxr-xr-x  4 www-data www-data    128 Sep  7 14:33 bootstrap
-rw-r--r--  1 www-data www-data   1766 Sep  9 07:37 composer.json
-rw-r--r--  1 www-data www-data 338242 Sep  9 07:37 composer.lock
drwxr-xr-x 17 www-data www-data    544 Sep 13 07:22 config
drwxr-xr-x  7 www-data www-data    224 Sep 13 06:45 database
-rw-r--r--  1 www-data www-data    713 Sep 14 14:32 docker-compose.yml
-rw-r--r--  1 www-data www-data   1319 Sep  9 07:04 phpunit.xml
drwxr-xr-x  7 www-data www-data    224 Sep  7 14:33 public
drwxr-xr-x  4 www-data www-data    128 Sep  9 07:07 resources
drwxr-xr-x  6 www-data www-data    192 Sep 10 05:52 routes
-rw-r--r--  1 www-data www-data    563 Sep  7 14:33 server.php
drwxr-xr-x  5 www-data www-data    160 Sep  7 14:33 storage
drwxr-xr-x  7 www-data www-data    224 Sep  9 12:02 tests

As you can see, www-data is the owner of /var/www/ directory and the files in it. I can run any command without any problem.

ls -l output on Ubuntu 20.04:

-rw-rw-r--  1  998  997    599 Sep 14 13:41 Dockerfile
-rw-rw-r--  1  998  997     17 Sep 14 10:16 README.md
drwxrwxr-x  7  998  997   4096 Sep 14 10:16 app
-rwxrwxr-x  1  998  997   1686 Sep 14 10:16 artisan
drwxrwxr-x  3  998  997   4096 Sep 14 10:16 bootstrap
-rw-rw-r--  1  998  997   1766 Sep 14 10:16 composer.json
-rw-rw-r--  1  998  997 338242 Sep 14 10:16 composer.lock
drwxrwxr-x  2  998  997   4096 Sep 14 10:16 config
drwxrwxr-x  5  998  997   4096 Sep 14 10:16 database
-rw-rw-r--  1  998  997    720 Sep 14 10:16 docker-compose.yml
-rw-rw-r--  1  998  997   1319 Sep 14 10:16 phpunit.xml
drwxrwxr-x  2  998  997   4096 Sep 14 10:16 public
drwxrwxr-x  4  998  997   4096 Sep 14 10:16 resources
drwxrwxr-x  2  998  997   4096 Sep 14 10:16 routes
-rw-rw-r--  1  998  997    563 Sep 14 10:16 server.php
drwxrwxr-x  5  998  997   4096 Sep 14 10:16 storage
drwxrwxr-x  5  998  997   4096 Sep 14 10:16 tests

But the user is 998 and the group is 997 on Ubuntu. And I keep getting a runtime exception. Why is this happening?


Solution

The problem here is that the UID/GID of the user within the container is not the same as the UID/GID of the user outside the container.

There are three ways you can go about solving this problem:

  1. Don't use bind mounts. (This is when a directory is mounted both on the host and the container.) The problem is that the host and guest disagree about which users correspond to which UID, so if you make them not share files, this issue will go away.

    Instead of using a bind mount, you can use COPY within the Dockerfile.

    COPY . /var/www
    

    The downside is that it can be useful to edit the container's files while the container is running.

  2. Match UID/GID inside and outside the container. Rather than create the user at build time, override the UID/GID of the FPM process at run time. This can be done in docker compose like this: How to set uid and gid in Docker Compose?

  3. Remap users using user namespaces. I've never tried this approach, so I can't really give you any guidance, but here's the Docker documentation on how to do this: https://docs.docker.com/engine/security/userns-remap/

    Also, here's a thread about using user remapping with bind mounts: https://forums.docker.com/t/permission-denied-when-using-userns-remap-and-writing-to-a-directory-mounted-from-the-host/48379/2



Answered By - Nick ODell