From 87338a564a9556e17bcd14369bf964086aba5c24 Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Fri, 7 Jul 2023 12:31:07 +0200 Subject: [PATCH] Initial release --- .pre-commit-config.yaml | 37 +++++++++++ .pylintrc | 7 ++ README.md | 57 ++++++++++++++++ migrate-apt-trusted-keys | 139 +++++++++++++++++++++++++++++++++++++++ setup.cfg | 3 + 5 files changed, 243 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 .pylintrc create mode 100644 README.md create mode 100755 migrate-apt-trusted-keys create mode 100644 setup.cfg diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8708b0d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +# Pre-commit hooks to run tests and ensure code is cleaned. +# See https://pre-commit.com for more information +repos: +- repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: ['--keep-percent-format', '--py37-plus'] +- repo: https://github.com/psf/black + rev: 22.12.0 + hooks: + - id: black + args: ['--target-version', 'py37', '--line-length', '100'] +- repo: https://github.com/PyCQA/isort + rev: 5.11.5 + hooks: + - id: isort + args: ['--profile', 'black', '--line-length', '100'] +- repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + args: ['--max-line-length=100'] +- repo: local + hooks: + - id: pylint + name: pylint + entry: pylint --extension-pkg-whitelist=cx_Oracle + language: system + types: [python] + require_serial: true +- repo: https://github.com/Lucas-C/pre-commit-hooks-bandit + rev: v1.0.5 + hooks: + - id: python-bandit-vulnerability-check + name: bandit + args: [--skip, "B101", --recursive, mylib] diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..41881fd --- /dev/null +++ b/.pylintrc @@ -0,0 +1,7 @@ +[MESSAGES CONTROL] +disable=invalid-name, + locally-disabled, + +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=100 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3284d7 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Migrate APT trusted keys + +Script to handle migration of obsolete /etc/apt/trusted.gpg to split GPG +keyrings in /etc/apt/trusted.gpg.d. + +Note: Useful to fix this type of APT error: + +``` +Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. +``` + +## Installation + +```bash +apt install python3 wget +wget -O /usr/local/sbin/migrate-apt-trusted-keys \ + https://gitea.zionetrix.net/bn8/migrate-apt-trusted-keys/raw/branch/main/migrate-apt-trusted-keys +chmod 750 /usr/local/sbin/migrate-apt-trusted-keys +``` + +## Usage + +``` +usage: migrate-apt-trusted-keys [-h] [-p KEYRING_PATH] [-o OUTPUT_PATH] [-a] + [-f] + +Script to manage the migration from the deprecated /etc/apt/trusted.gpg file +to splited GPG keyring in /etc/apt/trusted.gpg.d. + +options: + -h, --help show this help message and exit + -p KEYRING_PATH, --keyring-path KEYRING_PATH + APT keyring file path. + -o OUTPUT_PATH, --output-path OUTPUT_PATH + Output directory path. + -a, --auto Migrate all GPG keys, without user interaction. + -f, --force Force mode: overwrite output file if already exists. +``` + +## Copyright + +Copyright (c) 2023 Benjamin Renard + +## License + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/migrate-apt-trusted-keys b/migrate-apt-trusted-keys new file mode 100755 index 0000000..1d18879 --- /dev/null +++ b/migrate-apt-trusted-keys @@ -0,0 +1,139 @@ +#!/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 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1899e22 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[flake8] +ignore = E501,W503 +max-line-length = 100