aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrégoire Détrez <gregoire@mullvad.net>2022-07-07 16:02:42 +0200
committerGrégoire Détrez <gregoire@mullvad.net>2022-08-19 14:46:42 +0200
commiteb1668efd83d1e44e07f0f8d0e6075f05e31206d (patch)
treef3449857c291d6cf6756c2a0b40795dad2536a12
parent6d2e1e1b2845f3c5c2b83c3bbaf523b722d759f8 (diff)
Daemonize witness
-rwxr-xr-xsigsum-witness.py135
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