From 45a6d27c8ba29e32d23e7517f34ba98deca828c6 Mon Sep 17 00:00:00 2001 From: Iain Learmonth Date: Wed, 20 Apr 2022 15:34:11 +0100 Subject: [PATCH] alarms: integrate ooni alarms --- app/models.py | 2 +- app/portal/templates/alarms.html.j2 | 8 ++- app/terraform/block_ooni.py | 79 +++++++++++++++++++++++++++++ app/terraform/proxy_check.py | 1 + 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 app/terraform/block_ooni.py diff --git a/app/models.py b/app/models.py index 5ed6eaf..c3ef0c0 100644 --- a/app/models.py +++ b/app/models.py @@ -172,7 +172,7 @@ class Alarm(db.Model): bridge = db.relationship("Bridge", back_populates="alarms") def update_state(self, state: AlarmState, text: str): - if self.state != state: + if self.alarm_state != state: self.state_changed = datetime.utcnow() self.alarm_state = state self.text = text diff --git a/app/portal/templates/alarms.html.j2 b/app/portal/templates/alarms.html.j2 index b039b70..362cdbe 100644 --- a/app/portal/templates/alarms.html.j2 +++ b/app/portal/templates/alarms.html.j2 @@ -3,7 +3,7 @@ {% block content %}

Alarms

-

Proxies

+
@@ -11,6 +11,8 @@ + + @@ -18,11 +20,15 @@ {% if alarm.target == "proxy" %} + {% elif alarm.target == "origin" %} + {% elif alarm.target == "service/cloudfront" %} {% endif %} + + {% endfor %} diff --git a/app/terraform/block_ooni.py b/app/terraform/block_ooni.py new file mode 100644 index 0000000..b1d3834 --- /dev/null +++ b/app/terraform/block_ooni.py @@ -0,0 +1,79 @@ +from collections import defaultdict +from datetime import datetime +from datetime import timedelta +from typing import Dict + +import requests + +from app import app +from app.extensions import db +from app.models import Origin, AlarmState, Alarm + + +def check_origin(domain_name: str): + start_date = (datetime.utcnow() - timedelta(days=2)).strftime("%Y-%m-%dT%H%%3A%M") + end_date = datetime.utcnow().strftime("%Y-%m-%dT%H%%3A%M") + api_url = f"https://api.ooni.io/api/v1/measurements?domain={domain_name}&since={start_date}&until={end_date}" + result = defaultdict(lambda: {"anomaly": 0, "confirmed": 0, "failure": 0, "ok": 0}) + return _check_origin(api_url, result) + + +def _check_origin(api_url: str, result: Dict): + print(f"Processing {api_url}") + req = requests.get(api_url).json() + if not req['results']: + return result + for r in req['results']: + not_ok = False + for s in ["anomaly", "confirmed", "failure"]: + if s in r and r[s]: + result[r["probe_cc"]][s] += 1 + not_ok = True + break + if not not_ok: + result[r["probe_cc"]]["ok"] += 1 + if req['metadata']['next_url']: + return _check_origin(req['metadata']['next_url'], result) + return result + + +def threshold_origin(domain_name): + ooni = check_origin(domain_name) + for country in ooni: + total = sum([ + ooni[country]["anomaly"], + ooni[country]["confirmed"], + ooni[country]["failure"], + ooni[country]["ok"] + ]) + total_blocks = sum([ + ooni[country]["anomaly"], + ooni[country]["confirmed"] + ]) + block_perc = round((total_blocks / total * 100), 1) + ooni[country]["block_perc"] = block_perc + ooni[country]["state"] = AlarmState.CRITICAL if block_perc > 20 else AlarmState.OK + ooni[country]["message"] = f"Blocked in {block_perc}% of measurements" + return ooni + + +def set_ooni_alarm(origin_id: int, country: str, state: AlarmState, text: str): + alarm = Alarm.query.filter( + Alarm.origin_id == origin_id, + Alarm.alarm_type == f"origin-block-ooni-{country}" + ).first() + if alarm is None: + alarm = Alarm() + alarm.origin_id = origin_id + alarm.alarm_type = f"origin-block-ooni-{country}" + alarm.target = "origin" + db.session.add(alarm) + alarm.update_state(state, text) + + +with app.app_context(): + origins = Origin.query.filter(Origin.destroyed == None).all() + for origin in origins: + ooni = threshold_origin(origin.domain_name) + for country in ooni: + set_ooni_alarm(origin.id, country.lower(), ooni[country]["state"], ooni[country]["message"]) diff --git a/app/terraform/proxy_check.py b/app/terraform/proxy_check.py index 06c0cf1..d3b788b 100644 --- a/app/terraform/proxy_check.py +++ b/app/terraform/proxy_check.py @@ -14,6 +14,7 @@ def set_http_alarm(proxy_id: int, state: AlarmState, text: str): alarm = Alarm() alarm.proxy_id = proxy_id alarm.alarm_type = "http-status" + alarm.target = "proxy" db.session.add(alarm) alarm.update_state(state, text)
Resource Type StateMessageLast Update
Proxy: {{ alarm.proxy.url }} ({{ alarm.proxy.origin.domain_name }})Origin: {{ alarm.origin.domain_name }}AWS CloudFront{{ alarm.alarm_type }} {{ alarm.alarm_state.name }}{{ alarm.text }}{{ alarm.last_updated.strftime("%a, %d %b %Y %H:%M:%S") }}