Using Docker enviroment variables in server.php

1.9k Views Asked by At

I created my own dockerfile (ubuntu:xenial) using environment variables. This dockerfile uses php7.0-fpm php7.0-xml php7.0-mbstring php-mysql

The dockerfiles contains:

ENV           MYSQL_HOST=192.168.0.2
ENV           MYSQL_DBNAME=dbname_xyz
ENV           MYSQL_USERNAME=username_xyz
ENV           MYSQL_PASSWORD=password_xyz

...

RUN echo "clear_env = no" >> /etc/php/7.0/fpm/pool.d/www.conf

in the server.php I'm trying to use those variables, but they aren't known obviously

$host       = $_SERVER["MYSQL_HOST"];
$dbname     = $_SERVER["MYSQL_DBNAME"];
$username   = $_SERVER["MYSQL_USERNAME"];
$password   = $_SERVER["MYSQL_PASSWORD"];

$pdo = new \PDO("mysql:host=$host;dbname=$dbname", $username, $password);

Running that shows error:

FastCGI sent in stderr: "PHP message: PHP Notice:  Undefined index: MYSQL_HOST in /var/webdav/server.php on line 4

Executing on: container-shell shows correct value

php -r "echo getenv('MYSQL_HOST');"
env | grep MYSQL

Any suggestions what I've to change?

UPDATE 20211215 after AymDEV's feedback
UPDATE 20211215 after piotrekkr's feedback

Full dockerfile:

FROM          ubuntu:xenial
MAINTAINER    [email protected]

# Changing WEBDAV_PASSWORD doesn't work
# MYSQL_x aren't known to server.php
ENV           WEBDAV_USERNAME=admin
ENV           WEBDAV_PASSWORD=admin
ENV           MYSQL_HOST=192.168.0.2
ENV           MYSQL_DBNAME=dbname_xyz
ENV           MYSQL_USERNAME=username_xyz
ENV           MYSQL_PASSWORD=password_xyz

# Defaults
WORKDIR       /var/webdav
VOLUME        /var/webdav/public
VOLUME        /var/webdav/data


# Install zip
RUN           apt-get update && \
              DEBIAN_FRONTEND=noninteractive apt-get install -y zip unzip php-zip

