diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/Logger.php | 105 | ||||
-rwxr-xr-x | src/ssl_expire_notifier.php | 73 |
2 files changed, 178 insertions, 0 deletions
diff --git a/src/lib/Logger.php b/src/lib/Logger.php new file mode 100644 index 0000000..bf86133 --- /dev/null +++ b/src/lib/Logger.php @@ -0,0 +1,105 @@ +<?php + +class Logger { + + const DEBUG = 0; + const INFO = 1; + const WARNING = 2; + const ERROR = 3; + const FATAL = 4; + + protected static array $levelColors = [ + self::INFO => 34, + self::WARNING => 33, + self::ERROR => 31, + self::FATAL => 91, + ]; + + protected static array $levelEmojis = [ + self::INFO => 'ℹ️', + self::WARNING => '⚠️', + self::ERROR => '‼️', + self::FATAL => '⚡️' + ]; + + protected string $domain; + + public function __construct(string $domain) { + $this->domain = $domain; + } + + protected function stderr(string $message, $color = null) { + $fmt = "[%s] %s"; + if (is_int($color)) + $fmt = "\033[{$color}m$fmt\033[0m"; + $fmt .= "\n"; + $message = strip_tags($message); + fprintf(STDERR, $fmt, $this->domain, $message); + } + + protected function telegram(string $message) { + global $config; + + $url = 'https://api.telegram.org/bot'.$config['telegram_token'].'/sendMessage'; + $query_content = http_build_query([ + 'chat_id' => $config['telegram_chat_id'], + 'text' => $message, + 'parse_mode' => 'html' + ]); + + $ctx = stream_context_create([ + 'http' => [ + 'header' => [ + 'Content-type: application/x-www-form-urlencoded', + 'Content-Length: '.strlen($query_content) + ], + 'method' => 'POST', + 'content' => $query_content + ] + ]); + + $fp = @fopen($url, 'r', false, $ctx); + if ($fp === false) { + $this->stderr("fopen failed"); + return; + } + + $result = stream_get_contents($fp); + fclose($fp); + + $result = json_decode($result, true); + if (!$result['ok']) + $this->stderr("telegram did not OK"); + } + + protected function report(int $level, string $message) { + global $config; + + if ($config['verbose']) + $this->stderr($message, self::$levelColors[$level] ?? null); + + if ($level != self::DEBUG && ($config['telegram_enabled'] ?? 1) == 1) + $this->telegram(self::$levelEmojis[$level].' '.$this->domain.': '.$message); + } + + public function debug(string $message) { + $this->report(self::DEBUG, $message); + } + + public function info(string $message) { + $this->report(self::INFO, $message); + } + + public function warn(string $message) { + $this->report(self::WARNING, $message); + } + + public function error(string $message) { + $this->report(self::ERROR, $message); + } + + public function fatal(string $message) { + $this->report(self::FATAL, $message); + } + +}
\ No newline at end of file diff --git a/src/ssl_expire_notifier.php b/src/ssl_expire_notifier.php new file mode 100755 index 0000000..e6549e6 --- /dev/null +++ b/src/ssl_expire_notifier.php @@ -0,0 +1,73 @@ +#!/usr/bin/env php +<?php + +require_once __DIR__.'/lib/Logger.php'; + +error_reporting(E_ALL); +ini_set('display_errors', 1); + +$file = getenv('HOME').'/.config/ssl_expire_notifier.ini'; +if (!file_exists($file)) + die('ERROR: config '.$file.' not found'); + +$config = parse_ini_file($file); + +function ssl_expire_notifier() { + global $config; + $now = time(); + + foreach ($config['hosts'] as $host) { + $logger = new Logger($host); + if (($pos = strpos($host, ':')) !== false) { + $port = substr($host, $pos+1); + if (!is_numeric($port)) { + $logger->error("failed to parse host"); + continue; + } + $host = substr($host, 0, $pos); + } else { + $port = 443; + } + + $ipv4 = gethostbyname($host); + if (!$ipv4 || $ipv4 == $host) { + $logger->error("failed to resolve"); + continue; + } + + $logger->debug("resolved to $ipv4"); + + $get = stream_context_create([ + 'ssl' => [ + 'capture_peer_cert' => true, + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true, + 'verify_depth' => 0, + ] + ]); + $read = stream_socket_client('ssl://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get); + $cert = stream_context_get_params($read); + $cert_info = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); + + $valid_till = $cert_info['validTo_time_t']; + $logger->debug("valid till ".date('d.m.Y, H:i:s', $valid_till)); + + if ($valid_till <= $now) { + $logger->fatal('already expired at '.date('d.m.Y, H:i:s', $valid_till)); + } else { + $method = null; + if ($valid_till-$now < 86400*$config['error_days']) + $method = 'error'; + else if ($valid_till-$now < 86400*$config['warn_days']) + $method = 'warn'; + + if ($method !== null) + call_user_func([$logger, $method], "expires at ".date('d.m.Y, H:i:s', $valid_till)); + else + $logger->debug('ok'); + } + } +} + +ssl_expire_notifier();
\ No newline at end of file |