migrate-apt-trusted-keys/migrate-apt-trusted-keys
2023-07-07 12:49:38 +02:00

139 lines
4.2 KiB
Python
Executable file

#!/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 <brenard@zionetrix.net>
# 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