feat: Update notification cooldowns and add alerts to index
All checks were successful
Build Docker / BuildImage (push) Successful in 35s

This commit is contained in:
Nathan Woodburn 2024-09-12 18:51:39 +10:00
parent a2e173f777
commit fa78390fc7
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
4 changed files with 81 additions and 28 deletions

View File

@ -1,2 +1,4 @@
# HNSDoH Status
This is a simple webserver to check the status of the Handshake DoH server.
It will check every 5 minutes to see if each node is up and running. It checks the node for plain dns, DNS over HTTPS, and DNS over TLS. For DNS over HTTPS and DNS over TLS, it will check the certificate to make sure it is valid.

View File

@ -56,6 +56,19 @@ if not os.path.exists(log_dir):
os.mkdir("./logs")
log_dir = "./logs"
if not os.path.exists(f"{log_dir}/node_status.json"):
with open(f"{log_dir}/node_status.json", "w") as file:
json.dump([], file)
if not os.path.exists(f"{log_dir}/sent_notifications.json"):
with open(f"{log_dir}/sent_notifications.json", "w") as file:
json.dump({}, file)
else:
with open(f"{log_dir}/sent_notifications.json", "r") as file:
sent_notifications = json.load(file)
print(f"Log directory: {log_dir}", flush=True)
@ -183,7 +196,8 @@ def check_doh(ip: str) -> bool:
finally:
# Close the socket connection
if ssock:
# Check if ssock is defined
if "ssock" in locals():
ssock.close()
return status
@ -330,24 +344,13 @@ def check_nodes() -> list:
node["cert"]["expiry_date"], "%b %d %H:%M:%S %Y GMT"
)
if cert_expiry < datetime.now() + relativedelta.relativedelta(days=7):
if node["ip"] not in sent_notifications:
sent_notifications[node["ip"]] = datetime.now()
send_down_notification(node)
continue
if sent_notifications[node["ip"]] < datetime.now() - relativedelta.relativedelta(days=1):
send_down_notification(node)
continue
cert_853_expiry = datetime.strptime(
node["cert_853"]["expiry_date"], "%b %d %H:%M:%S %Y GMT"
)
if cert_853_expiry < datetime.now() + relativedelta.relativedelta(days=7):
if node["ip"] not in sent_notifications:
sent_notifications[node["ip"]] = datetime.now()
send_down_notification(node)
continue
if sent_notifications[node["ip"]] < datetime.now() - relativedelta.relativedelta(days=1):
send_down_notification(node)
continue
return node_status
def check_nodes_from_log() -> list:
@ -399,6 +402,29 @@ def send_notification(title, description,author):
def send_down_notification(node):
global sent_notifications
# Check if a notification has already been sent
if node["ip"] not in sent_notifications:
sent_notifications[node["ip"]] = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
else:
last_send = datetime.strptime(sent_notifications[node["ip"]], "%Y-%m-%d %H:%M:%S")
if last_send > datetime.now() - relativedelta.relativedelta(hours=1):
print(f"Notification already sent for {node['name']} in the last hr", flush=True)
return
# Only send certain notifications once per day
if node["plain_dns"] and node["doh"] and node["dot"]:
if last_send > datetime.now() - relativedelta.relativedelta(days=1):
print(f"Notification already sent for {node['name']} in the last day", flush=True)
return
# Save the notification to the file
sent_notifications[node["ip"]] = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
with open(f"{log_dir}/sent_notifications.json", "w") as file:
json.dump(sent_notifications, file, indent=4)
title = f"{node['name']} is down"
description = f"{node['name']} ({node['ip']}) is down with the following issues:\n"
@ -641,36 +667,48 @@ def api_refresh():
def index():
node_status = check_nodes_from_log()
alerts = []
warnings = []
for node in node_status:
if not node["plain_dns"]:
warnings.append(f"{node['name']} does not support plain DNS")
alerts.append(f"{node['name']} does not support plain DNS")
if not node["doh"]:
warnings.append(f"{node['name']} does not support DoH")
alerts.append(f"{node['name']} does not support DoH")
if not node["dot"]:
warnings.append(f"{node['name']} does not support DoT")
alerts.append(f"{node['name']} does not support DoT")
if not node["cert"]["valid"]:
warnings.append(f"{node['name']} has an invalid certificate")
alerts.append(f"{node['name']} has an invalid certificate")
if not node["cert_853"]["valid"]:
warnings.append(f"{node['name']} has an invalid certificate on port 853")
alerts.append(f"{node['name']} has an invalid certificate on port 853")
cert_expiry = datetime.strptime(
node["cert"]["expiry_date"], "%b %d %H:%M:%S %Y GMT"
)
if cert_expiry < datetime.now() + relativedelta.relativedelta(days=7):
warnings.append(
f"{node['name']} has a certificate expiring in less than 7 days on port 443"
if cert_expiry < datetime.now():
alerts.append(
f"The {node['name']} node's certificate has expired"
)
continue
elif cert_expiry < datetime.now() + relativedelta.relativedelta(days=7):
warnings.append(
f"The {node['name']} node's certificate is expiring {format_relative_time(cert_expiry)}"
)
continue
cert_853_expiry = datetime.strptime(
node["cert_853"]["expiry_date"], "%b %d %H:%M:%S %Y GMT"
)
if cert_853_expiry < datetime.now() + relativedelta.relativedelta(days=7):
if cert_853_expiry < datetime.now():
alerts.append(
f"The {node['name']} node's certificate has expired for DNS over TLS (port 853)"
)
continue
elif cert_853_expiry < datetime.now() + relativedelta.relativedelta(days=7):
warnings.append(
f"{node['name']} has a certificate expiring in less than 7 days on port 853"
f"The {node['name']} node's certificate is expiring {format_relative_time(cert_853_expiry)} for DNS over TLS (port 853)"
)
@ -698,6 +736,7 @@ def index():
"index.html",
nodes=node_status,
warnings=warnings,
alerts=alerts,
history=history_summary,
last_check=last_check,
)

View File

@ -10,7 +10,7 @@
margin: 25px;
display: block;
}
.warnings {
.warnings,.errors {
margin: auto;
width: 1000px;
max-width: 95%;

View File

@ -51,10 +51,22 @@
<section id="intro">
<div class="text-center">
<h1 class="text-center" style="font-size: 60px;">HNS DoH Status</h1>
<div class="errors">
<!-- Check if errors is empty -->
{% if alerts %}
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Alert</h4>
{% for alert in alerts %}
<p>{{ alert }}</p>
{% endfor %}
</div>
{% endif %}
</div>
<div class="warnings">
<!-- Check if warnings is empty -->
{% if warnings %}
<div class="alert alert-danger" role="alert">
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>
{% for warning in warnings %}
<p>{{ warning }}</p>