From c3f8908cb5c6df436869ac9020f8adfe7342e598 Mon Sep 17 00:00:00 2001 From: brutho Date: Sun, 11 Jan 2026 17:25:10 +0100 Subject: [PATCH] Initial commit --- .gitignore | 15 +++ Dockerfile | 34 ++++++ check_ssl.php | 67 ++++++++++++ docker-compose.yml | 17 +++ domains.txt | 77 ++++++++++++++ domains_ungueltig.txt | 235 ++++++++++++++++++++++++++++++++++++++++++ history.php | 161 +++++++++++++++++++++++++++++ index.php | 54 ++++++++++ 8 files changed, 660 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 check_ssl.php create mode 100644 docker-compose.yml create mode 100644 domains.txt create mode 100644 domains_ungueltig.txt create mode 100644 history.php create mode 100644 index.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89979af --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Runtime data +/data/ +*.db + +# Secrets +config/config.php +.env + +# Logs +*.log + +# OS / editor +.DS_Store +.idea/ +.vscode/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..02af4aa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM php:8.2-apache + +# Install required system packages +RUN apt-get update && apt-get install -y \ + cron \ + sqlite3 \ + libsqlite3-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# Install PHP extensions +RUN docker-php-ext-install pdo pdo_sqlite + +# Enable Apache rewrite (optional) +RUN a2enmod rewrite + +# Copy application +COPY index.php /var/www/html/index.php +# COPY history.php /var/www/html/history.php +COPY check_ssl.php /var/www/check_ssl.php +COPY domains.txt /var/www/domains.txt + +# Create data directory +RUN mkdir -p /var/www/data \ + && chown -R www-data:www-data /var/www + +# Cron job (every 6 hours) +RUN echo "0 */6 * * * php /var/www/check_ssl.php >> /var/log/cron.log 2>&1" \ + > /etc/cron.d/ssl \ + && chmod 0644 /etc/cron.d/ssl \ + && crontab /etc/cron.d/ssl + +# Start cron + Apache +CMD cron && apache2-foreground diff --git a/check_ssl.php b/check_ssl.php new file mode 100644 index 0000000..80e7090 --- /dev/null +++ b/check_ssl.php @@ -0,0 +1,67 @@ +exec(" +CREATE TABLE IF NOT EXISTS certs ( + domain TEXT PRIMARY KEY, + expires INTEGER, + checked_at INTEGER, + error TEXT +)"); + +$domains = file("/var/www/domains.txt", FILE_IGNORE_NEW_LINES); + +function check_ssl($domain) { + $ctx = stream_context_create([ + "socket" => ["bindto" => "0.0.0.0:0"], + "ssl" => [ + "capture_peer_cert" => true, + "verify_peer" => false, + "verify_peer_name" => false, + "SNI_enabled" => true, + "peer_name" => $domain + ] + ]); + + $client = @stream_socket_client( + "ssl://$domain:443", + $errno, + $errstr, + 5, + STREAM_CLIENT_CONNECT, + $ctx + ); + + if (!$client) return [null, $errstr]; + + $params = stream_context_get_params($client); + if (!isset($params["options"]["ssl"]["peer_certificate"])) { + return [null, "No certificate"]; + } + + $cert = openssl_x509_parse($params["options"]["ssl"]["peer_certificate"]); + return [$cert["validTo_time_t"] ?? null, null]; +} + +foreach ($domains as $domain) { + [$expiry, $error] = check_ssl($domain); + + $stmt = $db->prepare(" + INSERT INTO certs(domain, expires, checked_at, error) + VALUES(:d,:e,:c,:er) + ON CONFLICT(domain) DO UPDATE SET + expires=:e, checked_at=:c, error=:er + "); + + $stmt->execute([ + ":d" => $domain, + ":e" => $expiry, + ":c" => time(), + ":er" => $error + ]); + + usleep(200000); // anti-rate-limit (0.2s) +} + +echo "SSL check completed\n"; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d279e02 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.9" + +services: + ssl-monitor: + image: ssl-monitor:latest + container_name: ssl-monitor + build: . + ports: + - "8080:80" + volumes: + - ./data:/app/data + - ./config:/app/config + environment: + DB_PATH: /app/data/ssl.db + ALERT_DAYS: 14 + ADMIN_EMAIL: admin@example.com + restart: unless-stopped diff --git a/domains.txt b/domains.txt new file mode 100644 index 0000000..c43157a --- /dev/null +++ b/domains.txt @@ -0,0 +1,77 @@ +cms-pews.geosphere.at +cms-vis-pews.geosphere.at +pews.geosphere.at +hub-pews.geosphere.at +cobs.geosphere.at +swap.geosphere.at +portale.geosphere.at +edrop.geosphere.at +taguing.geosphere.at +appshiny.geosphere.at +eshiny-proxy.geosphere.at +klimaportal.geosphere.at +gitlab.geosphere.at +projects.gitlab.geosphere.at +4cast.gitlab.geosphere.at +ieatask51-austria.gitlab.geosphere.at +atmoi4ren-4cast.gitlab.geosphere.at +www.gitlab.geosphere.at +wmo.gitlab.geosphere.at +energyprotect.gitlab.geosphere.at +projectpages.geosphere.at +risklab.geosphere.at +mapi.geosphere.at +autodiscover.geosphere.at +webmail.geosphere.at +eva.geosphere.at +self-service.geosphere.at +vpn.geosphere.at +www.geosphere.at +eduid.geosphere.at +maintenance.hub.geosphere.at +metadata.hub.geosphere.at +datenzentrum.geosphere.at +s3.hub.geosphere.at +station.api.hub.geosphere.at +keycloak.hub.geosphere.at +data.hub.geosphere.at +frontend.dataset.api.staging.hub.geosphere.at +s3direct.hub.geosphere.at +dataset.api.hub.geosphere.at +thredds.staging.hub.geosphere.at +metadata.form.hub.geosphere.at +ckan.metadata.hub.geosphere.at +datenzentrum.hub.geosphere.at +www.dataset.api.hub.geosphere.at +openapi.hub.geosphere.at +s3.staging.hub.geosphere.at +keycloak.staging.hub.geosphere.at +thredds.hub.geosphere.at +public.hub.geosphere.at +mapping.api.staging.hub.geosphere.at +iris.geosphere.at +catalog.geosphere.at +geothermieatlas.geosphere.at +meldung-geodaten.geosphere.at +maps.geosphere.at +resource.geosphere.at +ardigeos.geosphere.at +gis.geosphere.at +smw.geosphere.at +webstat.geosphere.at +oculus.geosphere.at +coturn.geosphere.at +signaling.geosphere.at +recording.geosphere.at +rhea.geosphere.at +rocky-austria.geosphere.at +bibliothek.geosphere.at +bibliothek-admin.geosphere.at +bibliothek-test-admin.geosphere.at +bibliothek-test.geosphere.at +gitea.geosphere.at +jobs.geosphere.at +thesaurus.geolba.ac.at +resource.geolba.ac.at +gemas.geolba.ac.at +catalog.zamg.ac.at diff --git a/domains_ungueltig.txt b/domains_ungueltig.txt new file mode 100644 index 0000000..e733862 --- /dev/null +++ b/domains_ungueltig.txt @@ -0,0 +1,235 @@ +eon2.geosphere.at +keycloak.production.hub-dev.geosphere.at +k8s-prod1.geosphere.at +zaaspure1.geosphere.at +mx1mgmt.geosphere.at +vsents5.geosphere.at +zabbix.geosphere.at +connect.geosphere.at +www.maps.geosphere.at +www.testmattermost.geosphere.at +manual.k8s-review.geosphere.at +status.staging.hub-dev.geosphere.at +www.self-service.geosphere.at +www.resource.geosphere.at +data.staging.hub-dev.geosphere.at +metadata.staging.hub-dev.geosphere.at +backend-test.k8s-dev.geosphere.at +jupyterhub.k8s-dev.geosphere.at +keycloak.dhr.geosphere.at +ise4.geosphere.at +testgitlab.geosphere.at +146www.geosphere.at +foobartest123.geosphere.at +enexyohub-dev.geosphere.at +argocd.k8s-dev.geosphere.at +ckan.metadata.staging.hub-dev.geosphere.at +ise1.geosphere.at +s3.hub-infra.geosphere.at +www.myserver.geosphere.at +klimaportal-dev.geosphere.at +prod.hub-dev.geosphere.at +openapi.staging.hub-dev.geosphere.at +ionbe1502ilom.geosphere.at +gsasmtp.geosphere.at +metadata.form.staging.hub-dev.geosphere.at +my-test.whatever.geosphere.at +hub-staging.geosphere.at +keycloak.hub.staging.geosphere.at +testurl-rancher.geosphere.at +ise3.geosphere.at +www.webmail.geosphere.at +ise.geosphere.at +climate-indicators.geosphere.at +plausible.infra.hub-dev.geosphere.at +www.enexyohub-dev.geosphere.at +argocd.hub-infra.geosphere.at +vsents1.geosphere.at +grafana.k8s-prod1.geosphere.at +interflex.geosphere.at +dataset.api.staging.hub-dev.geosphere.at +grafana.k8s-staging1.geosphere.at +radius2.geosphere.at +k8s-staging1.geosphere.at +www-dev.geosphere.at +s3.hub.staging.geosphere.at +www.harbor.geosphere.at +enimbusd.geosphere.at +tagui.geosphere.at +vsenttest1.geosphere.at +k8s-utility1.geosphere.at +prometheus.k8s-prod1.geosphere.at +ionbe1503ilom.geosphere.at +www.signaling.geosphere.at +www-dev2.geosphere.at +rancher-dev.geosphere.at +vsents2b-l1c.geosphere.at +www.umwanalyse.geosphere.at +www.geothermieatlas.geosphere.at +satosa.k8s-dev.geosphere.at +k8s-staging.geosphere.at +vsentkeycloak.geosphere.at +s3.netapp.geosphere.at +www.nessus.geosphere.at +www.gsasmtp.geosphere.at +www.enimbusd.geosphere.at +plone-backend.k8s-staging1.geosphere.at +public.staging.hub-dev.geosphere.at +staging.hub-dev.geosphere.at +www.interflex.geosphere.at +harbor-dev.k8s-dev.geosphere.at +www.iot.geosphere.at +mapping.api.hub.staging.geosphere.at +thredds.production.hub-dev.geosphere.at +test.geosphere.at +www.prod.hub-dev.geosphere.at +www.www-dev.geosphere.at +rsa2.geosphere.at +wlc1mgmt.geosphere.at +maintenance-staging.geosphere.at +ise1mgmt.geosphere.at +uncached.k8s-prod1.geosphere.at +ewww-dev.geosphere.at +ipam.geosphere.at +plausible.staging.hub-dev.geosphere.at +wlc2mgmt.geosphere.at +www.www.testseite.geosphere.at +rsa1.geosphere.at +www.k8s-prod1.geosphere.at +dataset.api.production.hub-dev.geosphere.at +metadata.form.production.hub-dev.geosphere.at +semaphore.geosphere.at +www.climate-indicators.geosphere.at +foreman.k8s-dev.geosphere.at +ise2.geosphere.at +nessus.geosphere.at +kafka-ui.k8s-dev.geosphere.at +dhr-test.geosphere.at +atom.rancher.geosphere.at +www.enexyohub.geosphere.at +datenzentrum-staging.geosphere.at + +geprüft: +jobs.geosphere.at +ftp.geosphere.at +go.geosphere.at +cms-pews-test.geosphere.at +cms-vis-pews-test.geosphere.at +pews-test.geosphere.at +sftp.geosphere.at +mx1.geosphere.at +mx2.geosphere.at +pep725.geosphere.at +adfs.geosphere.at +eva-dev.geosphere.at +iot.geosphere.at +ns1.geosphere.at +ns2.geosphere.at +vdi.geosphere.at +beta.geosphere.at +hub.geosphere.at +api.hub.geosphere.at +dataset.api.staging.hub.geosphere.at +www.klimaportal-dev.geosphere.at +testurl-foo-rancher.geosphere.at +keycloak.staging.hub-dev.geosphere.at +vsents2a-l1c.geosphere.at +radius3.geosphere.at +harbor.geosphere.at +datenzentrum.staging.hub-dev.geosphere.at +k8s-review.geosphere.at +hub-infra.geosphere.at +openapi.production.hub-dev.geosphere.at +s3.production.hub-dev.geosphere.at +www2-dev.geosphere.at +bitwarden.geosphere.at +www.staging.hub-dev.geosphere.at +umwanalyse.geosphere.at +eeva.geosphere.at +www.dhr.geosphere.at +vsents3.geosphere.at +vcharbor.geosphere.at +www.testseite.geosphere.at +epep725.geosphere.at +cached-dev.k8s-dev.geosphere.at +wlan.geosphere.at +www.testgitlab.geosphere.at +monitoring.hub-infra.geosphere.at +dafne.dhr.geosphere.at +www.datenzentrum-staging.geosphere.at +gw.k8s-review.geosphere.at +mattermost.geosphere.at +www.recording.geosphere.at +www.infra.hub-dev.geosphere.at +quarantine.geosphere.at +www.dhr-test.geosphere.at +ionbe1501ilom.geosphere.at +maintenance.production.hub-dev.geosphere.at +cached-dev.k8s-prod1.geosphere.at +testseite.geosphere.at +eeva-dev.geosphere.at +plausible.hub-infra.geosphere.at +s3.infra.hub-dev.geosphere.at +spf.geosphere.at +thredds.hub.staging.geosphere.at +vnessus01.geosphere.at +api.eon2.geosphere.at +vsents2-l2a.geosphere.at +pdp.geosphere.at +sentinel.geosphere.at +www.sentinel.geosphere.at +data.production.hub-dev.geosphere.at +metadata.production.hub-dev.geosphere.at +cached-dev.k8s-staging1.geosphere.at +argocd.infra.hub-dev.geosphere.at +uncached.k8s-staging1.geosphere.at +station.api.staging.hub-dev.geosphere.at +maintenance.staging.hub-dev.geosphere.at +www.test.geosphere.at +dafne-backend.dhr.geosphere.at +cpm.geosphere.at +sma1.geosphere.at +mx2mgmt.geosphere.at +vsenttest.geosphere.at +www.go.geosphere.at +radius1.geosphere.at +cert-test.k8s-review.geosphere.at +www.rsa2.geosphere.at +loki.atom.rancher.geosphere.at +ise2mgmt.geosphere.at +prometheus.k8s-staging1.geosphere.at +ckan.metadata.hub.staging.geosphere.at +s3.staging.hub-dev.geosphere.at +grafana.atom.rancher.geosphere.at +grafana.k8s-dev.geosphere.at +plone-backend.k8s-prod1.geosphere.at +dhr.geosphere.at +enexyohub.geosphere.at +cps2.geosphere.at +maintenance.geosphere.at +dataset.api.hub.staging.geosphere.at +myserver.geosphere.at +plone-backend.k8s-dev.geosphere.at +dafne.geosphere.at +www.rsa1.geosphere.at +trivy.k8s-dev.geosphere.at +thredds.staging.hub-dev.geosphere.at +station.api.production.hub-dev.geosphere.at +vsentgss.geosphere.at +cached.k8s-dev.geosphere.at +www.mattermost.geosphere.at +dependency-track.k8s-dev.geosphere.at +www.harbor-dev.k8s-dev.geosphere.at +ise3mgmt.geosphere.at +ckan.metadata.production.hub-dev.geosphere.at +rancher.geosphere.at +dafne-backend.geosphere.at +testmattermost.geosphere.at +infra.hub-dev.geosphere.at +irgendwas.geosphere.at +cps1.geosphere.at +public.production.hub-dev.geosphere.at +itop.geosphere.at +wifi.geosphere.at +hub-pews-test.geosphere.at +rocky.geosphere.at diff --git a/history.php b/history.php new file mode 100644 index 0000000..5e1d9ab --- /dev/null +++ b/history.php @@ -0,0 +1,161 @@ +prepare(" + SELECT checked_at, expires + FROM history + WHERE domain = :d + ORDER BY checked_at ASC +"); +$stmt->execute([":d" => $domain]); +$data = $stmt->fetchAll(PDO::FETCH_ASSOC); + +$labels = []; +$values = []; + +foreach ($data as $row) { + $labels[] = date("Y-m-d H:i", $row["checked_at"]); + $values[] = round(($row["expires"] - $row["checked_at"]) / 86400, 1); +} +?> + + + +SSL History – <?= htmlspecialchars($domain) ?> + + + + +
🌙
+ + + +

Certificate History:

+ + + + + +

← Back

+ + + diff --git a/index.php b/index.php new file mode 100644 index 0000000..e7e0480 --- /dev/null +++ b/index.php @@ -0,0 +1,54 @@ +query("SELECT * FROM certs ORDER BY expires ASC")->fetchAll(PDO::FETCH_ASSOC); +?> + + + +SSL Monitor + + + + +

SSL Certificate Monitor

+ + + + + + + + + + +"; + continue; + } + + $days = floor(($r["expires"] - time()) / 86400); + if ($days > 30) $c="ok"; + elseif ($days > 7) $c="warn"; + else $c="critical"; +?> + + + + + + + + + +
DomainExpiresDays LeftStatusLast Check
{$r['domain']}{$r['error']}
+ +