From db8e13d749f88b6b97b4d605ed3ddd28a4e5c622 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sat, 9 Nov 2019 17:46:22 +0300 Subject: [PATCH 01/12] Hotfix to handle Chrly's long responses --- common/models/Textures.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 ?? []; From 2fe3ede4ead94d6872ac177e9e47f277ef179f26 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Fri, 15 Nov 2019 20:03:52 +0300 Subject: [PATCH 02/12] Replace basic mariadb image with the bitnami --- .env-dist | 12 ++++++------ .gitlab-ci.yml | 10 +++++----- Dockerfile | 10 +++++++--- 3 files changed, 18 insertions(+), 14 deletions(-) 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..bc90d0c 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" + MARIADB_RANDOM_ROOT_PASSWORD: "true" + 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..c2bbf5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] From b47522e6f942c0837c9b22a0bbb4ce6fb7e0b622 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Fri, 15 Nov 2019 20:09:19 +0300 Subject: [PATCH 03/12] Fix CI for bitmani mariadb image --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc90d0c..35fb3f1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -85,7 +85,7 @@ Codeception: REDIS_HOST: "redis" REDIS_PORT: "6379" # MariaDB config - MARIADB_RANDOM_ROOT_PASSWORD: "true" + ALLOW_EMPTY_PASSWORD: "yes" MARIADB_DATABASE: "ely_accounts_test" MARIADB_USER: "ely_accounts_tester" MARIADB_PASSWORD: "ely_accounts_tester_password" From 885729fcde1b736eac3cc472261fa4dc94fb39a2 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 26 Nov 2019 21:30:53 +0300 Subject: [PATCH 04/12] Replace nginx image to enable gzpi and brotli encoding --- Dockerfile | 2 +- docker/nginx/nginx.conf | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c2bbf5b..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 diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index eac3609..f370d65 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -29,5 +29,19 @@ http { default "off"; } + # GZIP + gzip on; + gzip_comp_level 6; + gzip_min_length 256; + gzip_proxied any; + gzip_vary on; + gzip_types application/* font/* image/* text/*; + + # Brotli + brotli on; + brotli_comp_level 6; + brotli_min_length 256; + brotli_types application/* font/* image/* text/*; + include /etc/nginx/conf.d/*.conf; } From 9eea03df73e208df3740c15aa6222038a60df730 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 26 Nov 2019 22:32:26 +0300 Subject: [PATCH 05/12] Enable gzip and brotli for all response types --- docker/nginx/nginx.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index f370d65..a32eba6 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -29,19 +29,19 @@ http { default "off"; } - # GZIP + # Gzip gzip on; gzip_comp_level 6; gzip_min_length 256; gzip_proxied any; gzip_vary on; - gzip_types application/* font/* image/* text/*; + gzip_types *; # Brotli brotli on; brotli_comp_level 6; brotli_min_length 256; - brotli_types application/* font/* image/* text/*; + brotli_types *; include /etc/nginx/conf.d/*.conf; } From 9f645d0934e767e83e04a6f744985ad2886fb77e Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 26 Nov 2019 22:47:22 +0300 Subject: [PATCH 06/12] Add CSP header --- docker/nginx/account.ely.by.conf.template | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/nginx/account.ely.by.conf.template b/docker/nginx/account.ely.by.conf.template index be254c3..4accae3 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; From a5f6a2d43777c7747c2964b7457b4e8b6b731005 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 27 Nov 2019 03:41:27 +0300 Subject: [PATCH 07/12] Tune nginx conf --- .dockerignore | 1 + docker/nginx/nginx.conf | 62 ++++++++++++++++++++++++++++++----------- 2 files changed, 47 insertions(+), 16 deletions(-) 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/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index a32eba6..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,29 +20,58 @@ 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"; } - # Gzip - gzip on; - gzip_comp_level 6; - gzip_min_length 256; - gzip_proxied any; - gzip_vary on; - gzip_types *; - - # Brotli - brotli on; - brotli_comp_level 6; - brotli_min_length 256; - brotli_types *; - include /etc/nginx/conf.d/*.conf; } From 22ef41ac7c055f3f6f113d9503af85105dab8367 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Mon, 2 Dec 2019 21:14:40 +0300 Subject: [PATCH 08/12] Fixes ACCOUNTS-5V9. Handle case when access token don't have associated account --- .../oauth/controllers/IdentityController.php | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) 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]; }, ], ], From 01028cf378e4fb35e7db3e1d6fc31e07288d94d5 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Mon, 2 Dec 2019 22:15:52 +0300 Subject: [PATCH 09/12] Fixes ACCOUNTS-5VC. Handle the case when there is missing session for access or refresh token --- .../OAuth2/Entities/AccessTokenEntity.php | 2 +- .../OAuth2/Grants/RefreshTokenGrant.php | 4 ++ .../OAuth2/Storage/SessionStorage.php | 37 ++++++++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/api/components/OAuth2/Entities/AccessTokenEntity.php b/api/components/OAuth2/Entities/AccessTokenEntity.php index 183704d..c489281 100644 --- a/api/components/OAuth2/Entities/AccessTokenEntity.php +++ b/api/components/OAuth2/Entities/AccessTokenEntity.php @@ -28,7 +28,7 @@ class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity { return $this; } - public function getSession() { + public function getSession(): ?OriginalSessionEntity { if ($this->session instanceof OriginalSessionEntity) { return $this->session; } diff --git a/api/components/OAuth2/Grants/RefreshTokenGrant.php b/api/components/OAuth2/Grants/RefreshTokenGrant.php index 4f20ad9..71b265b 100644 --- a/api/components/OAuth2/Grants/RefreshTokenGrant.php +++ b/api/components/OAuth2/Grants/RefreshTokenGrant.php @@ -121,6 +121,10 @@ class RefreshTokenGrant extends AbstractGrant { $session = $oldRefreshToken->getSession(); } + if ($session === null) { + throw new Exception\InvalidRefreshException(); + } + $scopes = $this->formatScopes($session->getScopes()); // Get and validate any requested scopes diff --git a/api/components/OAuth2/Storage/SessionStorage.php b/api/components/OAuth2/Storage/SessionStorage.php index 11e89d9..02ac0ba 100644 --- a/api/components/OAuth2/Storage/SessionStorage.php +++ b/api/components/OAuth2/Storage/SessionStorage.php @@ -3,6 +3,7 @@ namespace api\components\OAuth2\Storage; use api\components\OAuth2\Entities\AuthCodeEntity; use api\components\OAuth2\Entities\SessionEntity; +use api\exceptions\ThisShouldNotHappenException; use common\models\OauthSession; use ErrorException; use League\OAuth2\Server\Entity\AccessTokenEntity as OriginalAccessTokenEntity; @@ -19,8 +20,13 @@ class SessionStorage extends AbstractStorage implements SessionInterface { * @param string $sessionId * @return SessionEntity|null */ - public function getById($sessionId) { - return $this->hydrate($this->getSessionModel($sessionId)); + public function getById($sessionId): ?SessionEntity { + $session = $this->getSessionModel($sessionId); + if ($session === null) { + return null; + } + + return $this->hydrate($session); } public function getByAccessToken(OriginalAccessTokenEntity $accessToken) { @@ -35,9 +41,14 @@ class SessionStorage extends AbstractStorage implements SessionInterface { return $this->getById($authCode->getSessionId()); } - public function getScopes(OriginalSessionEntity $session) { + public function getScopes(OriginalSessionEntity $entity) { + $session = $this->getSessionModel($entity->getId()); + if ($session === null) { + return []; + } + $result = []; - foreach ($this->getSessionModel($session->getId())->getScopes() as $scope) { + foreach ($session->getScopes() as $scope) { if ($this->server->getScopeStorage()->get($scope) !== null) { $result[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); } @@ -72,20 +83,20 @@ class SessionStorage extends AbstractStorage implements SessionInterface { return $sessionId; } - public function associateScope(OriginalSessionEntity $session, ScopeEntity $scope) { - $this->getSessionModel($session->getId())->getScopes()->add($scope->getId()); - } - - private function getSessionModel(string $sessionId): OauthSession { - $session = OauthSession::findOne($sessionId); + public function associateScope(OriginalSessionEntity $sessionEntity, ScopeEntity $scopeEntity): void { + $session = $this->getSessionModel($sessionEntity->getId()); if ($session === null) { - throw new ErrorException('Cannot find oauth session'); + throw new ThisShouldNotHappenException('Cannot find oauth session'); } - return $session; + $session->getScopes()->add($scopeEntity->getId()); } - private function hydrate(OauthSession $sessionModel) { + private function getSessionModel(string $sessionId): ?OauthSession { + return OauthSession::findOne(['id' => $sessionId]); + } + + private function hydrate(OauthSession $sessionModel): SessionEntity { $entity = new SessionEntity($this->server); $entity->setId($sessionModel->id); $entity->setClientId($sessionModel->client_id); From 9557064a97aa2c445368f3afbb58d7c98981519d Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Mon, 2 Dec 2019 22:22:51 +0300 Subject: [PATCH 10/12] Fixes ACCOUNTS-5VF --- api/components/OAuth2/Entities/RefreshTokenEntity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/components/OAuth2/Entities/RefreshTokenEntity.php b/api/components/OAuth2/Entities/RefreshTokenEntity.php index 372f003..eec5987 100644 --- a/api/components/OAuth2/Entities/RefreshTokenEntity.php +++ b/api/components/OAuth2/Entities/RefreshTokenEntity.php @@ -15,7 +15,7 @@ class RefreshTokenEntity extends \League\OAuth2\Server\Entity\RefreshTokenEntity return false; } - public function getSession(): SessionEntity { + public function getSession(): ?SessionEntity { if ($this->session instanceof SessionEntity) { return $this->session; } From 46b771a061d1201b2de974243666dac5feaad305 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Mon, 2 Dec 2019 22:28:47 +0300 Subject: [PATCH 11/12] Fixes ACCOUNTS-5VE --- api/components/User/OAuth2Identity.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/components/User/OAuth2Identity.php b/api/components/User/OAuth2Identity.php index bbb06cf..1b8b69b 100644 --- a/api/components/User/OAuth2Identity.php +++ b/api/components/User/OAuth2Identity.php @@ -41,6 +41,11 @@ class OAuth2Identity implements IdentityInterface { } public function getAccount(): ?Account { + $session = $this->getSession(); + if ($session === null) { + return null; + } + return $this->getSession()->account; } @@ -70,7 +75,7 @@ class OAuth2Identity implements IdentityInterface { // @codeCoverageIgnoreEnd - private function getSession(): OauthSession { + private function getSession(): ?OauthSession { return OauthSession::findOne(['id' => $this->_accessToken->getSessionId()]); } From 8dad8a3eeb85eeb467aa3e26668b76001ef5724d Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 3 Dec 2019 17:22:18 +0300 Subject: [PATCH 12/12] Fix https detection on nginx from haproxy --- docker/nginx/account.ely.by.conf.template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/nginx/account.ely.by.conf.template b/docker/nginx/account.ely.by.conf.template index 4accae3..24dd49c 100644 --- a/docker/nginx/account.ely.by.conf.template +++ b/docker/nginx/account.ely.by.conf.template @@ -62,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; } }