#!/usr/bin/env python3 # # Script to handle migration of obsolete /etc/apt/trusted.gpg to split GPG # keyrings in /etc/apt/trusted.gpg.d. # # Author : Benjamin Renard # Date : Fri, 07 Jul 2023 12:26:54 +0200 # Source : http://gitea.zionetrix.net/bn8/migrate-apt-trusted-keys # Licence : GPL v3 # """ Script to handle migration of obsolete /etc/apt/trusted.gpg to split GPG keyrings in /etc/apt/trusted.gpg.d. """ import argparse import os import re import subprocess import sys import traceback # Variables raw_output = "" # Args Parsing parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "-p", "--keyring-path", help="APT keyring file path.", type=str, default="/etc/apt/trusted.gpg" ) parser.add_argument( "-o", "--output-path", help="Output directory path.", type=str, default="/etc/apt/trusted.gpg.d" ) parser.add_argument( "-a", "--auto", help="Migrate all GPG keys, without user interaction.", action="store_true" ) parser.add_argument( "-f", "--force", help="Force mode: overwrite output file if already exists.", action="store_true", ) args = parser.parse_args() if not os.path.exists(args.keyring_path): parser.error(f"APT keyring file {args.keyring_path} not found.") if not os.path.isdir(args.output_path): parser.error(f"Output directory {args.output_path} not found (or is not a directory).") # Execute Command process = subprocess.run( args=["gpg", "--keyring", args.keyring_path, "--list-keys"], capture_output=True, check=True ) if process.returncode > 0: print("No fingerprints found!") sys.exit() raw_output = str(process.stdout.decode("utf-8")) all_keys = [] current_key = None key_id_line = re.compile("^ +([0-9A-F]+)$") uid_line = re.compile("^uid +.*<[^@]+@([^>]+)>$") for line in raw_output.split("\n")[2:]: if not line: continue if line.startswith("pub") and current_key: all_keys.append(current_key) current_key = None if current_key is None: current_key = {"lines": [], "id": None, "domain": None} current_key["lines"].append(line) m = key_id_line.match(line) if m: current_key["id"] = m.group(1) m = uid_line.match(line) if m: current_key["domain"] = m.group(1) if current_key: all_keys.append(current_key) for key in all_keys: if not args.auto: print("\n\n") print("\n".join(key["lines"])) answer = input("Do you want to migrate this key [Y/n or q to exit]? ") answer = answer.strip().lower() if answer == 'q': sys.exit() if answer and answer != "y": continue print(f"Migrate key {key['id']}...") name = "-".join(key["domain"].lower().split(".")[:-1]) if key["domain"] else key["id"] name += ".gpg" if not args.auto: answer = input(f"Key name [{name}]? ") name = answer.strip() if answer else name output_path = os.path.join(args.output_path, name) print(f"Export key {key['id']} to {output_path}...") try: if os.path.exists(output_path): if not args.force: if args.auto: print(f"Outfile '{output_path}' already exists, pass") continue answer = input(f"Outfile '{output_path}' already exists, overwrite [y/N or q to exit]? ") answer = answer.strip().lower() if answer == 'q': sys.exit() if answer != "y": continue print(f"Remove existing output file '{output_path}'") os.remove(output_path) # pylint: disable=consider-using-with export_cmd = subprocess.Popen( ("gpg", "--keyring", args.keyring_path, "--export", key["id"]), stdout=subprocess.PIPE ) output_cmd = subprocess.check_output( ("gpg", "--dearmour", "-o", output_path), stdin=export_cmd.stdout ) export_cmd.wait() print(f"Key {key['id']} exported to {output_path}") print() except Exception: # pylint: disable=broad-exception-caught print(f"Fail to export key {key['id']} to {output_path}") traceback.print_exc() # vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab