Initial commit
This commit is contained in:
commit
c3f8908cb5
8 changed files with 660 additions and 0 deletions
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Runtime data
|
||||||
|
/data/
|
||||||
|
*.db
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
config/config.php
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# OS / editor
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
34
Dockerfile
Normal file
34
Dockerfile
Normal file
|
|
@ -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
|
||||||
67
check_ssl.php
Normal file
67
check_ssl.php
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
$db = new PDO("sqlite:/var/www/data/ssl.db");
|
||||||
|
$db->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";
|
||||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
|
|
@ -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
|
||||||
77
domains.txt
Normal file
77
domains.txt
Normal file
|
|
@ -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
|
||||||
235
domains_ungueltig.txt
Normal file
235
domains_ungueltig.txt
Normal file
|
|
@ -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
|
||||||
161
history.php
Normal file
161
history.php
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
<?php
|
||||||
|
$db = new PDO("sqlite:/var/www/data/ssl.db");
|
||||||
|
|
||||||
|
$domain = $_GET["domain"] ?? "";
|
||||||
|
if (!$domain) die("Domain missing");
|
||||||
|
|
||||||
|
$stmt = $db->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);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SSL History – <?= htmlspecialchars($domain) ?></title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f4f4f4;
|
||||||
|
--text: #000;
|
||||||
|
--card: #fff;
|
||||||
|
--border: #ddd;
|
||||||
|
--header: #222;
|
||||||
|
--link: #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
--bg: #121212;
|
||||||
|
--text: #e0e0e0;
|
||||||
|
--card: #1e1e1e;
|
||||||
|
--border: #333;
|
||||||
|
--header: #000;
|
||||||
|
--link: #4ea3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 80%;
|
||||||
|
margin: auto;
|
||||||
|
background: var(--card);
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: var(--header);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok { color: #4caf50; font-weight: bold; }
|
||||||
|
.warn { color: #ff9800; font-weight: bold; }
|
||||||
|
.critical { color: #f44336; font-weight: bold; }
|
||||||
|
|
||||||
|
.toggle {
|
||||||
|
position: fixed;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body { font-family: Arial; background:#f4f4f4; text-align:center }
|
||||||
|
canvas { background:#fff; padding:20px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="toggle" onclick="toggleDark()">🌙</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function applyTheme() {
|
||||||
|
const dark = localStorage.getItem("darkmode") === "1";
|
||||||
|
document.body.classList.toggle("dark", dark);
|
||||||
|
document.querySelector(".toggle").textContent = dark ? "☀️" : "🌙";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDark() {
|
||||||
|
localStorage.setItem(
|
||||||
|
"darkmode",
|
||||||
|
localStorage.getItem("darkmode") === "1" ? "0" : "1"
|
||||||
|
);
|
||||||
|
applyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTheme();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h2>Certificate History: <?= htmlspecialchars($domain) ?></h2>
|
||||||
|
|
||||||
|
<canvas id="chart" width="900" height="400"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
new Chart(document.getElementById('chart'), {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?= json_encode($labels) ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Days until expiration',
|
||||||
|
data: <?= json_encode($values) ?>,
|
||||||
|
borderColor: getComputedStyle(document.body)
|
||||||
|
.getPropertyValue('--link'),
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
tension: 0.1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: { ticks: { color: getComputedStyle(document.body).color } },
|
||||||
|
y: { ticks: { color: getComputedStyle(document.body).color } }
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: getComputedStyle(document.body).color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
title: { display: true, text: 'Days Remaining' }
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
title: { display: true, text: 'Check Time' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p><a href="/">← Back</a></p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
54
index.php
Normal file
54
index.php
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
$db = new PDO("sqlite:/var/www/data/ssl.db");
|
||||||
|
$rows = $db->query("SELECT * FROM certs ORDER BY expires ASC")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SSL Monitor</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial; background:#f4f4f4 }
|
||||||
|
table { border-collapse:collapse; width:80%; margin:auto; background:#fff }
|
||||||
|
th,td { padding:10px; border:1px solid #ddd; text-align:center }
|
||||||
|
th { background:#222; color:#fff }
|
||||||
|
.ok { color:green; font-weight:bold }
|
||||||
|
.warn { color:orange; font-weight:bold }
|
||||||
|
.critical { color:red; font-weight:bold }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2 style="text-align:center">SSL Certificate Monitor</h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Domain</th>
|
||||||
|
<th>Expires</th>
|
||||||
|
<th>Days Left</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Last Check</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<?php foreach ($rows as $r):
|
||||||
|
if (!$r["expires"]) {
|
||||||
|
echo "<tr><td>{$r['domain']}</td><td colspan=4 class='critical'>{$r['error']}</td></tr>";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$days = floor(($r["expires"] - time()) / 86400);
|
||||||
|
if ($days > 30) $c="ok";
|
||||||
|
elseif ($days > 7) $c="warn";
|
||||||
|
else $c="critical";
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($r["domain"]) ?></td>
|
||||||
|
<td><?= date("Y-m-d H:i:s", $r["expires"]) ?></td>
|
||||||
|
<td><?= $days ?></td>
|
||||||
|
<td class="<?= $c ?>"><?= strtoupper($c) ?></td>
|
||||||
|
<td><?= date("Y-m-d H:i:s", $r["checked_at"]) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue