Skip to content

Commit

Permalink
Allow signature verification logic to be reused for other backends
Browse files Browse the repository at this point in the history
  • Loading branch information
lkubb authored and dwoz committed May 27, 2024
1 parent b171fae commit aaad0d2
Show file tree
Hide file tree
Showing 10 changed files with 421 additions and 65 deletions.
1 change: 1 addition & 0 deletions changelog/66527.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for specifying different signature verification backends in `file.managed`/`archive.extracted`
126 changes: 91 additions & 35 deletions salt/modules/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ def get_source_sum(
signed_by_all=None,
keyring=None,
gnupghome=None,
sig_backend="gpg",
):
"""
.. versionadded:: 2016.11.0
Expand Down Expand Up @@ -813,7 +814,7 @@ def get_source_sum(
source_hash_sig
When ``source`` is a remote file source and ``source_hash`` is a file,
ensure a valid GPG signature exists on the source hash file.
ensure a valid signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a
file URI retrievable by `:py:func:`cp.cache_file <salt.modules.cp.cache_file>`
for a detached one.
Expand All @@ -822,15 +823,17 @@ def get_source_sum(
signed_by_any
When verifying ``source_hash_sig``, require at least one valid signature
from one of a list of key fingerprints. This is passed to :py:func:`gpg.verify
<salt.modules.gpg.verify>`.
from one of a list of keys.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
signed_by_all
When verifying ``source_hash_sig``, require a valid signature from each
of the key fingerprints in this list. This is passed to :py:func:`gpg.verify
<salt.modules.gpg.verify>`.
of the keys in this list.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
Expand All @@ -844,6 +847,13 @@ def get_source_sum(
.. versionadded:: 3007.0
sig_backend
When verifying signatures, use this execution module as a backend.
It must be compatible with the :py:func:`gpg.verify <salt.modules.gpg.verify>` API.
Defaults to ``gpg``. All signature-related parameters are passed through.
.. versionadded:: 3008.0
CLI Example:
.. code-block:: bash
Expand Down Expand Up @@ -888,14 +898,13 @@ def _invalid_source_hash_format():
_check_sig(
hash_fn,
signature=(
source_hash_sig
if isinstance(source_hash_sig, str)
else None
source_hash_sig if source_hash_sig is not True else None
),
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
sig_backend=sig_backend,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
Expand Down Expand Up @@ -1028,29 +1037,51 @@ def _check_sig(
signed_by_all=None,
keyring=None,
gnupghome=None,
sig_backend="gpg",
saltenv="base",
verify_ssl=True,
):
try:
verify = __salt__["gpg.verify"]
verify = __salt__[f"{sig_backend}.verify"]
except KeyError:
raise CommandExecutionError(
"Signature verification requires the gpg module, "
f"Signature verification requires the {sig_backend} module, "
"which could not be found. Make sure you have the "
"necessary tools and libraries intalled (gpg, python-gnupg)"
"necessary tools and libraries intalled"
)
sig = None
# The GPG module does not understand URLs as signatures currently.
# Also, we want to ensure that, when verification fails, we get rid
# of the cached signatures.
final_sigs = None
if signature is not None:
# Fetch detached signature
sig = __salt__["cp.cache_file"](signature, saltenv, verify_ssl=verify_ssl)
if not sig:
raise CommandExecutionError(
f"Detached signature file {signature} not found"
)
sigs = [signature] if isinstance(signature, str) else signature
sigs_cached = []
final_sigs = []
for sig in sigs:
cached_sig = None
try:
urllib.parse.urlparse(sig)
except (TypeError, ValueError):
pass
else:
cached_sig = __salt__["cp.cache_file"](
sig, saltenv, verify_ssl=verify_ssl
)
if not cached_sig:
# The GPG module expects signatures as a single file path currently
if sig_backend == "gpg":
raise CommandExecutionError(
f"Detached signature file {sig} not found"
)
else:
sigs_cached.append(cached_sig)
final_sigs.append(cached_sig or sig)
if isinstance(signature, str):
final_sigs = final_sigs[0]

res = verify(
filename=on_file,
signature=sig,
signature=final_sigs,
keyring=keyring,
gnupghome=gnupghome,
signed_by_any=signed_by_any,
Expand All @@ -1061,8 +1092,9 @@ def _check_sig(
return
# Ensure detached signature and file are deleted from cache
# on signature verification failure.
if sig:
salt.utils.files.safe_rm(sig)
if signature is not None:
for sig in sigs_cached:
salt.utils.files.safe_rm(sig)
salt.utils.files.safe_rm(on_file)
raise CommandExecutionError(
f"The file's signature could not be verified: {res['message']}"
Expand Down Expand Up @@ -4705,6 +4737,7 @@ def get_managed(
ignore_ordering=False,
ignore_whitespace=False,
ignore_comment_characters=None,
sig_backend="gpg",
**kwargs,
):
"""
Expand Down Expand Up @@ -4773,7 +4806,7 @@ def get_managed(
source_hash_sig
When ``source`` is a remote file source, ``source_hash`` is a file,
``skip_verify`` is not true and ``use_etag`` is not true, ensure a
valid GPG signature exists on the source hash file.
valid signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a
file URI retrievable by `:py:func:`cp.cache_file <salt.modules.cp.cache_file>`
for a detached one.
Expand All @@ -4782,15 +4815,17 @@ def get_managed(
signed_by_any
When verifying ``source_hash_sig``, require at least one valid signature
from one of a list of key fingerprints. This is passed to :py:func:`gpg.verify
<salt.modules.gpg.verify>`.
from one of a list of keys.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
signed_by_all
When verifying ``source_hash_sig``, require a valid signature from each
of the key fingerprints in this list. This is passed to :py:func:`gpg.verify
<salt.modules.gpg.verify>`.
of the keys in this list.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
Expand Down Expand Up @@ -4837,6 +4872,13 @@ def get_managed(
.. versionadded:: 3007.0
sig_backend
When verifying signatures, use this execution module as a backend.
It must be compatible with the :py:func:`gpg.verify <salt.modules.gpg.verify>` API.
Defaults to ``gpg``. All signature-related parameters are passed through.
.. versionadded:: 3008.0
CLI Example:
.. code-block:: bash
Expand Down Expand Up @@ -4910,6 +4952,7 @@ def _get_local_file_source_sum(path):
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
sig_backend=sig_backend,
)
except CommandExecutionError as exc:
return "", {}, exc.strerror
Expand Down Expand Up @@ -6362,6 +6405,7 @@ def manage_file(
ignore_whitespace=False,
ignore_comment_characters=None,
new_file_diff=False,
sig_backend="gpg",
**kwargs,
):
"""
Expand Down Expand Up @@ -6492,7 +6536,7 @@ def manage_file(
.. versionadded:: 3005
signature
Ensure a valid GPG signature exists on the selected ``source`` file.
Ensure a valid signature exists on the selected ``source`` file.
Set this to true for inline signatures, or to a file URI retrievable
by `:py:func:`cp.cache_file <salt.modules.cp.cache_file>`
for a detached one.
Expand All @@ -6514,7 +6558,7 @@ def manage_file(
source_hash_sig
When ``source`` is a remote file source, ``source_hash`` is a file,
``skip_verify`` is not true and ``use_etag`` is not true, ensure a
valid GPG signature exists on the source hash file.
valid signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a
file URI retrievable by `:py:func:`cp.cache_file <salt.modules.cp.cache_file>`
for a detached one.
Expand All @@ -6531,15 +6575,17 @@ def manage_file(
signed_by_any
When verifying signatures either on the managed file or its source hash file,
require at least one valid signature from one of a list of key fingerprints.
This is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`.
require at least one valid signature from one of a list of keys.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
signed_by_all
When verifying signatures either on the managed file or its source hash file,
require a valid signature from each of the key fingerprints in this list.
This is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`.
require a valid signature from each of the keys in this list.
By default, this is passed to :py:func:`gpg.verify <salt.modules.gpg.verify>`,
meaning a key is identified by its fingerprint.
.. versionadded:: 3007.0
Expand Down Expand Up @@ -6592,6 +6638,13 @@ def manage_file(
.. versionadded:: 3008.0
sig_backend
When verifying signatures, use this execution module as a backend.
It must be compatible with the :py:func:`gpg.verify <salt.modules.gpg.verify>` API.
Defaults to ``gpg``. All signature-related parameters are passed through.
.. versionadded:: 3008.0
CLI Example:
.. code-block:: bash
Expand Down Expand Up @@ -6682,11 +6735,12 @@ def manage_file(
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signature=signature if signature is not True else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
sig_backend=sig_backend,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
Expand Down Expand Up @@ -6817,11 +6871,12 @@ def manage_file(
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signature=signature if signature is not True else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
sig_backend=sig_backend,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
Expand Down Expand Up @@ -6948,11 +7003,12 @@ def _set_mode_and_make_dirs(name, dir_mode, mode, user, group):
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signature=signature if signature is not True else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
sig_backend=sig_backend,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
Expand Down
Loading

0 comments on commit aaad0d2

Please sign in to comment.