From eb1668efd83d1e44e07f0f8d0e6075f05e31206d Mon Sep 17 00:00:00 2001 From: Grégoire Détrez Date: Thu, 7 Jul 2022 16:02:42 +0200 Subject: Daemonize witness --- sigsum-witness.py | 135 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 112 insertions(+), 23 deletions(-) diff --git a/sigsum-witness.py b/sigsum-witness.py index 3c0d643..04b4ba5 100755 --- a/sigsum-witness.py +++ b/sigsum-witness.py @@ -16,24 +16,42 @@ # sigkey = nacl.signing.SigningKey('badc0ffee123456...', nacl.encoding.HexEncoder) # sigkey.verify_key.encode(nacl.encoding.HexEncoder) -import sys -import os -from stat import * import argparse -import requests +import logging +import os import struct +import sys +import threading +import time from binascii import hexlify, unhexlify -import nacl.encoding -import nacl.signing from hashlib import sha256 -import time from math import floor from pathlib import PurePath +from stat import * + +import nacl.encoding +import nacl.signing +import prometheus_client as prometheus +import requests + from tools.libsigntools import ssh_to_sign BASE_URL_DEFAULT = 'http://poc.sigsum.org:4780/' CONFIG_DIR_DEFAULT = os.path.expanduser('~/.config/sigsum-witness/') +LOGGER = logging.getLogger("sigsum-witness") + +# Metrics +SIGNING_ATTEMPTS = prometheus.Counter( + "sigsum_witness_signing_attempts", "Total number of signing attempts" +) +SIGNING_ERROR = prometheus.Counter( + "sigsum_witness_signing_errors", "Total number of signing error" +) +LAST_SUCCESS = prometheus.Gauge( + "sigsum_witness_last_success", "Time of last successful signature" +) + ERR_OK = 0 ERR_USAGE = os.EX_USAGE ERR_TREEHEAD_READ = 2 @@ -79,9 +97,30 @@ class Parser: default='signing-key', help="Signing key file, relative to $base_dir if not an absolute path (signing-key)") - p.add_argument('-u', '--base-url', - default=BASE_URL_DEFAULT, - help="Log base URL ({})".format(BASE_URL_DEFAULT)) + p.add_argument( + "-u", + "--base-url", + default=BASE_URL_DEFAULT, + help="Log base URL ({})".format(BASE_URL_DEFAULT), + ) + + p.add_argument( + "--interval", + action="store", + type=int, + default=30, + help="Interval between signing attempt, in seconds (30)", + ) + + p.add_argument( + "-v", + "--verbose", + action="store_const", + const=logging.DEBUG, + default=logging.INFO, + dest="log_level", + help="Log base URL ({})".format(BASE_URL_DEFAULT), + ) self.parser = p @@ -454,6 +493,54 @@ def user_confirm(prompt): return True return False + +class Witness(threading.Thread): + def __init__(self, signing_key, log_verification_key, cur_tree_head): + super().__init__() + self.signing_key = signing_key + self.log_verification_key = log_verification_key + self.cur_tree_head = cur_tree_head + self.exit = threading.Event() + + def run(self): + while not self.exit.wait(g_args.interval): + SIGNING_ATTEMPTS.inc() + try: + err = self.sign_once() + except Exception as e: + LOGGER.exception(e) + SIGNING_ERROR.inc() + continue + if err: + LOGGER.error(err[1]) + SIGNING_ERROR.inc() + else: + LAST_SUCCESS.set_to_current_time() + + def sign_once(self): + new_tree_head, err = fetch_tree_head_and_verify(self.log_verification_key) + if err: + return err + now = floor(time.time()) + err = new_tree_head.timestamp_valid(now) + if err: + return err + err = new_tree_head.history_valid(self.cur_tree_head) + if err: + return err + if not self.cur_tree_head.signature_valid(self.log_verification_key): + return ( + ERR_TREEHEAD_SIGNATURE_INVALID, + "ERROR: signature of current tree head invalid", + ) + err = sign_send_store_tree_head( + self.signing_key, self.log_verification_key, new_tree_head + ) + if err: + return err + self.cur_tree_head = new_tree_head + + def main(args): global g_args g_args = Parser() @@ -463,6 +550,10 @@ def main(args): if g_args.save_config: # TODO write to config file return ERR_NYI, "ERROR: --save-config is not yet implemented" + logging.basicConfig( + format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", + level=g_args.log_level, + ) now = floor(time.time()) consistency_verified = False @@ -502,20 +593,18 @@ def main(args): return (ERR_USAGE, "ERROR: Valid tree head found: --bootstrap-log not allowed") - new_tree_head, err = fetch_tree_head_and_verify(log_verification_key) - if err: return err - - err = new_tree_head.timestamp_valid(now) - if err: return err - - err = new_tree_head.history_valid(cur_tree_head) - if err: return err - - if not cur_tree_head.signature_valid(log_verification_key): - return ERR_TREEHEAD_SIGNATURE_INVALID, "ERROR: signature of current tree head invalid" + # Start up the server to expose the metrics. + LOGGER.info("Starting metrics server on port 8000") + prometheus.start_http_server(8000) - err = sign_send_store_tree_head(signing_key, log_verification_key, new_tree_head) - if err: return err + LOGGER.info("Starting witness") + thread = Witness(signing_key, log_verification_key, cur_tree_head) + thread.start() + try: + time.sleep(100000) + except KeyboardInterrupt: + thread.exit.set() + thread.join() return 0, None -- cgit v1.2.3