mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-03-22 05:09:12 +00:00
Add pgp_keyring action plugin to install PGP keys
Add action plugin to install PGP keys. First use case is to convert a PGP keyring to binary format before installing on destination host.
This commit is contained in:
parent
34938ca1ef
commit
1c1f2132c5
5 changed files with 192 additions and 0 deletions
103
plugins/action/pgp_keyring.py
Normal file
103
plugins/action/pgp_keyring.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
# Copyright: (c) 2025, Eero Aaltonen
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from ansible import constants as C
|
||||||
|
|
||||||
|
from ansible.config.manager import ensure_type
|
||||||
|
from ansible.errors import AnsibleActionFail, AnsibleError
|
||||||
|
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
|
||||||
|
from ansible.module_utils.parsing.convert_bool import boolean
|
||||||
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pgpy
|
||||||
|
except Exception as e:
|
||||||
|
raise AnsibleError('PGPym~=0.6.1 must be installed to use pgp_keyring plugin') from e
|
||||||
|
|
||||||
|
|
||||||
|
class ActionModule(ActionBase):
|
||||||
|
|
||||||
|
TRANSFERS_FILES = True
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
""" Install PGP keyrings in binary format """
|
||||||
|
|
||||||
|
if task_vars is None:
|
||||||
|
task_vars = dict()
|
||||||
|
|
||||||
|
super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
del tmp # tmp no longer has any effect
|
||||||
|
|
||||||
|
# Options type validation
|
||||||
|
# strings
|
||||||
|
for s_type in ('src', 'dest'):
|
||||||
|
if s_type in self._task.args:
|
||||||
|
value = ensure_type(self._task.args[s_type], 'string')
|
||||||
|
if value is not None and not isinstance(value, str):
|
||||||
|
raise AnsibleActionFail("%s is expected to be a string, but got %s instead" % (s_type, type(value)))
|
||||||
|
self._task.args[s_type] = value
|
||||||
|
|
||||||
|
# booleans
|
||||||
|
try:
|
||||||
|
follow = boolean(self._task.args.get('follow', False), strict=False)
|
||||||
|
except TypeError as e:
|
||||||
|
raise AnsibleActionFail(to_native(e))
|
||||||
|
|
||||||
|
# assign to local vars for ease of use
|
||||||
|
source = self._task.args.get('src', None)
|
||||||
|
dest = self._task.args.get('dest', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# logical validation
|
||||||
|
if source is None or dest is None:
|
||||||
|
raise AnsibleActionFail("src and dest are required")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# find in expected paths
|
||||||
|
source = self._find_needle('files', source)
|
||||||
|
except AnsibleError as e:
|
||||||
|
raise AnsibleActionFail(to_text(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
key, po = pgpy.PGPKey.from_file(source)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e)))
|
||||||
|
except Exception as e:
|
||||||
|
raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e)))
|
||||||
|
|
||||||
|
new_task = self._task.copy()
|
||||||
|
local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result_file = os.path.join(local_tempdir, os.path.basename(source))
|
||||||
|
with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f:
|
||||||
|
f.write(bytes(key))
|
||||||
|
|
||||||
|
new_task.args.update(
|
||||||
|
dict(
|
||||||
|
src=result_file,
|
||||||
|
dest=dest,
|
||||||
|
follow=follow,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# call with ansible.legacy prefix to eliminate collisions with collections while still allowing local override
|
||||||
|
copy_action = self._shared_loader_obj.action_loader.get('ansible.legacy.copy',
|
||||||
|
task=new_task,
|
||||||
|
connection=self._connection,
|
||||||
|
play_context=self._play_context,
|
||||||
|
loader=self._loader,
|
||||||
|
templar=self._templar,
|
||||||
|
shared_loader_obj=self._shared_loader_obj)
|
||||||
|
return copy_action.run(task_vars=task_vars)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
||||||
41
plugins/modules/pgp_keyring.py
Normal file
41
plugins/modules/pgp_keyring.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright: (c) 2025, Eero Aaltonen
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# This module is implemented as an action plugin and runs on the controller
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = r"""
|
||||||
|
---
|
||||||
|
module: pgp_keyring
|
||||||
|
short_description: Install PGP keyrings in binary format
|
||||||
|
description: Converts PGP keyrings to binary format on the ansible controller,
|
||||||
|
and installs them to the target systems.
|
||||||
|
version_added: 12.4.0
|
||||||
|
author: "Eero Aaltonen (@eaaltonen)"
|
||||||
|
options:
|
||||||
|
src:
|
||||||
|
description: Source key file (typically ASCII armored)
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
dest:
|
||||||
|
description: Destination key file. Can be relative, in which case the target system default is used
|
||||||
|
required: true
|
||||||
|
type: str
|
||||||
|
follow:
|
||||||
|
description: This flag indicates that filesystem links in the destination, if they exist, should be followed.
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
- name: Install Microsoft Package signing key
|
||||||
|
community.general.pgp_keyring:
|
||||||
|
src: microsoft.asc
|
||||||
|
dest: microsoft.gpg
|
||||||
|
become: true
|
||||||
|
"""
|
||||||
8
tests/integration/requirements.txt
Normal file
8
tests/integration/requirements.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
||||||
|
# requirement for the pgp_keyring action plugin
|
||||||
|
PGPy == 0.6.0 ; python_version <= '3.12'
|
||||||
|
PGPym ~= 0.6.1 ; python_version >= '3.13'
|
||||||
19
tests/integration/targets/pgp_keyring/files/microsoft.asc
Normal file
19
tests/integration/targets/pgp_keyring/files/microsoft.asc
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: BSN Pgp v1.1.0.0
|
||||||
|
|
||||||
|
mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT
|
||||||
|
LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV
|
||||||
|
7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag
|
||||||
|
OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j
|
||||||
|
H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr
|
||||||
|
M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs
|
||||||
|
ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATQEEwEI
|
||||||
|
AB4FAlYxWIwCGwMGCwkIBwMCAxUIAwMWAgECHgECF4AACgkQ6z6Urb4SKc+P9gf/
|
||||||
|
diY2900wvWEgV7iMgrtGzx79W/PbwWiOkKoD9sdzhARXWiP8Q5teL/t5TUH6TZ3B
|
||||||
|
ENboDjwr705jLLPwuEDtPI9jz4kvdT86JwwG6N8gnWM8Ldi56SdJEtXrzwtlB/Fe
|
||||||
|
6tyfMT1E/PrJfgALUG9MWTIJkc0GhRJoyPpGZ6YWSLGXnk4c0HltYKDFR7q4wtI8
|
||||||
|
4cBu4mjZHZbxIO6r8Cci+xxuJkpOTIpr4pdpQKpECM6x5SaT2gVnscbN0PE19KK9
|
||||||
|
nPsBxyK4wW0AvAhed2qldBPTipgzPhqB2gu0jSryil95bKrSmlYJd1Y1XfNHno5D
|
||||||
|
xfn5JwgySBIdWWvtOI05gw==
|
||||||
|
=zPfd
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
21
tests/integration/targets/pgp_keyring/tasks/main.yml
Normal file
21
tests/integration/targets/pgp_keyring/tasks/main.yml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# test code for the pgp_keyring action
|
||||||
|
# (c) 2025, Eero Aaltonen
|
||||||
|
|
||||||
|
- name: Install Microsoft Package signing key
|
||||||
|
pgp_keyring:
|
||||||
|
src: microsoft.asc
|
||||||
|
dest: microsoft.pgp
|
||||||
|
|
||||||
|
- name: stat output file
|
||||||
|
stat:
|
||||||
|
path: microsoft.pgp
|
||||||
|
register: binary_keyring
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
binary_keyring.stat.exists
|
||||||
|
|
||||||
|
- name: Remove binary keyring
|
||||||
|
file:
|
||||||
|
path: binary_keyring.path
|
||||||
|
state: absent
|
||||||
Loading…
Add table
Add a link
Reference in a new issue