From e6c85b81e51019e88c0044c2dafaeb0dabd7ee39 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Tue, 12 Mar 2024 15:51:50 +0100 Subject: [PATCH] Fixes to ssl/tls, misc improvements * Use the PHP context options to configure tls rather than reinventing the wheel. * Properly setup the SocketServer for ssl * Added generic getter for config values --- bin/mercureactd | 5 +++-- mercureactd.conf.dist | 9 +++++---- src/Configuration.php | 18 ++++++++++++++++++ src/Daemon.php | 17 ++++++++++++++++- src/Http/Server.php | 2 +- tests/Broker/TopicTest.php | 4 ++++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/bin/mercureactd b/bin/mercureactd index b22fee3..96d797e 100755 --- a/bin/mercureactd +++ b/bin/mercureactd @@ -56,8 +56,9 @@ if (isset($opts['C'])) { allow_origin: '*' csp: "default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'" encryption: - cert: foo.pem - key: foo.key + local_cert: ~ + local_pk: ~ + passphrase: ~ publish: overwrite_ids: false diff --git a/mercureactd.conf.dist b/mercureactd.conf.dist index a93c7c7..e73b512 100644 --- a/mercureactd.conf.dist +++ b/mercureactd.conf.dist @@ -14,10 +14,11 @@ server: # Content-Security-Policy csp: "default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'" - # Setup encryption - encryption: - cert: foo.pem - key: foo.key + # Setup encryption context, see PHPs TLS context options + #encryption: + #local_cert: foo.pem + #locak_pk: foo.key + #passphrase: somepassphrase publish: # Assign a UUID to published messages even if one is already set in the message diff --git a/src/Configuration.php b/src/Configuration.php index 1c65b22..33931ed 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -41,6 +41,24 @@ class Configuration return $config; } + /** + * Retrieve a key from the configuration. If the key ends with a dot, all values + * with keys starting with the requested key are returned. If the key is a single + * dot, all keys are matched and all values returned. + * + * @param string $key + * @param mixed $default Default value + * @return mixed + */ + public function get(string $key, $default = null): mixed + { + if ($key === '.') + return $this->config; + if (str_ends_with($key, ".")) + return array_filter($this->config, fn($k)=>str_starts_with($k,$key), ARRAY_FILTER_USE_KEY); + return $this->config[$key] ?? $default; + } + public function setPublicUrl(string $publicUrl): self { $this->config['server.public_url'] = $publicUrl; diff --git a/src/Daemon.php b/src/Daemon.php index 0d00ba4..5c4ca16 100644 --- a/src/Daemon.php +++ b/src/Daemon.php @@ -9,6 +9,7 @@ use NoccyLabs\Mercureact\Http\Server; use Psr\Log\LoggerInterface; use React\EventLoop\Loop; use React\EventLoop\LoopInterface; +use React\Socket\SecureServer; use React\Socket\SocketServer; class Daemon @@ -48,7 +49,21 @@ class Daemon $this->logger->warning("Empty listening address. You won't make it far."); return; } - $socket = new SocketServer("tcp://".$listenAddress); + + $certificate = $this->config->get("server.encryption.local_cert"); + + if ($certificate) { + $this->logger->info("Using local cert: {$certificate}"); + $context = [ + 'tls' => $this->config->get("server.encryption.") + ]; + $uri = 'tls://' . $listenAddress; + } else { + $uri = 'tcp://' . $listenAddress; + } + + $socket = new SocketServer($uri, $context??[]); + $this->server->listen($socket); } diff --git a/src/Http/Server.php b/src/Http/Server.php index 0d523ad..6687c8e 100644 --- a/src/Http/Server.php +++ b/src/Http/Server.php @@ -80,7 +80,7 @@ class Server $this->server->listen($socket); $this->logger->info(sprintf( "Listening on %s", - str_replace("tcp://",($socket instanceof SecureServer?"https://":"http://"),$socket->getAddress()) + strtr($socket->getAddress(), [ "tcp://"=>"http://", "tls://"=>"https://"]) )); } diff --git a/tests/Broker/TopicTest.php b/tests/Broker/TopicTest.php index 05803af..39c6a6e 100644 --- a/tests/Broker/TopicTest.php +++ b/tests/Broker/TopicTest.php @@ -15,12 +15,14 @@ class TopicTest extends \PHPUnit\Framework\TestCase public function isAuthorized():bool { return true; } public function deliver(Message $message):void { $this->messages[] = $message; } public function getPayload(): ?array { return null; } + public function getId(): string { return ""; } }; $unauthorizedSubscriber = new class implements SubscriberInterface { public array $messages = []; public function isAuthorized():bool { return false; } public function deliver(Message $message):void { $this->messages[] = $message; } public function getPayload(): ?array { return null; } + public function getId(): string { return ""; } }; $topic = new Topic("foo"); @@ -41,12 +43,14 @@ class TopicTest extends \PHPUnit\Framework\TestCase public function isAuthorized():bool { return true; } public function deliver(Message $message):void { $this->messages[] = $message; } public function getPayload(): ?array { return null; } + public function getId(): string { return ""; } }; $unauthorizedSubscriber = new class implements SubscriberInterface { public array $messages = []; public function isAuthorized():bool { return false; } public function deliver(Message $message):void { $this->messages[] = $message; } public function getPayload(): ?array { return null; } + public function getId(): string { return ""; } }; $topic = new Topic("foo");