diff --git a/README.md b/README.md index ce0d610..2b88236 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ podman rm kartograf-run #### rpki-client -Kartograf requires `rpki-client` version 8.4 or higher to be installed locally. Many package managers have `rpki-client`, however please note that typically only the latest version is available. Kartograf is currently tested to work with `rpki-client` 8.4 - 9.7. If a new release is available that breaks compatibility, you may need to build a compatible `rpki-client` version yourself if your preferred package manager does not have a compatible version available. In that case, see the [instructions in the rpki-client-portable project](https://github.com/rpki-client/rpki-client-portable/blob/master/INSTALL). +Kartograf requires `rpki-client` version 9.6 or higher to be installed locally. Many package managers have `rpki-client`, however please note that typically only the latest version is available. Kartograf is currently tested to work with `rpki-client` 9.6 - 9.7. If a new release is available that breaks compatibility, you may need to build a compatible `rpki-client` version yourself if your preferred package manager does not have a compatible version available. In that case, see the [instructions in the rpki-client-portable project](https://github.com/rpki-client/rpki-client-portable/blob/master/INSTALL). ``` # Linux/BSD diff --git a/kartograf/rpki/fetch.py b/kartograf/rpki/fetch.py index a2f2535..2864242 100644 --- a/kartograf/rpki/fetch.py +++ b/kartograf/rpki/fetch.py @@ -1,3 +1,4 @@ +import re import subprocess import sys @@ -73,7 +74,8 @@ def fetch_rpki_db(context): # Download TALs and presist them in the RPKI data folder download_rir_tals(context) tal_options = [item for path in data_tals(context) for item in ('-t', path)] - run_args = ["rpki-client", "-d", context.data_dir_rpki_cache] + tal_options + run_args = ["rpki-client", "-d", context.data_dir_rpki_cache, + "-P", context.epoch] + tal_options print("Downloading RPKI Data, this may take a while.") if context.stable_repos: @@ -81,18 +83,25 @@ def fetch_rpki_db(context): run_args += ["-H", url] print("Using only stable RPKI repositories.") + # rpki-client requires a writable output directory as a positional + # argument. Without one it falls back to a compiled-in default that + # is not writable in some environments (e.g. nix), which makes it + # exit before emitting the CCR hashes. + run_args.append(context.out_dir_rpki) + + result = subprocess.run(run_args, + capture_output=True, + check=False) + if context.debug_log: with open(context.debug_log, 'a') as logs: logs.write("=== RPKI Download ===\n") - logs.flush() # Without this the line above is not appearing first in the logs - subprocess.run(run_args, - stdout=logs, - stderr=logs, - check=False) - else: - subprocess.run(run_args, - capture_output=True, - check=False) + if result.stdout: + logs.write(result.stdout.decode()) + if result.stderr: + logs.write(result.stderr.decode()) + + parse_ccr_hashes(result.stdout.decode() if result.stdout else "") print(f"Downloaded RPKI Data, hash sum: {calculate_sha256_directory(context.data_dir_rpki_cache)}") @@ -158,3 +167,11 @@ def process_files_batch(batch): json.dump(s, f) print(f"{len(results_json)} RKPI ROAs validated\nSaved to: {result_path.name}\nFile hash: {calculate_sha256(result_path)}") + + +def parse_ccr_hashes(output): + """Extract and print CCR hashes from rpki-client stdout.""" + for line in output.splitlines(): + match = re.match(r"^(CCR .+ hash): (.+)$", line) + if match: + print(f"{match.group(1)}: {match.group(2)}") diff --git a/kartograf/rpki/parse.py b/kartograf/rpki/parse.py index c8f1fee..bb65790 100644 --- a/kartograf/rpki/parse.py +++ b/kartograf/rpki/parse.py @@ -1,4 +1,5 @@ import json +import subprocess from pathlib import Path from typing import Dict @@ -7,12 +8,16 @@ is_bogon_asn, is_out_of_encoding_range, ) +from kartograf.rpki.fetch import data_tals, parse_ccr_hashes from kartograf.timed import timed from kartograf.util import parse_pfx @timed def parse_rpki(context): + if context.reproduce: + compute_ccr_hashes(context) + raw_input = Path(context.out_dir_rpki) / "rpki_raw.json" rpki_res = Path(context.out_dir_rpki) / "rpki_final.txt" @@ -113,3 +118,14 @@ def parse_rpki(context): print(f'Invalids found: {invalids}') print(f'Incompletes: {incompletes}') print(f'Non-ROA files: {not_roas}') + + +def compute_ccr_hashes(context): + """Run rpki-client in offline mode to compute and print CCR hashes.""" + tal_options = [item for path in data_tals(context) for item in ('-t', path)] + run_args = ["rpki-client", "-n", "-d", context.data_dir_rpki_cache, + "-P", context.epoch] + tal_options + [context.out_dir_rpki] + result = subprocess.run(run_args, + capture_output=True, + check=False) + parse_ccr_hashes(result.stdout.decode() if result.stdout else "") diff --git a/kartograf/util.py b/kartograf/util.py index b66ecfc..1556f2b 100644 --- a/kartograf/util.py +++ b/kartograf/util.py @@ -82,8 +82,8 @@ def check_compatibility(): if local_version is None: raise RuntimeError("Could not determine rpki-client version. Is it installed?") - if local_version < 8.4: - raise Exception("Error: rpki-client version 8.4 or higher is required.") + if local_version < 9.6: + raise Exception("Error: rpki-client version 9.6 or higher is required.") if local_version == latest_version: print(f"Using rpki-client version {local_version} (recommended).")