diff --git a/keylime.conf b/keylime.conf index f447590ab..a2de419bd 100644 --- a/keylime.conf +++ b/keylime.conf @@ -253,6 +253,13 @@ revocation_notifier = True revocation_notifier_ip = 127.0.0.1 revocation_notifier_port = 8992 +# Enable revocation notifications via webhook. This can be used to notify other +# systems that do not have a Keylime agent running. +revocation_notifier_webhook = False + +# Webhook url for revocation notifications. +webhook_url = '' + # The verifier limits the size of upload payloads (allowlists) which defaults to # 100MB (104857600 bytes). This setting can be raised (or lowered) based on the # size of the actual payloads diff --git a/keylime/cloud_verifier_common.py b/keylime/cloud_verifier_common.py index 46b25197d..dcdd435e6 100644 --- a/keylime/cloud_verifier_common.py +++ b/keylime/cloud_verifier_common.py @@ -365,7 +365,9 @@ def process_get_status(agent): def notify_error(agent, msgtype='revocation', event=None): - if not config.getboolean('cloud_verifier', 'revocation_notifier'): + send_mq = config.getboolean('cloud_verifier', 'revocation_notifier') + send_webhook = config.getboolean('cloud_verifier', 'revocation_notifier_webhook', False) + if not (send_mq or send_webhook): return # prepare the revocation message: @@ -391,7 +393,10 @@ def notify_error(agent, msgtype='revocation', event=None): else: tosend['signature'] = "none" - revocation_notifier.notify(tosend) + if send_mq: + revocation_notifier.notify(tosend) + if send_webhook: + revocation_notifier.notify_webhook(tosend) def validate_agent_data(agent_data): diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py index 0c1d8315b..b3e0e72a5 100644 --- a/keylime/revocation_notifier.py +++ b/keylime/revocation_notifier.py @@ -11,6 +11,7 @@ import sys import signal +import requests import simplejson as json import zmq @@ -79,6 +80,36 @@ def worker(tosend): t.start() +def notify_webhook(tosend): + url = config.get('cloud_verifier', 'webhook_url', '') + # Check if a url was specified + if url == '': + return + + def worker_webhook(tosend, url): + retry_interval = config.getfloat('cloud_verifier', 'retry_interval') + session = requests.session() + logger.info("Sending revocation event via webhook...") + for i in range(config.getint('cloud_verifier', 'max_retries')): + try: + response = session.post(url, json=tosend) + if response.status_code in [200, 202]: + break + + logger.debug(f"Unable to publish revocation message {i} times via webhook, " + f"trying again in {retry_interval} seconds. " + f"Server returned status code: {response.status_code}") + except requests.exceptions.RequestException as e: + logger.debug(f"Unable to publish revocation message {i} times via webhook, " + f"trying again in {retry_interval} seconds: {e} ") + + time.sleep(config.getfloat('cloud_verifier', 'retry_interval')) + + w = functools.partial(worker_webhook, tosend, url) + t = threading.Thread(target=w) + t.start() + + cert_key = None