From 779a702c87bc4dc3a833c33821f9560a5611e887 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Tue, 21 Jul 2020 01:26:53 +0100 Subject: [PATCH] Add Docker --- .docker.env.example | 1 + .dockerignore | 7 ++ .env.example | 7 ++ .gitignore | 3 + Makefile | 23 ++++ README.md | 40 +++++++ docker-compose.yaml | 66 +++++++++++ helpers.php | 55 +++++++++ tools/assets/development/.gitkeep | 0 tools/docker/composer/Dockerfile | 26 +++++ tools/docker/composer/composer-installer.sh | 17 +++ tools/docker/nginx/Dockerfile | 9 ++ tools/docker/nginx/conf.d/app.conf | 123 ++++++++++++++++++++ tools/docker/php/Dockerfile | 17 +++ tools/docker/php/php.ini | 2 + 15 files changed, 396 insertions(+) create mode 100644 .docker.env.example create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 Makefile create mode 100644 docker-compose.yaml create mode 100644 helpers.php create mode 100644 tools/assets/development/.gitkeep create mode 100644 tools/docker/composer/Dockerfile create mode 100644 tools/docker/composer/composer-installer.sh create mode 100644 tools/docker/nginx/Dockerfile create mode 100644 tools/docker/nginx/conf.d/app.conf create mode 100644 tools/docker/php/Dockerfile create mode 100644 tools/docker/php/php.ini diff --git a/.docker.env.example b/.docker.env.example new file mode 100644 index 0000000..d19ab2b --- /dev/null +++ b/.docker.env.example @@ -0,0 +1 @@ +MYSQL_RANDOM_ROOT_PASSWORD=1 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9f06bf6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +/.dependabot/ +/.github/ +/.git/ +/.platform/ +/Makefile +/README.md +/**/node_modules/ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2e9937d --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +MYSQL_DATABASE=stream +MYSQL_HOSTNAME=mysql +MYSQL_PASSWORD=secret +MYSQL_PORT=3306 +MYSQL_USER=stream + +DRUPAL_SALT=abc123 diff --git a/.gitignore b/.gitignore index 48c72fd..39d0e24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ /* +!/.dockerignore +!/.docker.env.example !/.env.example !/.gitignore !/assets/ !/composer.* !/config/ +!/docker-compose.yaml !/helpers.php !/patches/ !/README.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5d33bd9 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +default: init + +app-install: copy-required-files + docker-compose run --rm php vendor/bin/drush site:install -y \ + --existing-config \ + --site-name="Stream Demo" \ + --account-pass=admin123 + +copy-required-files: vendor + cp .env.example .env + cp .docker.env.example .docker.env + +init: copy-required-files start app-install + +start: copy-required-files + docker-compose up -d + +stop: + docker-compose down --remove-orphans + +vendor: composer.json composer.lock + docker-compose run --rm composer validate + docker-compose run --rm composer install diff --git a/README.md b/README.md index 83cd411..592013a 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,43 @@ Managing dependencies for a custom project - [ Using Drupal's Composer Scaffold ](https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold) + +## Usage + +### Starting the project + +The project is using Docker and Docker Compose for local development. + +Run the `make` command to create the required files, download Drupal core, the additional contrib modules and PHP libraries, and install Drupal. + +If you are using [Traefik](https://docs.traefik.io) (recommended), then the site should now be available at . + +### Running Composer commands + +Compser commands can be run via the `docker-compose run` command: + +``` +docker-compose run --rm composer +``` + +For example, to add the Admin Toolbar module: + +``` +docker-compose run --rm composer require drupal/admin_toolbar +``` + +### Running Drush commands + +Drush commands can be run via the `docker-compose run` command: + +``` +docker-compose run --rm php vendor/bin/drush +``` + +For example, to rebuild the cache: + +``` +docker-compose run --rm php vendor/bin/drush cache:rebuild +``` + +Alternatively, you could connect to the `php` container using `docker-compose exec php` and run `vendor/bin/drush ` within the container. diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..46d7994 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,66 @@ +version: '3.4' + +services: + nginx: + container_name: stream-demo-nginx + build: + context: . + dockerfile: tools/docker/nginx/Dockerfile + volumes: + - ./web:/srv/app/web + depends_on: + - mysql + - php + labels: + - "traefik.docker.network=traefik" + - "traefik.http.routers.stream-demo.rule=Host(`stream-demo.docker.localhost`)" + networks: + - traefik + - internal + + php: + image: stream-demo-php + container_name: stream-demo-php + build: + context: . + dockerfile: tools/docker/php/Dockerfile + volumes: + - .:/srv/app + env_file: + - .env + links: + - mysql + networks: + - internal + + mysql: + container_name: stream-demo-mysql + image: mariadb:10 + env_file: + - .docker.env + - .env + volumes: + - mysql:/var/lib/mysql + networks: + - internal + + composer: + container_name: stream-demo-composer + build: + context: . + dockerfile: tools/docker/composer/Dockerfile + volumes: + - .:/app + - ~/.composer:/root/.composer + - ~/.ssh:/root/.ssh + environment: + COMPOSER_MEMORY_LIMIT: -1 + +networks: + traefik: + external: true + internal: + +volumes: + mysql: + driver: local diff --git a/helpers.php b/helpers.php new file mode 100644 index 0000000..9740094 --- /dev/null +++ b/helpers.php @@ -0,0 +1,55 @@ + 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') { + return substr($value, 1, -1); + } + + return $value; + } +} + +if (! function_exists('value')) { + /** + * Return the default value of the given value. + * + * @param mixed $value + * @return mixed + */ + function value($value) + { + return $value instanceof Closure ? $value() : $value; + } +} diff --git a/tools/assets/development/.gitkeep b/tools/assets/development/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/docker/composer/Dockerfile b/tools/docker/composer/Dockerfile new file mode 100644 index 0000000..37553a1 --- /dev/null +++ b/tools/docker/composer/Dockerfile @@ -0,0 +1,26 @@ +FROM php:7.4-cli + +COPY tools/docker/composer/composer-installer.sh /usr/local/bin/composer-installer + +RUN apt-get -yqq update \ + && apt-get -yqq install --no-install-recommends \ + git \ + libpng-dev \ + unzip \ + wget \ + zip \ + && apt-get autoremove -y --purge \ + && rm -fr /var/lib/apt/lists/* \ + && chmod +x /usr/local/bin/composer-installer \ + && composer-installer \ + && mv composer.phar /usr/local/bin/composer \ + && chmod +x /usr/local/bin/composer \ + && composer --version + +RUN docker-php-ext-install gd + +COPY . /app + +WORKDIR /app + +ENTRYPOINT [ "/usr/local/bin/composer" ] diff --git a/tools/docker/composer/composer-installer.sh b/tools/docker/composer/composer-installer.sh new file mode 100644 index 0000000..40250f5 --- /dev/null +++ b/tools/docker/composer/composer-installer.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +EXPECTED_CHECKSUM="$(wget -q -O - https://composer.github.io/installer.sig)" +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" + +if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ] +then + >&2 echo 'ERROR: Invalid installer checksum' + rm composer-setup.php + exit 1 +fi + +php composer-setup.php --quiet +RESULT=$? +rm composer-setup.php +exit $RESULT diff --git a/tools/docker/nginx/Dockerfile b/tools/docker/nginx/Dockerfile new file mode 100644 index 0000000..7b39cef --- /dev/null +++ b/tools/docker/nginx/Dockerfile @@ -0,0 +1,9 @@ +FROM nginx:1 + +RUN rm /etc/nginx/conf.d/default.conf + +COPY tools/docker/nginx/conf.d/* /etc/nginx/conf.d/ + +COPY web /srv/app/web + +WORKDIR /srv/app/web diff --git a/tools/docker/nginx/conf.d/app.conf b/tools/docker/nginx/conf.d/app.conf new file mode 100644 index 0000000..b02a9ad --- /dev/null +++ b/tools/docker/nginx/conf.d/app.conf @@ -0,0 +1,123 @@ +server { + server_name _; + root /srv/app/web; + + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Very rarely should these ever be accessed outside of your lan + location ~* \.(txt|log)$ { + allow 192.168.0.0/16; + deny all; + } + + location ~ \..*/.*\.php$ { + return 403; + } + + location ~ ^/sites/.*/private/ { + return 403; + } + + # Block access to scripts in site files directory + location ~ ^/sites/[^/]+/files/.*\.php$ { + deny all; + } + + # Allow "Well-Known URIs" as per RFC 5785 + location ~* ^/.well-known/ { + allow all; + } + + # Block access to "hidden" files and directories whose names begin with a + # period. This includes directories used by version control systems such + # as Subversion or Git to store control files. + location ~ (^|/)\. { + return 403; + } + + location / { + # try_files $uri @rewrite; # For Drupal <= 6 + try_files $uri /index.php?$query_string; # For Drupal >= 7 + } + + location @rewrite { + rewrite ^/(.*)$ /index.php?q=$1; + } + + # Don't allow direct access to PHP files in the vendor directory. + location ~ /vendor/.*\.php$ { + deny all; + return 404; + } + + # Protect files and directories from prying eyes. + location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|composer\.(lock|json)$|web\.config$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ { + deny all; + return 404; + } + + # In Drupal 8, we must also match new paths where the '.php' appears in + # the middle, such as update.php/selection. The rule we use is strict, + # and only allows this pattern with the update.php front controller. + # This allows legacy path aliases in the form of + # blog/index.php/legacy-path to continue to route to Drupal nodes. If + # you do not have any paths like that, then you might prefer to use a + # laxer rule, such as: + # location ~ \.php(/|$) { + # The laxer rule will continue to work if Drupal uses this new URL + # pattern with front controllers other than update.php in a future + # release. + location ~ '\.php$|^/update.php' { + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + # Ensure the php file exists. Mitigates CVE-2019-11043 + try_files $fastcgi_script_name =404; + # Security note: If you're running a version of PHP older than the + # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini. + # See http://serverfault.com/q/627903/94922 for details. + include fastcgi_params; + # Block httpoxy attacks. See https://httpoxy.org/. + fastcgi_param HTTP_PROXY ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors on; + # PHP 5 socket location. + #fastcgi_pass unix:/var/run/php5-fpm.sock; + # PHP 7 socket location. + fastcgi_pass php:9000; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + try_files $uri @rewrite; + expires max; + log_not_found off; + } + + # Fighting with Styles? This little gem is amazing. + # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6 + location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7 + try_files $uri @rewrite; + } + + # Handle private files through Drupal. Private file's path can come + # with a language prefix. + location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7 + try_files $uri /index.php?$query_string; + } + + # Enforce clean URLs + # Removes index.php from urls like www.example.com/index.php/my-page --> www.example.com/my-page + # Could be done with 301 for permanent or other redirect codes. + if ($request_uri ~* "^(.*/)index\.php/(.*)") { + return 307 $1$2; + } +} diff --git a/tools/docker/php/Dockerfile b/tools/docker/php/Dockerfile new file mode 100644 index 0000000..d428212 --- /dev/null +++ b/tools/docker/php/Dockerfile @@ -0,0 +1,17 @@ +FROM php:7.4-fpm + +RUN apt-get update \ + && apt-get -yqq install --no-install-recommends \ + default-mysql-client \ + libpng-dev \ + && apt-get autoremove -y --purge \ + && rm -fr /var/lib/apt/lists/* + +RUN docker-php-ext-install gd opcache pdo pdo_mysql + +COPY . /srv/app +COPY tools/docker/php/php.ini /usr/local/etc/php/conf.d/php.ini + +WORKDIR /srv/app + +RUN chown -R www-data: /srv/app diff --git a/tools/docker/php/php.ini b/tools/docker/php/php.ini new file mode 100644 index 0000000..c0f44fa --- /dev/null +++ b/tools/docker/php/php.ini @@ -0,0 +1,2 @@ +date.timezone = UTC +expose_php = off