feature(keyringctl): use the export command purely to export keyrings

This gives more control over the export command that may be useful to
export a single packager to import it into gpg. This will also give more
flexibility to chain this function to the future verify stage.

By default the command exports the whole keyring directory.
This commit is contained in:
Levente Polyak 2021-10-21 20:34:48 +02:00
parent aa2f2ea497
commit 86747ecab7
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
2 changed files with 94 additions and 30 deletions

View File

@ -29,12 +29,12 @@ Import a new packager key by deriving the username from the filename.
Alternatively import a file or directory and override the username
```bash
./keyringctl import --name <username> <file_or_directory>
./keyringctl import --name <username> <file_or_directory...>
```
Updates to existing keys will automatically derive the username from the known fingerprint.
```bash
./keyringctl import <file_or_directory>
./keyringctl import <file_or_directory...>
```
Main key imports support the same options plus a mandatory `--main`
@ -42,6 +42,23 @@ Main key imports support the same options plus a mandatory `--main`
./keyringctl import --main <username>.asc
```
### Export
Export the whole keyring including main and packager to stdout
```bash
./keyringctl export
```
Limit to specific usernames using an output file
```bash
./keyringctl export <usernames...> --output <filename>
```
Only export specific certificate directories in [keyring](keyring)
```bash
./keyringctl export <directory...>
```
## Installation
To install archlinux-keyring system-wide use the included `Makefile`:

View File

@ -1067,6 +1067,69 @@ def get_fingerprints(working_dir: Path, decomposed_paths: List[Path]) -> Set[Fin
return fingerprints
def export(
working_dir: Path,
keyring_root: Path,
sources: Optional[List[Path]] = None,
output: Optional[Path] = None,
) -> str:
"""Export all provided PGP packet files to a single output file
If sources contains directories, any .asc files below them are considered.
Parameters
----------
working_dir: Path
A directory to use for temporary files
keyring_root: Path
The keyring root directory to look up username shorthand sources
sources: Optional[List[Path]]
A list of directories or files from which to read PGP packet information (defaults to `keyring_root`)
output: Optional[Path]
An output file that all PGP packet data is written to, return the result instead if None
Returns
-------
str
The result if no output file has been used
"""
if not sources:
sources = [keyring_root]
# resolve shorthand username exports for packager keys
for index, source in enumerate(sources):
packager_source = keyring_root / "packager" / source.name
if not source.exists() and packager_source.exists():
sources[index] = packager_source
# depth first search certificate paths
cert_dirs: Set[Path] = set()
visit: List[Path] = sources
while visit:
path = visit.pop()
# this level contains a certificate, abort depth search
if list(path.glob("*.asc")):
cert_dirs.add(path)
continue
visit.extend([path for path in path.iterdir() if path.is_dir()])
temp_dir = Path(mkdtemp(dir=working_dir, prefix="arch-keyringctl-export-join-")).absolute()
certificates = []
for cert_dir in sorted(cert_dirs):
cert_path = temp_dir / f"{cert_dir.name}.asc"
debug(f"Joining {cert_dir} in {cert_path}")
packet_join(
packets=sorted(cert_dir.glob("**/*.asc")),
output=cert_path,
force=True,
)
certificates.append(cert_path)
return keyring_merge(certificates, output)
def export_keyring(
working_dir: Path,
main: List[Path],
@ -1194,30 +1257,13 @@ if __name__ == "__main__":
"export",
help="export a directory structure of PGP packet data to a combined file",
)
export_parser.add_argument("output", type=absolute_path, help="file to write PGP packet data to")
export_parser.add_argument("-o", "--output", type=absolute_path, help="file to write PGP packet data to")
export_parser.add_argument(
"-m",
"--main",
action="append",
help="files or directories containing PGP packet data that is trusted (can be provided multiple times)",
required=True,
"source",
nargs="*",
help="username or files/directories containing PGP packet data (can be provided multiple times)",
type=absolute_path,
)
export_parser.add_argument(
"-s",
"--source",
action="append",
help="files or directories containing PGP packet data (can be provided multiple times)",
required=True,
type=absolute_path,
)
export_parser.add_argument(
"-p",
"--pacman-integration",
action="store_true",
default=False,
help="export trusted and revoked files (used by pacman) alongside the keyring",
)
args = parser.parse_args()
@ -1257,13 +1303,14 @@ if __name__ == "__main__":
pacman_integration=True,
)
elif "export" == args.subcommand:
export_keyring(
working_dir=working_dir,
main=args.main,
sources=args.source,
output=args.output,
force=args.force,
pacman_integration=args.pacman_integration,
print(
export(
working_dir=working_dir,
keyring_root=keyring_root,
sources=args.source,
output=args.output,
),
end="",
)
else:
parser.print_help()