diff options
| -rwxr-xr-x | sigsum-witness.py | 135 | 
1 files 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 | 
