From 7713008d1a7369c9bc031f36d582539432009448 Mon Sep 17 00:00:00 2001 From: Philippe Lafrance Date: Sat, 8 Jul 2023 22:24:31 -0400 Subject: [PATCH] Add support for letsencrypt --- .docker/mock-key.pem | 28 +++++++++ .docker/mock.pem | 24 ++++++++ .gitignore | 1 + Dockerfile | 6 ++ docker-compose.yml | 3 + .../.well-known/acme-challenge/.gitkeep | 0 server.php | 59 +++++++++++++++++-- 7 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 .docker/mock-key.pem create mode 100644 .docker/mock.pem create mode 100644 letsencrypt/.well-known/acme-challenge/.gitkeep diff --git a/.docker/mock-key.pem b/.docker/mock-key.pem new file mode 100644 index 0000000..0549724 --- /dev/null +++ b/.docker/mock-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4XnH1rZS/hCWH +6AV6ptUZcOOAWUVbkICbiNWXA5yk0DfjbVFbdrS9fkK+WcPcIOhE2T678ec8tltd +TeIRpqkgN/yPrLKQ4ob2ZSFqvkr7tEgTTuDa+QyOfn+cLhP8nbOfxe/JHWvYKKBD +OwqSswUaKS9aNSYaEtF2H46k6/s74Ty+PfXiSFlTFzfmT0eQmihfEH/EHQk3ZEir +oU0iAIS/nnR+BWCMemeB5KkYp4vvmOkX6cCKc3FA/jFxxsjFNCOpvd1MVKrSKjfB +hxNrhxV8vjdRUzzkb7pZ6lv23qGpDfeP0GX2SLgjB2A/gxPsTf/QdOrSVEAiCgAt +B2OWSGGzAgMBAAECggEAI7mRRXS0HQQjLZmE7kZDhqF8Q9OIf/yZHwo9sPxvLvR1 +z+jP2fQ/g3RixHWqYQ4zU5vw7wQptXb0bzj3NEph/S88Di79jtuGIVppRkDHUMyX +Jr6jDWsyxq22VnILR2jbdHsrQoIj8oe0vjJDkzeDJpWSal1gG69HzSbCQizDys8+ +61CUzs37N8SmFDdb4kLZVkWp2jZ1bO1Wp91uyPV8cb772nw88LR346u6O8pwa/b3 +GeTDZDLFuYdLPltocc0lHx1hMK7WGhQeumboPVSBezyPLTnnGncY4+9elJ0Zgns6 +NevNYUtzN4NdC2EdQogP4iO7VxS76Xi2NPYEyTdS4QKBgQDQIBUCos5wz+7ChTQF +Exgcz4Al38J5NRxESRsUzdXA6poYyayRbSZbGD76DNCyrxoUUOgrS1TB1pqTYso5 +S3jzxdcIC2Ec78ThDrGZ6jG0rFuL3Q9ZHRknfYLJA/jOCfoPIneKN0Bspkhpua8u +Edb4MlE+CK7sEQ3tr9bcp3CFwwKBgQDix2pdJ+oc+KXUsbGQT9pj6mFl7fF13/7Z +HpD4zLZ6teH+vdcJXPJD2OmupxxpzfGdla3/bNZbJQV6qo+lH0P88WsXLC/f330M +tErtt6rWFaGJnIVwvl0EdOcXAt7Eu3ePdlFQFQ34K6y1MUHaSgn1+Xe6hRAr7O17 +yVNoSVXFUQKBgQC5/aGpDGNej4O4VB+L1Wd7/7duoUuC1AKhx48rBVNbpov1UO0l +vRxE1iMZhLBQroio1TDV1+kznJvg0K0VjKvXddrXdovftyoDUgVHHfIzmr/o+rGH +mqhrEQ/6odEMGABe3Bdl7kBlmCgoS2cQzaKFhTnzLQFdN1RyL+zcncnBzwKBgEdb +1H5HpwVCwHuzBlqQpfm2sLswKkCbIUgUtC11zR3Dyb8s4VNS6fCreqy4bNpmMVcS +2FN9Xd4EQjsdHGIq+mvHU5lRECdz3Zd3y/QNTTZcTB5PIDYf0Iksd+Lnxk1FaBO+ +eUuntGOY6pmBTGVx0ryRqTkJ0dLglMHU0egI23JhAoGBAMixQEkfHKv8bdGfFQzV +WkpGrhg+SvAV1GgXKk5gHom3Y3r98z6RNAVklBsZyZzx0m1Py3G+QVYIwk7ZOaKM +Jjd+2HqMoHPjyl8qzTPK80n2vzOUsWzg6T1xubyyMy3IgL8odbwjCbPdmxBUWPnR +H6PfBy+KP4/9tBClxtfZobyw +-----END PRIVATE KEY----- diff --git a/.docker/mock.pem b/.docker/mock.pem new file mode 100644 index 0000000..8c94ca9 --- /dev/null +++ b/.docker/mock.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEGDCCAoCgAwIBAgIRAKee5ztyQXf7oIivaQ64KncwDQYJKoZIhvcNAQELBQAw +ZTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMR0wGwYDVQQLDBRwaGls +QGVnZ3BsYW50IChQaGlsKTEkMCIGA1UEAwwbbWtjZXJ0IHBoaWxAZWdncGxhbnQg +KFBoaWwpMB4XDTIzMDcwNzE5MjA1MloXDTI1MTAwNzE5MjA1MlowSDEnMCUGA1UE +ChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMR0wGwYDVQQLDBRwaGls +QGVnZ3BsYW50IChQaGlsKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALhecfWtlL+EJYfoBXqm1Rlw44BZRVuQgJuI1ZcDnKTQN+NtUVt2tL1+Qr5Zw9wg +6ETZPrvx5zy2W11N4hGmqSA3/I+sspDihvZlIWq+Svu0SBNO4Nr5DI5+f5wuE/yd +s5/F78kda9gooEM7CpKzBRopL1o1JhoS0XYfjqTr+zvhPL499eJIWVMXN+ZPR5Ca +KF8Qf8QdCTdkSKuhTSIAhL+edH4FYIx6Z4HkqRini++Y6RfpwIpzcUD+MXHGyMU0 +I6m93UxUqtIqN8GHE2uHFXy+N1FTPORvulnqW/beoakN94/QZfZIuCMHYD+DE+xN +/9B06tJUQCIKAC0HY5ZIYbMCAwEAAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMGA1Ud +JQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFLwSTf/zvQoUr1zUkwMS4XSNt+Qr +MBYGA1UdEQQPMA2CC3Byb2ZsaWUuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQBsonk5 +aMklWpkVEVO0U1Z2OpI4DmUcfyEGJI+FBrGm5v4NG2KPqn/OQOzfdGqEGqSSd9iS +z2CeDlgk0y8mHYY+KGqqyn9fezjycms6DuNI5tcqe4Blitbtpusut3mz699VXmQl +JQmSGpLVNFyVioA2bl2AhXRfkZkZv8ieZpAO5bCJZnUFzOdMWc1mS6v3wSnUDbKU +MuY2f5jdIPRBSNEFoAAgY7eogu1GbPAtiLz9R+ZnvYH76AJUPIF8RUexsYPdlI7J +WBHKFvcOA4yF6sXuQDXvfCn2ejzYzeiVfcVXMlT4vyUR7KJalikplSyrPtPs/qnU +C9XmccEvNLlyTgV3/aPFM9LVXkpJqNp4Ddb0DgwgjOHSMdO5A/v+tOK83Nhduq+u +63GxiHKcxLsrLQl0J+55Y0XHpyCabd8C70b3g+PFvMk3VU6ObfPjjvP4wi/iYkAa +fbj0eTmKFEirCqxhVf0F/SteZuWV74mTejTC01Gdk2q0v7+UyN8eOb1AczI= +-----END CERTIFICATE----- diff --git a/.gitignore b/.gitignore index d0389c9..341cc65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env /vendor +/logs diff --git a/Dockerfile b/Dockerfile index 308d375..07177fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,8 @@ FROM phpswoole/swoole:latest as dev RUN docker-php-ext-install mysqli pdo_mysql +RUN apt-get update && apt-get install -y certbot + FROM dev as build COPY ./bootstrap /var/www/bootstrap @@ -10,5 +12,9 @@ COPY ./src /var/www/src COPY ./composer.json /var/www/composer.json COPY ./migration.php /var/www/migration.php COPY ./server.php /var/www/server.php +COPY ./letsencrypt /var/www/letsencrypt + +COPY ./.docker/mock.pem /etc/letsencrypt/live/proflie.com/fullchain.pem +COPY ./.docker/mock-key.pem /etc/letsencrypt/live/proflie.com/privkey.pem RUN composer update diff --git a/docker-compose.yml b/docker-compose.yml index 59988bd..8ad6e2d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,9 +16,12 @@ services: - RECAPTCHA_PRIVATE ports: - "80:80" + - "443:443" volumes: - ".:/var/www" + - "./logs:/var/log/proflie/" - "/var/www/mysql-data" + - "/var/www/logs" mysql: restart: always diff --git a/letsencrypt/.well-known/acme-challenge/.gitkeep b/letsencrypt/.well-known/acme-challenge/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server.php b/server.php index 653030a..251206b 100644 --- a/server.php +++ b/server.php @@ -15,14 +15,35 @@ Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL); -$http = new Swoole\Http\Server("0.0.0.0", 80); -$http->set([ +$https = new Swoole\Http\Server("0.0.0.0", 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$http = $https->addListener("0.0.0.0", 80, SWOOLE_SOCK_TCP); + +$https->set([ + 'ssl_cert_file' => '/etc/letsencrypt/live/proflie.com/fullchain.pem', + 'ssl_key_file' => '/etc/letsencrypt/live/proflie.com/privkey.pem', 'log_level' => 0, - 'open_http2_protocol' => true, + //'open_http2_protocol' => true, ]); -$http->on("request", function (Request $request, Response $response) use ($routes, $services, $domainWithoutDotCom) { +$http->on("request", function (Request $request, Response $response) { try { + accessLog($request, $response); + if (letsEncrypt($request, $response)) + return; + + $response->redirect("https://{$request->header['host']}{$request->server['request_uri']}", 302); // todo: switch to 301 permanent redirect + } catch(\Throwable $e) { + var_dump($request); + throw $e; + } +}); + +$https->on("request", function (Request $request, Response $response) use ($routes, $services, $domainWithoutDotCom) { + try { + accessLog($request, $response); + if (letsEncrypt($request, $response)) + return; + if (getStatic($request, $response)) return; @@ -41,8 +62,36 @@ } }); +$https->start(); $http->start(); +function accessLog(Request $request, Response $response) : void +{ + $date = date("c", $request->server['master_time']); +$log = <<< LOG +{$request->server['remote_addr']} - {$request->header['host']} - [$date] - {$request->server['request_method']} {$request->server['request_uri']} - {$request->header['user-agent']}\r\n +LOG; + + file_put_contents("/var/log/proflie/access.log", $log, FILE_APPEND); +} + +function letsEncrypt(Request $request, Response $response): bool +{ + if (substr($request->server['request_uri'], 0, 28) !== "/.well-known/acme-challenge/") { + return false; + } + + $staticFile = __DIR__ . "/letsencrypt/.well-known/acme-challenge/" . substr($request->server['request_uri'], 28); + + if (!file_exists($staticFile)) { + return false; + } + + $response->header("Content-Type", "text/html; charset=utf-8") + $response->sendfile($staticFile); + return true; +} + function getStatic(Request $request, Response $response): bool { $staticFile = __DIR__ . "/src" . $request->server['request_uri']; @@ -54,7 +103,6 @@ function getStatic(Request $request, Response $response): bool return false; }; - $response->header('Content-Type', 'text/javascript'); $response->sendfile($staticFile); return true; } @@ -92,6 +140,5 @@ function getPhp(Request $request, Response $response, array $services, $routes): $response->end(); return true; } - return false; }