Skip to content

Conversation

maisim
Copy link
Contributor

@maisim maisim commented Sep 24, 2025

Add new gpg.key and gpg.dearmor operations to manage GPG keys and keyrings. These operations provide a modern alternative to apt-key for managing APT repository keys.

Features:

  • Install keys from URLs, local files, or keyservers
  • Remove keys by ID or entire keyring files
  • Convert ASCII armored keys to binary format
  • Manage keys in specific keyrings or across all APT keyrings

This is part 1/3 of modernizing APT key management.

  • Pull request is based on the default branch (3.x at this time)
  • Pull request includes tests for any new/updated operations/facts
  • Pull request includes documentation for any new/updated operations/facts
  • Tests pass (see scripts/dev-test.sh)
  • Type checking & code style passes (see scripts/dev-lint.sh)

@maisim maisim marked this pull request as draft September 24, 2025 09:19
@maisim maisim force-pushed the pr1-add-gpg-operations branch 3 times, most recently from 6521c5b to 5585d21 Compare September 24, 2025 11:36
Add new gpg.key and gpg.dearmor operations to manage GPG keys and keyrings.
These operations provide a modern alternative to apt-key for managing
APT repository keys.

Features:
- Install keys from URLs, local files, or keyservers
- Remove keys by ID or entire keyring files
- Convert ASCII armored keys to binary format
- Manage keys in specific keyrings or across all APT keyrings

This is part 1/3 of modernizing APT key management.
@maisim maisim force-pushed the pr1-add-gpg-operations branch from 5585d21 to 8d3ea39 Compare September 24, 2025 14:01
@maisim maisim marked this pull request as ready for review September 24, 2025 14:07
@maisim
Copy link
Contributor Author

maisim commented Sep 24, 2025

Hi @DonDebonair !
Can you take a look at this, please?
It seems to work, but I'm not sure I'm always using gpg correctly. Also, I'm always hesitant to use facts or shell commands within operations. Let me know what you think.
Thanks!

@DonDebonair
Copy link
Contributor

DonDebonair commented Sep 25, 2025

Thanks for the PR @maisim . I would not be afraid to use facts inside operations. I actually consider it a best practice to use facts to check things in operations. So instead of doing checks inside the shell commands you yield, there are probably places where you can rely on facts instead. That makes the yielded commands simpler.

If you can change those already, I'll do a more full review this weekend.

Caveat btw: I'm by no means a GPG expert. A short while ago, I wanted to use pyinfra to install Docker on a Debian host. I just created the keyring directory and downloaded the key directly into that, never touching GPG 😅

@maisim maisim mentioned this pull request Sep 25, 2025
5 tasks
for kid in keyid:
# Remove key from all matching keyrings
yield (
f'for keyring in {pattern}; do [ -e "$keyring" ] && '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't it make more sense to retrieve keyrings through a fact and then only try to remove the key from keyrings that actually exist?

Or maybe even better, create a fact that lists all known keyrings and then use the GpgKeys fact for each of those keyrings to get all the keys. Then you can specifically remove the keyid if it exists

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Since there’s no global or common location for keyrings, this will allow specifying the directories to search in — for example, /etc/apt/trusted.gpg.d/ and /usr/share/keyrings/ in the APT context.


# Clean up empty keyrings
yield (
f'for keyring in {pattern}; do [ -e "$keyring" ] && '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As referenced in the other comment, if you can list all keys by keyring, then you know if keyrings are going to be empty after removing keyid from those rings and you can remove them based on that

yield (f'if [ -f "{dest}" ] && ({condition}); then ' f'rm -f "{dest}"; fi')
return

elif dest and not keyid:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be else instead of elif. To me, that seems cleaner because it clearly signals there are only 3 possible combinations of conditions: keyid is provided and dest isn't, keyid and dest are both provided and lastly, dest is provided and keyid isn't

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it is more an "documentation" elif. To be more explicit.
What about add an else wich raise an exception if we go into an unhandle case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for me!

elif dest and keyid:
# For APT keyring files, use a simpler approach:
# Check if keys exist in file, and if so, remove the entire file
# This is appropriate for most APT use cases
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit concerned about "most APT use cases" here. Is there a situation we might be removed a keyfile/keyring that has multiple keys in it?



@operation()
def key(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very complex function that covers many different scenarios. I think that from a public API standpoint, that is fine, because we're dealing with 1 resource: keys. But you could break up the function into separate functions that are all called from the key() function, based on the scenario.

See the docker operations for an example of how this can be done

@DonDebonair
Copy link
Contributor

Left some more comments @maisim incl. some ideas on how to leverate facts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants