From ac83101e53f44f0a0e5ecbd4c570ed721d5a7c96 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 21 Nov 2020 10:48:08 +0000 Subject: [PATCH] Create a monica app with docker-compose --- docker-compose.yml | 66 +++++++++++++++++ monica/.env | 168 ++++++++++++++++++++++++++++++++++++++++++ monica/Dockerfile | 29 ++++++++ monica/entrypoint.sh | 69 +++++++++++++++++ monica/start-cron.sh | 5 ++ monica/start-fpm.sh | 5 ++ monica/start-queue.sh | 5 ++ nginx.conf | 164 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 511 insertions(+) create mode 100644 docker-compose.yml create mode 100644 monica/.env create mode 100644 monica/Dockerfile create mode 100644 monica/entrypoint.sh create mode 100644 monica/start-cron.sh create mode 100644 monica/start-fpm.sh create mode 100644 monica/start-queue.sh create mode 100644 nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f49425b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,66 @@ +version: "3.4" + +services: + app: + build: ./monica + image: monica + env_file: ./monica/.env + volumes: + - data:/var/www/html/storage + - monica-public:/var/www/html/public + restart: always + depends_on: + - db + + db: + image: mariadb + environment: + - MYSQL_ROOT_PASSWORD=secret + - MYSQL_DATABASE=monica + - MYSQL_USER=homestead + - MYSQL_PASSWORD=secret + volumes: + - db:/var/lib/mysql + restart: always + + nginx: + image: nginx:alpine + restart: always + volumes: + - data:/var/www/html/storage:ro + - certs:/etc/nginx/certs:ro + - ./nginx.conf:/etc/nginx/nginx.conf + - monica-public:/var/www/html/public:ro + depends_on: + - app + ports: + - 80:80 + - 443:443 + networks: + - proxy-tier + - default + depends_on: + - omgwtfssl + + omgwtfssl: + image: paulczar/omgwtfssl + restart: "no" + volumes: + - certs:/certs + environment: + - SSL_SUBJECT=personel.scarif.local + - CA_SUBJECT=chris@scarif.local + - SSL_KEY=/certs/personel.scarif.local.key + - SSL_CSR=/certs/personel.scarif.local.csr + - SSL_CERT=/certs/personel.scarif.local.crt + networks: + - proxy-tier + +volumes: + data: + db: + certs: + monica-public: + +networks: + proxy-tier: diff --git a/monica/.env b/monica/.env new file mode 100644 index 0000000..1bc32ae --- /dev/null +++ b/monica/.env @@ -0,0 +1,168 @@ +# +# Welcome, friend ❤. Thanks for trying out Monica. We hope you'll have fun. +# + +# Two choices: local|production. Use local if you want to install Monica as a +# development version. Use production otherwise. +APP_ENV=production + +# true if you want to show debug information on errors. For production, put this +# to false. +APP_DEBUG=false + +# The encryption key. This is the most important part of the application. Keep +# this secure otherwise, everyone will be able to access your application. +# Must be 32 characters long exactly. +# Use `php artisan key:generate` or `pwgen -s 32 1` to generate a random key. +APP_KEY=LUdDIZ6FcVn0Lvajb6IY4cwGvAaomVtP + +# Prevent information leakage by referring to IDs with hashIds instead of +# the actual IDs used in the database. +HASH_SALT=YIfc5Go7H1TNxwnS7Uza +HASH_LENGTH=18 + +# The URL of your application. +APP_URL=https://personel.scarif.local + +# Force using APP_URL as base url of your application. +# You should not need this, unless you are using subdirectory config. +APP_FORCE_URL=false + +# Database information +# To keep this information secure, we urge you to change the default password +# Currently only "mysql" compatible servers are working +DB_CONNECTION=mysql +DB_HOST=db +DB_PORT=3306 +# You can use mysql unix socket if available, it overrides DB_HOST and DB_PORT values. +#DB_UNIX_SOCKET=/var/run/mysqld/mysqld.sock +DB_DATABASE=monica +DB_USERNAME=chris +DB_PASSWORD=secret +DB_PREFIX= +DB_TEST_HOST=127.0.0.1 +DB_TEST_DATABASE=monica_test +DB_TEST_USERNAME=homestead +DB_TEST_PASSWORD=secret + +# Use utf8mb4 database charset format to support emoji characters +# ⚠ be sure your DBMS supports utf8mb4 format +DB_USE_UTF8MB4=true + +# Mail credentials used to send emails from the application. +MAIL_MAILER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=f41fa47f889c25 +MAIL_PASSWORD=9a13972ad70a6a +MAIL_ENCRYPTION= +# Outgoing emails will be sent with these identity +MAIL_FROM_ADDRESS=chris@scarif.local +MAIL_FROM_NAME="Scarif" +# New registration notification sent to this email +APP_EMAIL_NEW_USERS_NOTIFICATION= + +# Ability to disable signups on your instance. +# Can be true or false. Default to false. +APP_DISABLE_SIGNUP=true + +# Enable user email verification. +APP_SIGNUP_DOUBLE_OPTIN=false + +# Set trusted proxy IP addresses. +# To trust all proxies that connect directly to your server, use a "*". +# To trust one or more specific proxies that connect directly to your server, +# use a comma separated list of IP addresses. +APP_TRUSTED_PROXIES= + +# Enable automatic cloudflare trusted proxy discover +APP_TRUSTED_CLOUDFLARE=false + +# Frequency of creation of new log files. Logs are written when an error occurs. +# Refer to config/logging.php for the possible values. +LOG_CHANNEL=daily + +# Error tracking. Specific to hosted version on .com. You probably don't need +# those. +SENTRY_SUPPORT=false +SENTRY_LARAVEL_DSN= + +# Send a daily ping to https://version.monicahq.com to check if a new version +# is available. When a new version is detected, you will have a message in the +# UI, as well as the release notes for the new changes. Can be true or false. +# Default to true. +CHECK_VERSION=true + +# Cache, session, and queue parameters +# ⚠ Change this only if you know what you are doing +#. Cache: database, file, memcached, redis, dynamodb +#. Session: file, cookie, database, apc, memcached, redis, array +#. Queue: sync, database, beanstalkd, sqs, redis +# If Queue is not set to 'sync', you'll have to set a queue worker +# See https://laravel.com/docs/5.7/queues#running-the-queue-worker +CACHE_DRIVER=database +SESSION_DRIVER=file +SESSION_LIFETIME=120 +QUEUE_CONNECTION=database + +# If you use redis, set the redis host or ip, like: +#REDIS_HOST=redis + +# Maximum allowed size for uploaded files, in kilobytes. +# Make sure this is an integer, without commas or spaces. +DEFAULT_MAX_UPLOAD_SIZE=10240 + +# Maximum allowed storage size per account, in megabytes. +# Make sure this is an integer, without commas or spaces. +DEFAULT_MAX_STORAGE_SIZE=512 + +# Default filesystem to store uploaded files. +# Possible values: public|s3 +DEFAULT_FILESYSTEM=public + +# AWS keys for S3 when using this storage method +AWS_KEY= +AWS_SECRET= +AWS_REGION=us-east-1 +AWS_BUCKET= +AWS_SERVER= + +# Allow Two Factor Authentication feature on your instance +MFA_ENABLED=true + +# Enable DAV support +DAV_ENABLED=true + +# CLIENT ID and SECRET used for OAuth authentication +PASSPORT_PERSONAL_ACCESS_CLIENT_ID= +PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET= + +# Allow to access general statistics about your instance through a public API +# call +ALLOW_STATISTICS_THROUGH_PUBLIC_API_ACCESS=false + +# Indicates that each user in the instance must comply to international policies +# like CASL or GDPR +POLICY_COMPLIANT=true + +# Enable geolocation services +# This is used to translate addresses to GPS coordinates. +ENABLE_GEOLOCATION=false + +# API key for geolocation services +# We use LocationIQ (https://locationiq.com/) to translate addresses to +# latitude/longitude coordinates. We could use Google instead but we don't +# want to give anything to Google, ever. +# LocationIQ offers 10,000 free requests per day. +LOCATION_IQ_API_KEY= + +# Enable weather on contact profile page +# Weather can only be fetched if we know longitude/latitude - this is why +# you also need to activate the geolocation service above to make it work +ENABLE_WEATHER=false + +# Access to weather data from darksky api +# https://darksky.net/dev/register +# Darksky provides an api with 1000 free API calls per day +# You need to enable the weather above if you provide an API key here. +DARKSKY_API_KEY= diff --git a/monica/Dockerfile b/monica/Dockerfile new file mode 100644 index 0000000..1b1cd34 --- /dev/null +++ b/monica/Dockerfile @@ -0,0 +1,29 @@ +FROM monica:fpm + +# Add wait-for-it +#ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /bin/wait-for-it.sh +#RUN chmod +x /bin/wait-for-it.sh + +# Add S6 supervisor (for graceful stop) +ADD https://github.com/just-containers/s6-overlay/releases/download/v1.21.1.1/s6-overlay-amd64.tar.gz /tmp/ +RUN tar xzf /tmp/s6-overlay-amd64.tar.gz -C / + +COPY entrypoint.sh /usr/local/bin +RUN chmod 755 /usr/local/bin/entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] +CMD ["/init"] + +RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" + +# Copy PHP-FPM service script +COPY start-fpm.sh /etc/services.d/php_fpm/run +RUN chmod 755 /etc/services.d/php_fpm/run + +# Copy cron service script +COPY start-cron.sh /etc/services.d/cron/run +RUN chmod 755 /etc/services.d/cron/run + +# Copy queue service script +COPY start-queue.sh /etc/services.d/queue/run +RUN chmod 755 /etc/services.d/queue/run diff --git a/monica/entrypoint.sh b/monica/entrypoint.sh new file mode 100644 index 0000000..b98d29c --- /dev/null +++ b/monica/entrypoint.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -Eeo pipefail + +# wait for the database to start +waitfordb() { + HOST=${DB_HOST:-mysql} + PORT=${DB_PORT:-3306} + echo "Connecting to ${HOST}:${PORT}" + + attempts=0 + max_attempts=30 + while [ $attempts -lt $max_attempts ]; do + busybox nc -w 1 "${HOST}:${PORT}" && break + echo "Waiting for ${HOST}:${PORT}..." + sleep 1 + let "attempts=attempts+1" + done + + if [ $attempts -eq $max_attempts ]; then + echo "Unable to contact your database at ${HOST}:${PORT}" + exit 1 + fi + + echo "Waiting for database to settle..." + sleep 3 +} + +if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ]; then + + MONICADIR=/var/www/html + ARTISAN="php ${MONICADIR}/artisan" + + # Ensure storage directories are present + STORAGE=${MONICADIR}/storage + mkdir -p ${STORAGE}/logs + mkdir -p ${STORAGE}/app/public + mkdir -p ${STORAGE}/framework/views + mkdir -p ${STORAGE}/framework/cache + mkdir -p ${STORAGE}/framework/sessions + chown -R www-data:www-data ${STORAGE} + chmod -R g+rw ${STORAGE} + + if [ -z "${APP_KEY:-}" -o "$APP_KEY" = "ChangeMeBy32KeyLengthOrGenerated" ]; then + ${ARTISAN} key:generate --no-interaction + else + echo "APP_KEY already set" + fi + + # Run migrations + waitfordb + ${ARTISAN} monica:update --force -vv + + if [ -n "${SENTRY_SUPPORT:-}" -a "$SENTRY_SUPPORT" = "true" -a -z "${SENTRY_NORELEASE:-}" -a -n "${SENTRY_ENV:-}" ]; then + commit=$(cat .sentry-commit) + release=$(cat .sentry-release) + ${ARTISAN} sentry:release --release="$release" --commit="$commit" --environment="$SENTRY_ENV" --force -v || true + fi + + if [ ! -f "${STORAGE}/oauth-public.key" -o ! -f "${STORAGE}/oauth-private.key" ]; then + echo "Passport keys creation ..." + ${ARTISAN} passport:keys + ${ARTISAN} passport:client --personal --no-interaction + echo "! Please be careful to backup $MONICADIR/storage/oauth-public.key and $MONICADIR/storage/oauth-private.key files !" + fi + +fi + +exec "$@" diff --git a/monica/start-cron.sh b/monica/start-cron.sh new file mode 100644 index 0000000..1309842 --- /dev/null +++ b/monica/start-cron.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv sh +set -eu; + +# Start Cron +exec busybox crond -f -l 0 -L /proc/1/fd/1 diff --git a/monica/start-fpm.sh b/monica/start-fpm.sh new file mode 100644 index 0000000..9ee8a0a --- /dev/null +++ b/monica/start-fpm.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv sh +set -e; + +# Start PHP-FPM +php-fpm -R --nodaemonize diff --git a/monica/start-queue.sh b/monica/start-queue.sh new file mode 100644 index 0000000..6adc690 --- /dev/null +++ b/monica/start-queue.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv sh +set -eu; + +# Start Queue +exec php /var/www/html/artisan queue:work --sleep=10 --timeout=0 --tries=3 --queue=default,migration >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..a846877 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,164 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + real_ip_header X-Real-IP; + + # Connect to app service + upstream php-handler { + server app:9000; + } + + server { + listen 443 ssl http2; + + ssl_certificate /etc/nginx/certs/personel.scarif.local.crt; + ssl_certificate_key /etc/nginx/certs/personel.scarif.local.key; + + server_name personel.scarif.local; + + ## HSTS ## + # Add the 'Strict-Transport-Security' headers to enable HSTS protocol. + # WARNING: Only add the preload option once you read about the consequences: https://hstspreload.org/. + # This form will add the domain to a hardcoded list that is shipped in all major browsers and getting + # removed from this list could take several months. + # + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;" always; + + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "none" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Remove X-Powered-By, which is an information leak + fastcgi_hide_header X-Powered-By; + + root /var/www/html/public; + + index index.html index.htm index.php; + + charset utf-8; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ ^/(?:robots.txt|security.txt) { + allow all; + log_not_found off; + access_log off; + } + + error_page 404 500 502 503 504 /index.php; + + location ~ /\.well-known/(?:carddav|caldav) { + return 301 $scheme://$host/dav; + } + location = /.well-known/security.txt { + return 301 $scheme://$host/security.txt; + } + location ~ /\.(?!well-known).* { + deny all; + } + + # set max upload size + client_max_body_size 10G; + fastcgi_buffers 64 4K; + + # Enable gzip but do not remove ETag headers + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + # Uncomment if your server is build with the ngx_pagespeed module + # This module is currently not supported. + #pagespeed off; + + location ~ \.php$ { + # regex to split $uri to $fastcgi_script_name and $fastcgi_path + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + + # Check that the PHP script exists before passing it + try_files $fastcgi_script_name =404; + + fastcgi_pass php-handler; + fastcgi_index index.php; + + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + # Bypass the fact that try_files resets $fastcgi_path_info + # see: http://trac.nginx.org/nginx/ticket/321 + set $path_info $fastcgi_path_info; + fastcgi_param PATH_INFO $path_info; + } + + # Adding the cache control header for js and css files + # Make sure it is BELOW the PHP block + location ~ \.(?:css|js|woff2?|svg|gif|json)$ { + try_files $uri /index.php$request_uri; + add_header Cache-Control "public, max-age=15778463"; + + ## HSTS ## + # Add the 'Strict-Transport-Security' headers to enable HSTS protocol. + # Note it is intended to have those duplicated to the ones above. + # WARNING: Only add the preload option once you read about the consequences: https://hstspreload.org/. + # This form will add the domain to a hardcoded list that is shipped in all major browsers and getting + # removed from this list could take several months. + # + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;" always; + + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "none" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Optional: Don't log access to assets + access_log off; + } + + location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ { + try_files $uri /index.php$request_uri; + + # Optional: Don't log access to assets + access_log off; + } + + # deny access to .htaccess files + location ~ /\.ht { + deny all; + } + } +}