├── project
├── test.txt
└── index.php
├── .gitignore
├── img
└── ok.png
├── .env.dist
├── tests.sh
├── Dockerfile
├── .travis.yml
├── docker-compose.yml
├── php
└── entrypoint.sh
├── nginx
└── default.conf
├── LICENSE
└── README.md
/project/test.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | .idea/
--------------------------------------------------------------------------------
/img/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ypereirareis/docker-permissions/HEAD/img/ok.png
--------------------------------------------------------------------------------
/.env.dist:
--------------------------------------------------------------------------------
1 | PHPUID=YOUR_LOCAL_USER_UID # often 1000 but not always
2 | PHPGID=YOUR_LOCAL_USER_GID # often 1000 but not always
--------------------------------------------------------------------------------
/tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 |
3 | URI='http://127.0.0.1:8888/'
4 |
5 | docker-compose rm -f || true
6 | docker-compose build
7 | docker-compose up -d
8 | sleep 3
9 | curl "${URI}" | grep -q "READ OK" && sleep 1
10 | curl "${URI}" | grep -q "WRITE OK" && sleep 1
11 | curl "${URI}" | grep -q "EXECUTION OK" && sleep 1
12 |
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.2.3-fpm-alpine3.7
2 |
3 | RUN echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories
4 | RUN apk --no-cache add shadow
5 |
6 | ARG PROJECT_DIR_ARG='/usr/share/nginx/html'
7 | ENV PROJECT_DIR=$PROJECT_DIR_ARG
8 |
9 | RUN mkdir -p $PROJECT_DIR
10 | COPY ./project $PROJECT_DIR
11 | RUN chown -R www-data:www-data $PROJECT_DIR
12 |
13 | COPY ./php/entrypoint.sh /entrypoint.sh
14 | RUN chmod +x /entrypoint.sh
15 |
16 | WORKDIR $PROJECT_DIR
17 | VOLUME $PROJECT_DIR
18 |
19 | ENTRYPOINT ["/entrypoint.sh"]
20 | CMD ["php-fpm"]
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | env:
4 | global:
5 | - PHPUID=2000
6 | - PHPGID=2000
7 | - DEBUG=true
8 |
9 | before_install:
10 | - sudo rm /usr/local/bin/docker-compose
11 | - curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > docker-compose
12 | - chmod +x docker-compose
13 | - sudo mv docker-compose /usr/local/bin
14 |
15 | language: bash
16 |
17 | services:
18 | - docker
19 |
20 | script:
21 | - chmod +x tests.sh
22 | - bash tests.sh
23 |
24 | after_script:
25 | - docker-compose stop
26 | - docker-compose rm -f
27 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | php:
4 | build:
5 | context: .
6 | # args:
7 | # - PROJECT_DIR_ARG=/usr/share/nginx/public
8 | container_name: ypr-permissions-php
9 | environment:
10 | - PHP_UID=${PHPUID}
11 | - PHP_GID=${PHPGID}
12 | - DEBUG=false # true, to have more info from the entry point
13 | volumes:
14 | - ./project:/usr/share/nginx/html
15 | nginx:
16 | image: nginx:1.13.9-alpine
17 | container_name: ypr-permissions-nginx
18 | depends_on:
19 | - php
20 | volumes:
21 | - ./project:/usr/share/nginx/html
22 | - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
23 | ports:
24 | - 8888:80 # http://127.0.0.1:8888/
--------------------------------------------------------------------------------
/php/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | [ "$DEBUG" = "true" ] && set -x
3 | set -e
4 |
5 | # --
6 | PHP_UID_DEFAULT=$(id -u www-data)
7 |
8 | # Here we check if GID and UID are already defined properly or not
9 | # i.e Do we have a volume mounted and with a different uid/gid ?
10 | if [[ -z "$(ls -n $PROJECT_DIR | grep $PHP_UID_DEFAULT)" ]]; then
11 |
12 | : ${PHP_UID:=$(id -u www-data)}
13 | : ${PHP_GID:=$(id -g www-data)}
14 |
15 | export PHP_UID
16 | export PHP_GID
17 |
18 | if [ "$PHP_UID" != "0" ] && [ "$PHP_UID" != "$(id -u www-data)" ]; then
19 | echo "Need to change UID and GID."
20 | usermod -u $PHP_UID www-data
21 | groupmod -g $PHP_GID www-data
22 | chown -R www-data:www-data $PROJECT_DIR
23 | echo "UID and GID changed to $PHP_UID and $PHP_GID."
24 | fi
25 | else
26 | echo "UID and GUI are OK !"
27 | fi
28 |
29 | exec "$@"
--------------------------------------------------------------------------------
/project/index.php:
--------------------------------------------------------------------------------
1 | READ OK
';
4 | echo 'If you see this line, it means that you are able to read and execute PHP files from: '.__DIR__.'';
5 | echo '
';
6 |
7 | if (is_writable(__DIR__)){
8 | echo 'WRITE OK
';
9 | echo 'If you see this line, it means that you CAN write files in:'.__DIR__;
10 | } else {
11 | echo 'WRITE : ERROR
';
12 | echo "If you see this line, it means that you CANNOT create files in:".__DIR__;
13 | }
14 |
15 | echo '
';
16 |
17 | if (is_executable(__DIR__.'/test.txt')){
18 | echo 'EXECUTION OK
';
19 | echo 'If you see this line, it means that you CAN execute files in:'.__DIR__;
20 | } else {
21 | echo 'EXECUTION : ERROR
';
22 | echo "If you see this line, it means that you CANNOT execute files in:".__DIR__;
23 | }
--------------------------------------------------------------------------------
/nginx/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | root /usr/share/nginx/html;
4 |
5 | location / {
6 | # try to serve file directly, fallback to app.php
7 | try_files $uri /index.php$is_args$args;
8 | }
9 |
10 | # Deny all . files
11 | location ~ /\. {
12 | deny all;
13 | }
14 |
15 | location ~ ^/index\.php(/|$) {
16 | fastcgi_pass php:9000;
17 | fastcgi_split_path_info ^(.+\.php)(/.*)$;
18 | include fastcgi_params;
19 |
20 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
21 | fastcgi_param DOCUMENT_ROOT $realpath_root;
22 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
23 |
24 | fastcgi_buffer_size 128k;
25 | fastcgi_buffers 256 16k;
26 | fastcgi_busy_buffers_size 256k;
27 | fastcgi_temp_file_write_size 256k;
28 |
29 | internal;
30 | }
31 |
32 | # return 404 for all other php files not matching the front controller
33 | # this prevents access to other php files you don't want to be accessible.
34 | location ~ \.php$ {
35 | return 404;
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Yannick Pereira-Reis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Docker volumes and permissions
2 |
3 | [](https://travis-ci.org/ypereirareis/docker-permissions)
4 |
5 | This repository shows you a way to deal with read/write/exec permissions
6 | and how to define user and group ids using volumes from the host, when running containers.
7 |
8 | Indeed, user uid and gid are often different in the container and on the host system.
9 | And it's possible to use the same docker configuration on different hosts (local, test, stating,...)
10 | and with possibly different users from a uid/gid point of view.
11 |
12 | So if you want to use volumes, you need to understand how to configure the permissions of the project/folder
13 | mapped into your containers.
14 |
15 | :whale: **A second way of doing things is available at:** [ypereirareis/docker-permissions-reborn](https://github.com/ypereirareis/docker-permissions-reborn)
16 |
17 | ## Docker images
18 |
19 | For this demo project we are relying on two simple docker alpine images:
20 |
21 | * [Nginx (nginx:1.13.9-alpine)](https://hub.docker.com/_/nginx/)
22 | * [PHP-FPM (php:7.2.3-fpm-alpine3.7)](https://hub.docker.com/_/php/)
23 |
24 | We are using the nginx image directly simply overriding the configuration file with our own
25 | to be able to use PHP-FPM running in the `php` container.
26 |
27 | ```bash
28 | location ~ ^/index\.php(/|$) {
29 | fastcgi_pass php:9000;
30 | ...
31 | internal;
32 | }
33 | ```
34 |
35 | For the PHP-FPM image we are building our own form the `php:7.2.3-fpm-alpine3.7` because we need to
36 | add a custom entry point to deal with permissions.
37 |
38 | ## The problem
39 |
40 | We have a problem if we use a volume to share our code from host to container.
41 |
42 | * The user running `php-fpm` in the container is `wwww-data` with `uid=100` and `gid=101`.
43 | * Our host user often has `uid=1000` and `gid=1000` but not always.
44 | * The `nginx` user will not be able to read/write/exec files from the volume if permissions are not defined properly.
45 |
46 | But I do not recommend to change permissions with `chmod` directly.
47 | The way I recommend is to change the owner of the shared directory to map uid and gid of the container user
48 | to the host user.
49 |
50 | ## The Dockerfile and entry point to change uig/gid
51 |
52 | The interesting part of the Dockerfile is this one:
53 |
54 | ```bash
55 | ARG PROJECT_DIR_ARG='/usr/share/nginx/html'
56 | ENV PROJECT_DIR=$PROJECT_DIR_ARG
57 |
58 | RUN mkdir -p $PROJECT_DIR
59 | COPY ./project $PROJECT_DIR
60 | RUN chown -R www-data:www-data $PROJECT_DIR
61 | ```
62 |
63 | * In the entry point we are checking if uid and gid of the container user `nginx` must be changed.
64 |
65 | ```bash
66 | # --
67 | PHP_UID_DEFAULT=$(id -u www-data)
68 |
69 | # Here we check if GID and UID are already defined properly or not
70 | # i.e Do we have a volume mounted and with a different uid/gid ?
71 | if [[ -z "$(ls -n $PROJECT_DIR | grep $PHP_UID_DEFAULT)" ]]; then
72 |
73 | : ${PHP_UID:=$(id -u www-data)}
74 | : ${PHP_GID:=$(id -g www-data)}
75 |
76 | export PHP_UID
77 | export PHP_GID
78 |
79 | if [ "$PHP_UID" != "0" ] && [ "$PHP_UID" != "$(id -u www-data)" ]; then
80 | echo "Need to change UID and GID."
81 | usermod -u $PHP_UID www-data
82 | groupmod -g $PHP_GID www-data
83 | chown -R www-data:www-data $PROJECT_DIR
84 | echo "UID and GID changed to $PHP_UID and $PHP_GID."
85 | fi
86 | else
87 | echo "UID and GUI are OK !"
88 | fi
89 | ```
90 |
91 | * The possible new values are coming from environment variables.
92 |
93 | ```yaml
94 | php:
95 | build:
96 | context: .
97 | environment:
98 | - PHP_UID=${PHPUID}
99 | - PHP_GID=${PHPGID}
100 | ```
101 |
102 | * We can define per-host custom UID/GID environment varaibles with a `.env` file.
103 |
104 | ```bash
105 | PHPUID=1000
106 | PHPGID=1000
107 | ```
108 |
109 | # Run the demo
110 |
111 | ```bash
112 | $ git clone git@github.com:ypereirareis/docker-permissions.git && cd docker-permissions
113 | ```
114 |
115 | * Copy `.env.dist` to `.env` and set your uid and gid values.
116 | * Comment/Uncomment volume from `docker-compose.yml`
117 |
118 | ```yaml
119 | services:
120 | php:
121 | volumes:
122 | - ./project:/usr/share/nginx/html
123 | ```
124 |
125 | ```bash
126 | $ docker-compose build
127 |
128 | ```
129 |
130 | ```bash
131 | $ docker-compose up
132 | Starting ypr-permissions-php ...
133 | Starting ypr-permissions-php ... done
134 | Recreating ypr-permissions-nginx ...
135 | Recreating ypr-permissions-nginx ... done
136 | Attaching to ypr-permissions-php, ypr-permissions-nginx
137 | ypr-permissions-php | UID and GUI are OK !
138 | ypr-permissions-php | [07-Mar-2018 16:35:37] NOTICE: fpm is running, pid 1
139 | ypr-permissions-php | [07-Mar-2018 16:35:37] NOTICE: ready to handle connections
140 | ```
141 |
142 | Go to [http://127.0.0.1:8888/](http://127.0.0.1:8888/)
143 |
144 | If everything is ok, you should see:
145 |
146 | 
147 |
148 | # Tests
149 |
150 | ```bash
151 | chmod +x tests.sh && ./tests.sh
152 | ```
153 |
154 | # LICENSE
155 |
156 | MIT License
157 |
158 | Copyright (c) 2018 Yannick Pereira-Reis
159 |
160 | Permission is hereby granted, free of charge, to any person obtaining a copy
161 | of this software and associated documentation files (the "Software"), to deal
162 | in the Software without restriction, including without limitation the rights
163 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
164 | copies of the Software, and to permit persons to whom the Software is
165 | furnished to do so, subject to the following conditions:
166 |
167 | The above copyright notice and this permission notice shall be included in all
168 | copies or substantial portions of the Software.
169 |
170 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
172 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
173 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
174 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
175 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
176 | SOFTWARE.
177 |
--------------------------------------------------------------------------------