# Install nginx with php7 support
RUN           apt-get update && \
              DEBIAN_FRONTEND=noninteractive apt-get install -y nginx php7.0-fpm php7.0-xml php7.0-mbstring php-mysql && \
              rm -rf /var/lib/apt/lists/*

# Install SabreDAV
RUN           php -r "readfile('http://getcomposer.org/installer');" > composer-setup.php && \
              php composer-setup.php --install-dir=/usr/bin --filename=composer && \
              php -r "unlink('composer-setup.php');" && \
              composer require sabre/dav ~3.2.2 && \
              rm /usr/bin/composer

# Set up entrypoint
COPY          /scripts/install.sh /install.sh

# Configure nginx
COPY          /config/nginx/default /etc/nginx/sites-enabled/default
COPY          /config/nginx/fastcgi_params /etc/nginx/fastcgi_params

# forward request and error logs to docker log collector
RUN           ln -sf /dev/stdout /var/log/nginx/access.log && \
              ln -sf /dev/stderr /var/log/nginx/error.log

# copy server.php for client -- sabredav communication
COPY          /web/server.php /var/webdav/server.php


#make environment variables available to php
RUN echo "clear_env = no" >> /etc/php/7.0/fpm/pool.d/www.conf

#nginx will be process with PID=1
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD           /install.sh && service php7.0-fpm start && nginx

full server.php

<?php

date_default_timezone_set('Europe/Berlin');
$baseUri = '/';
$host       = $_ENV["MYSQL_HOST"];
$dbname     = $_ENV["MYSQL_DBNAME"];
$username   = $_ENV["MYSQL_USERNAME"];
$password   = $_ENV["MYSQL_PASSWORD"];

$pdo = new \PDO("mysql:host=$host;dbname=$dbname", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

require_once 'vendor/autoload.php';

$authBackend      = new \Sabre\DAV\Auth\Backend\PDO($pdo);
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo);
$carddavBackend   = new \Sabre\CardDAV\Backend\PDO($pdo);
$caldavBackend    = new \Sabre\CalDAV\Backend\PDO($pdo);

$nodes = [
    new \Sabre\CalDAV\Principal\Collection($principalBackend),
    new \Sabre\CalDAV\CalendarRoot($principalBackend, $caldavBackend),
    new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
];

$server = new \Sabre\DAV\Server($nodes);
if (isset($baseUri)) $server->setBaseUri($baseUri);

$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend));
$server->addPlugin(new \Sabre\DAV\Browser\Plugin());
$server->addPlugin(new \Sabre\CalDAV\Plugin());
$server->addPlugin(new \Sabre\CardDAV\Plugin());
$server->addPlugin(new \Sabre\DAV\Sync\Plugin());

$server->exec();
1

There are 1 best solutions below

3
andrzejwp On

Actually - everything is working as designed. There are several reasons why you are not seeing the ENV variables and the approach you are using is a bit flawed.

TLDR: use docker-compose and split the FPM process into a separate container, with fpm as the entrypoint.

Why it doesn't work

Since this case is a bit complex - I will try to go step by step by the reasons why it's not working for you. Hopefully this will help.

  1. Your PHP scripts are executed within the FPM workers. Each of these workers passes specific data (context) into PHP. That context you can see, for example, inside the $_SERVER variable:

     Array
        (
        [LANGUAGE] =>
        [LC_TIME] =>
        [LC_CTYPE] =>
        [LC_MONETARY] =>
        [TERM] => xterm
        [LC_COLLATE] =>
        [PATH] => /sbin:/usr/sbin:/bin:/usr/bin
        [LC_ADDRESS] =>
        [LANG] =>
        [LC_TELEPHONE] =>
        [LC_MESSAGES] =>
        [LC_NAME] =>
        [LC_MEASUREMENT] =>
        [LC_IDENTIFICATION] =>
        [LC_ALL] =>
        [PWD] => /
        [LC_NUMERIC] =>
        [LC_PAPER] =>
        [USER] => www-data
        [HOME] => /var/www
        [HTTP_ACCEPT] => */*
        [HTTP_USER_AGENT] => curl/7.47.0
        [HTTP_HOST] => localhost
        [REDIRECT_STATUS] => 200
        [SERVER_NAME] => _
        [SERVER_PORT] => 80
        [SERVER_ADDR] => 127.0.0.1
        [REMOTE_PORT] => 35542
        [REMOTE_ADDR] => 127.0.0.1
        [SERVER_SOFTWARE] => nginx/1.10.3
        [GATEWAY_INTERFACE] => CGI/1.1
        [REQUEST_SCHEME] => http
        [SERVER_PROTOCOL] => HTTP/1.1
        [DOCUMENT_ROOT] => /var/www/html
        [DOCUMENT_URI] => /print.php
        [REQUEST_URI] => /print.php
        [SCRIPT_NAME] => /print.php
        [CONTENT_LENGTH] =>
        [CONTENT_TYPE] =>
        [REQUEST_METHOD] => GET
        [QUERY_STRING] =>
        [SCRIPT_FILENAME] => /var/www/html/print.php
        [PATH_INFO] =>
        [FCGI_ROLE] => RESPONDER
        [PHP_SELF] => /print.php
        [REQUEST_TIME_FLOAT] => 1639586759.7522
        [REQUEST_TIME] => 1639586759
        )
    

    If you print out the same variable from the CLI - the result will be quite different (I guess you already observed that):

    Array
    (
        [HOSTNAME] => 580747313ddc
        [TERM] => xterm
        [MYSQL_PASSWORD] => password_xyz
        [LS_COLORS] => rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
        [WEBDAV_USERNAME] => admin
        [MYSQL_DBNAME] => dbname_xyz
        [PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
        [PWD] => /var/www/html
        [WEBDAV_PASSWORD] => admin
        [MYSQL_USERNAME] => username_xyz
        [SHLVL] => 1
        [HOME] => /root
        [MYSQL_HOST] => 192.168.0.2
        [_] => /usr/bin/php
        [OLDPWD] => /etc/nginx
        [PHP_SELF] => print.php
        [SCRIPT_NAME] => print.php
        [SCRIPT_FILENAME] => print.php
        [PATH_TRANSLATED] => print.php
        [DOCUMENT_ROOT] =>
        [REQUEST_TIME_FLOAT] => 1639586851.762
        [REQUEST_TIME] => 1639586851
        [argv] => Array
            (
                [0] => print.php
            )
    
        [argc] => 1
    )
    
  2. The ENV is properly passed into the container and if you execute processes in the container - the ENV is available to them. This is seen in the second example above. But, if you look at the processes running in your container:

root@580747313ddc:/var/www/html# ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   1872   420 ?        Ss   16:20   0:00 /bin/sh -c service php7.0-fpm start && nginx
root        37  0.0  0.4  45560  9208 ?        S    16:20   0:00 nginx: master process nginx
root        43  0.0  0.1   3628  2908 pts/0    Ss   16:21   0:01 bash
www-data  3154  0.0  0.1  45932  4032 ?        S    16:38   0:00 nginx: worker process
www-data  3155  0.0  0.1  45932  3232 ?        S    16:38   0:00 nginx: worker process
www-data  3156  0.0  0.1  45932  3232 ?        S    16:38   0:00 nginx: worker process
www-data  3157  0.0  0.1  45932  3232 ?        S    16:38   0:00 nginx: worker process
www-data  3158  0.0  0.1  45932  3232 ?        S    16:38   0:00 nginx: worker process
root      3217  0.0  0.5 121364 10324 ?        Ss   16:41   0:00 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf)
www-data  3218  0.0  0.4 121708  9920 ?        S    16:41   0:00 php-fpm: pool www
www-data  3219  0.0  0.4 121708  9512 ?        S    16:41   0:00 php-fpm: pool www
root      3233  0.0  0.1   5472  2380 pts/0    R+   16:51   0:00 ps -aux

