diff --git a/.dockerignore b/.dockerignore index f73b76d..1fc3508 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ .git/* .env +data # vendor folder will be filled from the container vendor diff --git a/.env-dist b/.env-dist index 9ef67e1..0b0949b 100644 --- a/.env-dist +++ b/.env-dist @@ -49,9 +49,9 @@ AUTHSERVER_HOST=authserver.ely.by # LETSENCRYPT_HOST=account.ely.by # LETSENCRYPT_EMAIL=erickskrauch@ely.by -# MySQL -MYSQL_ALLOW_EMPTY_PASSWORD=yes -MYSQL_ROOT_PASSWORD= -MYSQL_DATABASE=ely_accounts -MYSQL_USER=ely_accounts_user -MYSQL_PASSWORD=ely_accounts_password +# MariaDB +ALLOW_EMPTY_PASSWORD=yes +MARIADB_ROOT_PASSWORD= +MARIADB_DATABASE=ely_accounts +MARIADB_USER=ely_accounts_user +MARIADB_PASSWORD=ely_accounts_password diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2b0d718..35fb3f1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -74,7 +74,7 @@ Codeception: services: - name: redis:4.0.10-alpine alias: redis - - name: mariadb:10.2.11 + - name: bitnami/mariadb:10.3.20-debian-9-r4 alias: db variables: # App config @@ -85,10 +85,10 @@ Codeception: REDIS_HOST: "redis" REDIS_PORT: "6379" # MariaDB config - MYSQL_RANDOM_ROOT_PASSWORD: "true" - MYSQL_DATABASE: "ely_accounts_test" - MYSQL_USER: "ely_accounts_tester" - MYSQL_PASSWORD: "ely_accounts_tester_password" + ALLOW_EMPTY_PASSWORD: "yes" + MARIADB_DATABASE: "ely_accounts_test" + MARIADB_USER: "ely_accounts_tester" + MARIADB_PASSWORD: "ely_accounts_tester_password" before_script: # We don't count code coverage yet, so xdebug can be removed - sudo rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini diff --git a/Dockerfile b/Dockerfile index 3dec577..8652dc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,7 +68,7 @@ CMD ["php-fpm"] # ================================================================================ -FROM nginx:1.15.10-alpine AS web +FROM fholzer/nginx-brotli:v1.16.0 AS web ENV PHP_SERVERS php:9000 @@ -88,7 +88,9 @@ CMD ["nginx", "-g", "daemon off;"] # ================================================================================ -FROM mariadb:10.3.14-bionic AS db +FROM bitnami/mariadb:10.3.20-debian-9-r4 AS db + +USER 0 COPY ./docker/mariadb/config.cnf /etc/mysql/conf.d/ @@ -116,5 +118,7 @@ RUN set -ex \ && rm -rf /mysql-sys \ && apt-get purge -y --auto-remove $fetchDeps -ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["mysqld"] +USER 1001 + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["/run.sh"] diff --git a/api/modules/oauth/controllers/IdentityController.php b/api/modules/oauth/controllers/IdentityController.php index 034e8c5..57e5281 100644 --- a/api/modules/oauth/controllers/IdentityController.php +++ b/api/modules/oauth/controllers/IdentityController.php @@ -1,4 +1,6 @@ ['index'], 'allow' => true, 'roles' => [P::OBTAIN_ACCOUNT_INFO], - 'roleParams' => function() { - /** @noinspection NullPointerExceptionInspection */ - return [ - 'accountId' => Yii::$app->user->getIdentity()->getAccount()->id, - ]; + 'roleParams' => function(): array { + /** @var \api\components\User\IdentityInterface $identity */ + $identity = Yii::$app->user->getIdentity(); + $account = $identity->getAccount(); + if ($account === null) { + Yii::$app->sentry->captureMessage('Unexpected lack of account', [ + 'identityType' => get_class($identity), + 'userId' => $identity->getId(), + 'assignedPermissions' => $identity->getAssignedPermissions(), + ], [ + 'level' => 'warning', + ]); + + return ['accountId' => 0]; + } + + return ['accountId' => $account->id]; }, ], ], diff --git a/common/models/Textures.php b/common/models/Textures.php index 2077cfc..9051a6c 100644 --- a/common/models/Textures.php +++ b/common/models/Textures.php @@ -65,12 +65,22 @@ class Textures { public function getTextures(): array { /** @var SkinSystemApi $api */ $api = Yii::$container->get(SkinSystemApi::class); + if (YII_ENV_PROD) { + $api->setClient(new \GuzzleHttp\Client([ + 'connect_timeout' => 2, + 'decode_content' => false, + 'read_timeout' => 5, + 'stream' => true, + 'timeout' => 5, + ])); + } + try { $textures = $api->textures($this->account->username); } catch (RequestException $e) { Yii::warning('Cannot get textures from skinsystem.ely.by. Exception message is ' . $e->getMessage()); } catch (GuzzleException $e) { - Yii::error($e); + Yii::warning($e); } return $textures ?? []; diff --git a/docker/nginx/account.ely.by.conf.template b/docker/nginx/account.ely.by.conf.template index be254c3..24dd49c 100644 --- a/docker/nginx/account.ely.by.conf.template +++ b/docker/nginx/account.ely.by.conf.template @@ -10,6 +10,7 @@ server { add_header X-Frame-Options "sameorigin" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; + add_header Content-Security-Policy "default-src 'none';style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' https://www.google-analytics.com https://recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.gstatic.cn/recaptcha/;img-src 'self' data: www.google-analytics.com;font-src 'self' data:;connect-src 'self' https://sentry.io https://sentry.ely.by;frame-src https://www.google.com/recaptcha/ https://recaptcha.net/recaptcha/"; # You can uncomment the next lines to enable debug mode # rewrite_log on; @@ -61,7 +62,8 @@ server { fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param REQUEST_URI $request_url; fastcgi_param REMOTE_ADDR $http_x_real_ip; - # Override HTTPS param to handle ssl from nginx-proxy container + # Override HTTPS param to handle ssl from nginx-proxy or haproxy containers fastcgi_param HTTPS $http_x_forwarded_ssl if_not_empty; + fastcgi_param HTTPS $http_x_forwarded_proto if_not_empty; } } diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index eac3609..811236a 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -5,7 +5,8 @@ error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { - worker_connections 1024; + worker_connections 4096; + use epoll; } http { @@ -19,11 +20,54 @@ http { access_log /var/log/nginx/access.log main; sendfile on; - keepalive_timeout 10; + server_tokens off; + + keepalive_timeout 16; + connection_pool_size 4k; + request_pool_size 8k; + output_buffers 10 32k; + client_max_body_size 2m; + client_body_buffer_size 16k; + client_header_buffer_size 4k; + large_client_header_buffers 16 8k; fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=cache:128m inactive=600m use_temp_path=off; fastcgi_cache_key "$scheme$request_method$host$request_uri"; + # Gzip + gzip on; + gzip_comp_level 6; + gzip_min_length 4096; + gzip_proxied any; + gzip_vary on; + gzip_types text/plain + text/css + text/javascript + application/javascript + application/json + application/octet-stream + application/x-font-ttf + application/x-font-opentype + application/vnd.ms-fontobject + image/svg+xml + image/x-icon; + + # Brotli + brotli on; + brotli_comp_level 6; + brotli_min_length 4096; + brotli_types text/plain + text/css + text/javascript + application/javascript + application/json + application/octet-stream + application/x-font-ttf + application/x-font-opentype + application/vnd.ms-fontobject + image/svg+xml + image/x-icon; + map $uri $cache_duration { "~*^.+\.(jpe?g|gif|png|svg|js|json|css|zip|rar|eot|ttf|woff|woff2|ico|xml)$" "max"; default "off";