you can examine the environment of each one of them, see process with PID=1 (the entrypoint of the container):

root@580747313ddc:/var/www/html# cat /proc/1/environ
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=580747313ddcWEBDAV_USERNAME=adminWEBDAV_PASSWORD=adminMYSQL_HOST=192.168.0.2MYSQL_DBNAME=dbname_xyzMYSQL_USERNAME=username_xyzMYSQL_PASSWORD=password_xyzHOME=/root

the environment has the ENVs that you created. All good here. But if you look at the FPM process:

root@580747313ddc:/var/www/html# cat /proc/3217/environ
root@580747313ddc:/var/www/html#

it's empty!

This is because you are running the FPM as a service (as in a systemd service), which means that you would have to define these environment variables within the service's configuration file, in this case /lib/systemd/system/php7.0-fpm.service.

It can be done, however - it wouldn't be very clean to do so.

What you can do to fix it

You can work around the problems above by simply using php-fpm as the entrypoint to your container. That way - it will have access to the ENV. This is already being done by the official php-fpm images and we can try to use them. Here's a minimal workable example, using docker-compose:

docker-compose.yml:

version: '3'
services:
  web:
    image: nginx
    container_name: web
    depends_on:
      - php
    links:
      - php
    volumes:
      - ./html:/var/www/html
      - ./conf:/etc/nginx/conf.d/
    ports:
      - "8080:80"
  php:
    image: php:7.4-fpm-alpine
    environment:
      - MYSQL_PASS=pass123
    volumes:
      - ./html:/var/www/html

Now, in the same folder create html/print.php:

<?php print_r($_SERVER);?>

and conf/default.conf:

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    root /var/www/html;
    location ~ \.php$ {

        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param REQUEST_URI $request_uri;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        include        fastcgi_params;
    }

}

with this - simply run docker-compose up -d and then curl localhost:8080/print.php.

This should give you the expected env variable MYSQL_PASS inside your $_SERVER array:

Array
(
    [HOSTNAME] => 84a4d1e174d3
    [PHP_INI_DIR] => /usr/local/etc/php
    [SHLVL] => 1
    [HOME] => /home/www-data
    [PHP_LDFLAGS] => -Wl,-O1 -pie
    [PHP_CFLAGS] => -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
    [PHP_VERSION] => 7.4.26
    [GPG_KEYS] => 42670A7FE4D0441C8E4632349E4FDC074A4EF02D 5A52880781F755608BF815FC910DEB46F53EA312
    [PHP_CPPFLAGS] => -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
    [PHP_ASC_URL] => https://www.php.net/distributions/php-7.4.26.tar.xz.asc
    [MYSQL_PASS] => pass123
    [PHP_URL] => https://www.php.net/distributions/php-7.4.26.tar.xz
    [PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    [WEBDAV_USER] => test
    [PHPIZE_DEPS] => autoconf       dpkg-dev dpkg       file        g++         gcc         libc-dev        make        pkgconf         re2c
    [PWD] => /var/www/html
    [PHP_SHA256] => e305b3aafdc85fa73a81c53d3ce30578bc94d1633ec376add193a1e85e0f0ef8
    [USER] => www-data
    [HTTP_ACCEPT] => */*
    [HTTP_USER_AGENT] => curl/7.77.0
    [HTTP_HOST] => localhost:8080
    [PATH_INFO] =>
    [SCRIPT_FILENAME] => /var/www/html/print.php
    [REDIRECT_STATUS] => 200
    [SERVER_NAME] => localhost
    [SERVER_PORT] => 80
    [SERVER_ADDR] => 172.18.0.3
    [REMOTE_PORT] => 55288
    [REMOTE_ADDR] => 172.18.0.1
    [SERVER_SOFTWARE] => nginx/1.21.4
    [GATEWAY_INTERFACE] => CGI/1.1
    [REQUEST_SCHEME] => http
    [SERVER_PROTOCOL] => HTTP/1.1
    [DOCUMENT_ROOT] => /var/www/html
    [DOCUMENT_URI] => /print.php
    [REQUEST_URI] => /print.php
    [SCRIPT_NAME] => /print.php
    [CONTENT_LENGTH] =>
    [CONTENT_TYPE] =>
    [REQUEST_METHOD] => GET
    [QUERY_STRING] =>
    [FCGI_ROLE] => RESPONDER
    [PHP_SELF] => /print.php
    [REQUEST_TIME_FLOAT] => 1639591114.4143
    [REQUEST_TIME] => 1639591114
    [argv] => Array
        (
        )

    [argc] => 0
)