Add signature verification to file.managed/archive.extracted

This commit is contained in:
jeanluc 2023-02-02 17:07:31 +01:00 committed by Daniel Wozniak
parent da4579e3e1
commit 0ff2d2b7a8
16 changed files with 1155 additions and 4 deletions

1
changelog/63143.added Normal file
View file

@ -0,0 +1 @@
Added signature verification to file.managed/archive.extraced

View file

@ -766,6 +766,11 @@ def get_source_sum(
source_hash_name=None,
saltenv="base",
verify_ssl=True,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
):
"""
.. versionadded:: 2016.11.0
@ -806,6 +811,39 @@ def get_source_sum(
.. versionadded:: 3002
source_hash_sig
When ``source_hash`` is a file, ensure a valid GPG 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.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
keyring
When verifying ``source_hash_sig``, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying ``source_hash_sig``, use this GnuPG home.
.. versionadded:: 3007
CLI Example:
.. code-block:: bash
@ -846,6 +884,20 @@ def get_source_sum(
raise CommandExecutionError(
f"Source hash file {source_hash} not found"
)
if source_hash_sig:
_check_sig(
hash_fn,
signature=source_hash_sig
if isinstance(source_hash_sig, str)
else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
else:
if proto != "":
# Some unsupported protocol (e.g. foo://) is being used.
@ -967,6 +1019,54 @@ def check_hash(path, file_hash):
return get_hash(path, hash_type) == hash_value
def _check_sig(
on_file,
signature=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
saltenv="base",
verify_ssl=True,
):
try:
verify = __salt__["gpg.verify"]
except KeyError:
raise CommandExecutionError(
"Signature verification requires the gpg module, "
"which could not be found. Make sure you have the "
"necessary tools and libraries intalled (gpg, python-gnupg)"
)
sig = 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"
)
res = verify(
filename=on_file,
signature=sig,
keyring=keyring,
gnupghome=gnupghome,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
)
if res["res"] is True:
return
# Ensure detached signature and file are deleted from cache
# on signature verification failure.
if sig:
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']}"
)
def find(path, *args, **kwargs):
"""
Approximate the Unix ``find(1)`` command and return a list of paths that
@ -4595,6 +4695,11 @@ def get_managed(
skip_verify=False,
verify_ssl=True,
use_etag=False,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
**kwargs,
):
"""
@ -4660,6 +4765,39 @@ def get_managed(
.. versionadded:: 3005
source_hash_sig
When ``source_hash`` is a file and ``skip_verify`` is not true and ``use_etag``
is not true, ensure a valid GPG signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a file URI
retrievable by ``cp.cache_file`` for a detached one. The cached file
will be deleted if the signature verification fails.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
keyring
When verifying ``source_hash_sig``, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying ``source_hash_sig``, use this GnuPG home.
.. versionadded:: 3007
CLI Example:
.. code-block:: bash
@ -4728,6 +4866,11 @@ def get_managed(
source_hash_name,
saltenv,
verify_ssl=verify_ssl,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
)
except CommandExecutionError as exc:
return "", {}, exc.strerror
@ -5978,6 +6121,12 @@ def manage_file(
serange=None,
verify_ssl=True,
use_etag=False,
signature=None,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
**kwargs,
):
"""
@ -6107,6 +6256,53 @@ def manage_file(
.. versionadded:: 3005
signature
Ensure a valid GPG signature exists on the selected ``source`` file.
Set this to true for inline signatures, or to a file URI retrievable by
``cp.cache_file`` for a detached one. The cached file will be deleted
if the signature verification fails.
.. note::
This signature will be enforced regardless of source type and will be
required on the final output, therefore this does not lend itself well
when templates are rendered.
.. versionadded:: 3007
source_hash_sig
When ``source_hash`` is a file and ``skip_verify`` is not true and ``use_etag``
is not true, ensure a valid GPG signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a file URI
retrievable by ``cp.cache_file`` for a detached one. The cached file
will be deleted if the signature verification fails.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
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>`.
.. versionadded:: 3007
keyring
When verifying signatures, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying signatures, use this GnuPG home.
.. versionadded:: 3007
CLI Example:
.. code-block:: bash
@ -6192,6 +6388,23 @@ def manage_file(
ret["result"] = False
return ret
if signature:
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
except CommandExecutionError as err:
ret["result"] = False
ret["comment"] = f"Failed checking new file's signature: {err}"
return ret
# Print a diff equivalent to diff -u old new
if __salt__["config.option"]("obfuscate_templates"):
ret["changes"]["diff"] = "<Obfuscated Template>"
@ -6286,6 +6499,23 @@ def manage_file(
ret["result"] = False
return ret
if signature:
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
except CommandExecutionError as err:
ret["result"] = False
ret["comment"] = f"Failed checking new file's signature: {err}"
return ret
try:
salt.utils.files.copyfile(
sfn,
@ -6394,6 +6624,24 @@ def manage_file(
)
ret["result"] = False
return ret
if signature:
try:
_check_sig(
sfn,
signature=signature if isinstance(signature, str) else None,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
saltenv=saltenv,
verify_ssl=verify_ssl,
)
except CommandExecutionError as err:
ret["result"] = False
ret["comment"] = f"Failed checking new file's signature: {err}"
return ret
# It is a new file, set the diff accordingly
ret["changes"]["diff"] = "New file"
if not os.path.isdir(contain_dir):

View file

@ -165,6 +165,52 @@ def _cleanup_destdir(name):
pass
def _check_sig(
on_file,
signature,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
):
try:
verify = __salt__["gpg.verify"]
except KeyError:
raise CommandExecutionError(
"Signature verification requires the gpg module, "
"which could not be found. Make sure you have the "
"necessary tools and libraries intalled (gpg, python-gnupg)"
)
sig = None
if signature is not None:
# fetch detached signature
sig = __salt__["cp.cache_file"](signature, __env__)
if not sig:
raise CommandExecutionError(
f"Detached signature file {signature} not found"
)
res = verify(
filename=on_file,
signature=sig,
keyring=keyring,
gnupghome=gnupghome,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
)
if res["res"] is True:
return
# Ensure detached signature and file are deleted from cache
# on signature verification failure.
if sig:
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']}"
)
def extracted(
name,
source,
@ -190,7 +236,13 @@ def extracted(
enforce_ownership_on=None,
archive_format=None,
use_etag=False,
**kwargs
signature=None,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
**kwargs,
):
"""
.. versionadded:: 2014.1.0
@ -671,6 +723,47 @@ def extracted(
.. versionadded:: 3005
signature
Ensure a valid GPG signature exists on the selected ``source`` file.
This needs to be a file URI retrievable by ``cp.cache_file`` which
identifies a detached signature.
This signature will be enforced regardless of source type.
.. versionadded:: 3007
source_hash_sig
When ``source_hash`` is a file and ``skip_verify`` is not true and ``use_etag``
is not true, ensure a valid GPG signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a file URI
retrievable by ``cp.cache_file`` for a detached one. The cached file
will be deleted if the signature verification fails.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
keyring
When verifying signatures, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying signatures, use this GnuPG home.
.. versionadded:: 3007
**Examples**
1. tar with lmza (i.e. xz) compression:
@ -824,6 +917,16 @@ def extracted(
"'source_hash' is not also specified."
)
if signature or source_hash_sig:
# Fail early in case the gpg module is not present
try:
__salt__["gpg.verify"]
except KeyError:
ret[
"comment"
] = "Cannot verify signatures because the gpg module was not loaded"
return ret
try:
source_match = __salt__["file.source_list"](source, source_hash, __env__)[0]
except CommandExecutionError as exc:
@ -983,6 +1086,11 @@ def extracted(
source_hash=source_hash,
source_hash_name=source_hash_name,
saltenv=__env__,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
)
except CommandExecutionError as exc:
ret["comment"] = exc.strerror
@ -1063,6 +1171,11 @@ def extracted(
skip_verify=skip_verify,
saltenv=__env__,
use_etag=use_etag,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
)
except Exception as exc: # pylint: disable=broad-except
msg = "Failed to cache {}: {}".format(
@ -1084,6 +1197,20 @@ def extracted(
)
return result
if signature:
try:
_check_sig(
cached,
signature,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
)
except CommandExecutionError as err:
ret["comment"] = f"Failed verifying the source file's signature: {err}"
return ret
existing_cached_source_sum = _read_cached_checksum(cached)
if source_hash and source_hash_update and not skip_verify:
@ -1396,7 +1523,7 @@ def extracted(
options=options,
trim_output=trim_output,
password=password,
**kwargs
**kwargs,
)
except (CommandExecutionError, CommandNotFoundError) as exc:
ret["comment"] = exc.strerror
@ -1409,7 +1536,7 @@ def extracted(
trim_output=trim_output,
password=password,
extract_perms=extract_perms,
**kwargs
**kwargs,
)
elif archive_format == "rar":
try:

View file

@ -2315,6 +2315,12 @@ def managed(
win_perms_reset=False,
verify_ssl=True,
use_etag=False,
signature=None,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
**kwargs,
):
r"""
@ -2911,6 +2917,54 @@ def managed(
the ``source_hash`` parameter.
.. versionadded:: 3005
signature
Ensure a valid GPG signature exists on the selected ``source`` file.
Set this to true for inline signatures, or to a file URI retrievable by
``cp.cache_file`` for a detached one.
.. note::
This signature will be enforced regardless of source type and will be
required on the final output, therefore this does not lend itself well
when templates are rendered.
The file will not be modified, meaning inline signatures are not
removed.
.. versionadded:: 3007
source_hash_sig
When ``source_hash`` is a file and ``skip_verify`` is not true and ``use_etag``
is not true, ensure a valid GPG signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a file URI
retrievable by ``cp.cache_file`` for a detached one. The cached file
will be deleted if the signature verification fails.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
keyring
When verifying signatures, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying signatures, use this GnuPG home.
.. versionadded:: 3007
"""
if "env" in kwargs:
# "env" is not supported; Use "saltenv".
@ -2932,6 +2986,15 @@ def managed(
if selinux is not None and not salt.utils.platform.is_linux():
return _error(ret, "The 'selinux' option is only supported on Linux")
if signature or source_hash_sig:
# Fail early in case the gpg module is not present
try:
__salt__["gpg.verify"]
except KeyError:
_error(
ret, "Cannot verify signatures because the gpg module was not loaded"
)
if selinux:
seuser = selinux.get("seuser", None)
serole = selinux.get("serole", None)
@ -3220,6 +3283,11 @@ def managed(
serange=serange,
verify_ssl=verify_ssl,
follow_symlinks=follow_symlinks,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
**kwargs,
)
@ -3283,6 +3351,11 @@ def managed(
skip_verify,
verify_ssl=verify_ssl,
use_etag=use_etag,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
**kwargs,
)
except Exception as exc: # pylint: disable=broad-except
@ -3338,6 +3411,11 @@ def managed(
setype=setype,
serange=serange,
use_etag=use_etag,
signature=signature,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
**kwargs,
)
except Exception as exc: # pylint: disable=broad-except
@ -3417,6 +3495,11 @@ def managed(
setype=setype,
serange=serange,
use_etag=use_etag,
signature=signature,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
**kwargs,
)
except Exception as exc: # pylint: disable=broad-except
@ -8939,6 +9022,11 @@ def cached(
skip_verify=False,
saltenv="base",
use_etag=False,
source_hash_sig=None,
signed_by_any=None,
signed_by_all=None,
keyring=None,
gnupghome=None,
):
"""
.. versionadded:: 2017.7.3
@ -8996,6 +9084,38 @@ def cached(
.. versionadded:: 3005
source_hash_sig
When ``source_hash`` is a file and ``skip_verify`` is not true and ``use_etag``
is not true, ensure a valid GPG signature exists on the source hash file.
Set this to ``true`` for an inline (clearsigned) signature, or to a file URI
retrievable by ``cp.cache_file`` for a detached one. The cached file
will be deleted if the signature verification fails.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
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 ``gpg.verify``.
.. versionadded:: 3007
keyring
When verifying signatures, use this keyring.
.. versionadded:: 3007
gnupghome
When verifying signatures, use this GnuPG home.
.. versionadded:: 3007
This state will in most cases not be useful in SLS files, but it is useful
when writing a state or remote-execution module that needs to make sure
@ -9062,6 +9182,11 @@ def cached(
source_hash=source_hash,
source_hash_name=source_hash_name,
saltenv=saltenv,
source_hash_sig=source_hash_sig,
signed_by_any=signed_by_any,
signed_by_all=signed_by_all,
keyring=keyring,
gnupghome=gnupghome,
)
except CommandExecutionError as exc:
ret["comment"] = exc.strerror

View file

@ -0,0 +1 @@
9591159d86f0a180e4e0645b2320d0235e23e66c66797df61508bf185e0ac1d2 custom.tar.gz

View file

@ -0,0 +1,8 @@
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9vQUwAKCRCx+apXxJfn
HPoFA/9NbyrFi+PtSXFsVqetAc4iLQVjgYoaq2UnJNkYg9peD+lvz0qdYpWVh/4E
z5Etr3ggs+2Ff5JEpMDBopyTxsE24Dfrk64JML5s+l9VFnbOgH3QBYzDtxHqBmG6
CymTEWsFkdsWSzzBoVkym28FEJPH7q/grAtjDS9FZqqBsD4mfg==
=/ffy
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,12 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
9591159d86f0a180e4e0645b2320d0235e23e66c66797df61508bf185e0ac1d2 custom.tar.gz
-----BEGIN PGP SIGNATURE-----
iLMEAQEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9vQKwAKCRCx+apXxJfn
HKmyBADACOiaroBrJKmqy0vqrySy7iTiMdTAv75YheaXLeFgUrdktfWDzNU1kFkB
VTZRyX+og8yi7601ls1jpuDHyBz2sT93zOWvw2iIdYBRKSenh+fHS0aAeRN1Zc9b
DzivTpN6CBVJFpBBxF8Ro/gob9Pek1+rqVFt12azCdZH/hlsOw==
=9QTd
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,8 @@
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9vP4wAKCRCx+apXxJfn
HCOLA/sEH71Nph/3HVcmXfey9Fv58ekoJ16eKyfiYziBLH/mpwsdE9m3Hp7VHKlb
I0mnwsbmQnOGytsSLeepa5kwUOXw15z523N2egMXdcxyv3Vn0RRVwFw3MCd7pWRr
p8xmp2RLGuQjzHA+ymVmFiEfna2l3EknISHktMXv46jCWLL/HQ==
=mJSJ
-----END PGP SIGNATURE-----

View file

@ -0,0 +1 @@
2689de4b07720aafe8f709619f142021ae8dbdd2742a3c86701cd794f97f506c scene33

View file

@ -0,0 +1,17 @@
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9p1FwAKCRCx+apXxJfn
HBb4BACEdEmETIy6bo16qS+vh8U4WC0V6/toslO5dokBpKAaD2Xg1+mtyaUhmXTu
e6OCxqMGxVbUgmOpo4r4TX+FeqASQVNB4Kk9urwUuSa1FKZTngm+bKGnFBLbJKjm
SKZBmmvtc4iIUWZtucLJWgzbD2bv/fcEI8A/8euSrfM1ArWQHw==
=BsFN
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQSN8J3lSrZ/YDHXHW8h5Z/XBbOHgQUCY9p1KAAKCRAh5Z/XBbOH
gaoTA/0V75HNnbA/+nuaw7pBJr1EBUh74+qAb1QQvqNrNdJhgZGLlEwz43kPopUr
MxFVBaz82lQ4nxaZXT/06trjNqnaacLcvRD67iwCPTBO3UR5AEfjZlP1ahAkCRQD
DF6gLHkm00u7hXEBH3rfp5lkWt0sdHLzuiQ6YVIAwOP412fpoQ==
=2cF/
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,12 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
2689de4b07720aafe8f709619f142021ae8dbdd2742a3c86701cd794f97f506c scene33
-----BEGIN PGP SIGNATURE-----
iLMEAQEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9p0PAAKCRCx+apXxJfn
HP9QBADbGuPV6iFeij6ArezoFkfp2CTUT/wX94KXeXuKGh/8o2wH3xPRx5cDj04E
L+iU6N/MTAfVwAF+aJwRJt1pjSGWLYnUHLqxRDGRq5CTliWksLCjsV2MK0dAEU58
952bfcFq50ECOYq316Fjg/0cizaVcqyqOtwy+2ggGloqWUjkEA==
=LR1/
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,17 @@
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9p0owAKCRCx+apXxJfn
HJVmA/0QXQ8C/+XoUqzTh94+NQ8+hwaKz44LFKAAMfGAHUdKx6qGVa0rv9nIfRJT
O+4XgKiqOeUxtF4sXL9nNu5zuPkW3+prfEum1hAC/owm6z0O1WFvO9oFN25n/Q64
cvuWQEXMjJHIXF2IdDKame2+JK3KHn/Ng9bxjqdg4KmlGhhajA==
=qQmG
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQSN8J3lSrZ/YDHXHW8h5Z/XBbOHgQUCY9p02QAKCRAh5Z/XBbOH
gRwQBAC7vGMkoDFImhwmXg98gGsaxpGY2SD7rkoN/uO1u4IWufviyDULHX2K6zxa
bYPZpt6NPB385Vr3t7sEGu+gNapUmUrLPBFughaEdDvOkJhnQtq31kL4qoFIVGNs
PI093+VRCQBG7cjpNMAeRRk4TlE3BtuFQwoIaKGEPhUC/VfThQ==
=KaYP
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,107 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Scene 33
[clop clop whinny]
KNIGHT: They're nervous, sire.
ARTHUR: Then we'd best leave them here and carry on on foot. Dis-mount!
TIM: Behold the cave of Kyre Banorg!
ARTHUR: Right! Keep me covered.
KNIGHT: What with?
ARTHUR: Just keep me covered.
TIM: Too late!
[chord]
ARTHUR: What?
TIM: There he is!
ARTHUR: Where?
TIM: There!
ARTHUR: What, behind the rabbit?
TIM: It is the {{ spam }}!
ARTHUR: You silly sod! You got us all worked up!
TIM: Well, that's no ordinary rabbit. That's the most foul, cruel,
and bad-tempered rodent you ever set eyes on.
ROBIN: You tit! I soiled my armor I was so scared!
TIM: Look, that rabbit's got a vicious streak a mile wide, it's a
killer!
KNIGHT: Get stuffed!
TIM: It'll do you a trick, mate!
KNIGHT: Oh, yeah?
ROBIN: You mangy Scot git!
TIM: I'm warning you!
ROBIN: What's he do, nibble your bum?
TIM: He's got huge, sharp-- he can leap about-- look at the bones!
ARTHUR: Go on, Boris. Chop his head off!
BORIS: Right! Silly little bleeder. One rabbit stew comin' right up!
TIM: Look!
[squeak]
BORIS: Aaaugh!
[chord]
ARTHUR: Jesus Christ!
TIM: I warned you!
ROBIN: I peed again!
TIM: I warned you! But did you listen to me? Oh, no, you knew it all,
didn't you? Oh, it's just a harmless little bunny, isn't it? Well,
it's always the same, I always--
ARTHUR: Oh, shut up!
TIM: --But do they listen to me?--
ARTHUR: Right!
TIM: -Oh, no--
KNIGHTS: Charge!
[squeak squeak]
KNIGHTS: Aaaaugh! Aaaugh! etc.
KNIGHTS: Run away! Run away!
TIM: Haw haw haw. Haw haw haw. Haw haw.
ARTHUR: Right. How many did we lose?
KNIGHT: Gawain.
KNIGHT: Hector.
ARTHUR: And Boris. That's five.
GALAHAD: Three, sir.
ARTHUR: Three. Three. And we'd better not risk another frontal
assault, that rabbit's dynamite.
ROBIN: Would it help to confuse it if we run away more?
ARTHUR: Oh, shut up and go and change your armor.
GALAHAD: Let us taunt it! It may become so cross that it will make
a mistake.
ARTHUR: Like what?
GALAHAD: Well,....
ARTHUR: Have we got bows?
KNIGHT: No.
LAUNCELOT: We have the Holy Hand Grenade.
ARTHUR: Yes, of course! The Holy Hand Grenade of Antioch! 'Tis one
of the sacred relics Brother Maynard carries with him! Brother Maynard!
Bring up the Holy Hand Grenade!
[singing]
How does it, uh... how does it work?
KNIGHT: I know not, my liege.
ARTHUR: Consult the Book of Armaments!
MAYNARD: Armaments, Chapter Two, Verses Nine to Twenty-One.
BROTHER: "And Saint Atila raised the hand grenade up on high, saying,
'Oh, Lord, bless this thy hand grenade that with it thou mayest blow
thy enemies to tiny bits, in thy mercy.' And the Lord did grin, and
people did feast upon the lambs, and sloths, and carp, and anchovies,
and orangutans, and breakfast cereals, and fruit bats, and large --"
MAYNARD: Skip a bit, Brother.
BROTHER: "And the Lord spake, saying, 'First shalt thou take out the
Holy Pin. Then, shalt thou count to three, no more, no less. Three
shalt be the number thou shalt count, and the number of the counting
shalt be three. Four shalt thou not count, nor either count thou two,
excepting that thou then proceed to three. Five is right out. Once
the number three, being the third number, be reached, then lobbest thou
thy Holy Hand Grenade of Antioch towards thou foe, who being naughty
in my sight, shall snuff it.'"
MAYNARD: Amen.
ALL: Amen.
ARTHUR: Right! One... two... five!
KNIGHT: Three, sir!
ARTHUR: Three!
[boom]
-----BEGIN PGP SIGNATURE-----
iLMEAQEIAB0WIQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9p0bAAKCRCx+apXxJfn
HGt0BAC9HNKB8ticKt4xToPpiide8pa2L19msWJ9tbSp7INQiGFKU/bGBzf3Rg/j
41BCL4L9dszcmpoa147pG3/A7UPyYzbA9fT3AvjX9Cc2OIzgmWwzSaccbZVFyctC
Z9x6uIWrCY/AiSekoIWri4Xwi0u2TzTxRKaraHASxYzdcV111w==
=0u4u
-----END PGP SIGNATURE-----

View file

@ -42,11 +42,21 @@ def grail_scene33_file(grail):
return grail / "scene33"
@pytest.fixture
def grail_scene33_clearsign_file(grail_scene33_file):
return grail_scene33_file.with_suffix(".clearsign.asc")
@pytest.fixture
def grail_scene33_file_hash(grail_scene33_file):
return hashlib.sha256(grail_scene33_file.read_bytes()).hexdigest()
@pytest.fixture
def grail_scene33_clearsign_file_hash(grail_scene33_clearsign_file):
return hashlib.sha256(grail_scene33_clearsign_file.read_bytes()).hexdigest()
@pytest.fixture(scope="module")
def state_file_account():
with pytest.helpers.create_account(create_group=True) as system_account:

View file

@ -3,13 +3,22 @@ import hashlib
import os
import shutil
import stat
import subprocess
import types
import psutil
import pytest
import salt.utils.files
import salt.utils.platform
try:
import gnupg as gnupglib
HAS_GNUPG = True
except ImportError:
HAS_GNUPG = False
pytestmark = [
pytest.mark.windows_whitelisted,
]
@ -19,14 +28,125 @@ BINARY_FILE = b"GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\
@pytest.fixture
def remote_grail_scene33(webserver, grail_scene33_file, grail_scene33_file_hash):
def remote_grail_scene33(
webserver,
grail_scene33_file,
grail_scene33_file_hash,
grail_scene33_clearsign_file,
grail_scene33_clearsign_file_hash,
):
return types.SimpleNamespace(
file=grail_scene33_file,
file_clearsign=grail_scene33_clearsign_file,
hash=grail_scene33_file_hash,
hash_clearsign=grail_scene33_clearsign_file_hash,
hash_file=grail_scene33_file.with_suffix(".SHA256"),
url=webserver.url("grail/scene33"),
url_hash=webserver.url("grail/scene33.SHA256"),
)
@pytest.fixture
def gpghome(tmp_path):
root = tmp_path / "gpghome"
root.mkdir(mode=0o0700)
try:
yield root
finally:
# Make sure we don't leave any gpg-agents running behind
gpg_connect_agent = shutil.which("gpg-connect-agent")
if gpg_connect_agent:
gnupghome = root / ".gnupg"
if not gnupghome.is_dir():
gnupghome = root
try:
subprocess.run(
[gpg_connect_agent, "killagent", "/bye"],
env={"GNUPGHOME": str(gnupghome)},
shell=False,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
# This is likely CentOS 7 or Amazon Linux 2
pass
# If the above errored or was not enough, as a last resort, let's check
# the running processes.
for proc in psutil.process_iter():
try:
if "gpg-agent" in proc.name():
for arg in proc.cmdline():
if str(root) in arg:
proc.terminate()
except Exception: # pylint: disable=broad-except
pass
@pytest.fixture
def gnupg(gpghome):
return gnupglib.GPG(gnupghome=str(gpghome))
@pytest.fixture
def a_pubkey():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY9pxawEEAPpBbXxRYFUm6np5h746Nch7+OrbLdtBxP8x7VDOockr/x7drssb
llVFuK4HmiJg+Nkyakn3XmVYBHY2yBIkN/MP+R1zRxiFmniKOTD15UuHSQaWZTqh
qac6XrLZ20BiWl1fKweCz1wGUcMZaOBs0WVB0sIupqfS90Ub93VC/+oxABEBAAG0
JlNhbHRTdGFjayBBIFRlc3QgPGF0ZXN0QHNhbHRzdGFjay5jb20+iNEEEwEIADsW
IQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9pxawIbAwULCQgHAgIiAgYVCgkICwIE
FgIDAQIeBwIXgAAKCRCx+apXxJfnHFDhA/47t5yYdCcjxXu/1Kn9sQwI+aq/S3x9
/ZKE+RodlryqA43BUT7N6JLQ5zJO6p+kRhMwCcVfBeDNJANqVi63HEDp8q3633BF
q1Cbi3BG0ugBdCADIETYBwl/ytMSgYwRO8b4TkYCyhWuWAgliVF3ceX0AVsng8pF
o6Vh4A3SqosQgA==
=eHpb
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def a_fp():
return "F8CCC8CB5E1D8868DAA5859AB1F9AA57C497E71C"
@pytest.fixture
def b_pubkey():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY9pxigEEANbHCh566IEbp9Ez1WE3oEi+XXyf7H3GDgrVc8v9COMexpAFkJa1
gG+yCm4bOZ5vHAXbP2rGvlOEcao3y3evj2TWahg0+05CDugRjL0pO4JcMUBV1mBZ
ynUGoQ5T+WtKilJ5k/JrSRpJW3y//46q0g5c470qVNn9ZX0YZW/b7DFXABEBAAG0
JlNhbHRTdGFjayBCIFRlc3QgPGJ0ZXN0QHNhbHRzdGFjay5jb20+iNEEEwEIADsW
IQSN8J3lSrZ/YDHXHW8h5Z/XBbOHgQUCY9pxigIbAwULCQgHAgIiAgYVCgkICwIE
FgIDAQIeBwIXgAAKCRAh5Z/XBbOHgTCuA/9mYXAehM9avvq0Jm2dVbPidqxLstki
tgo3gCWmO1b5dXEBrhOZ8pZAktQ3WWoRrbwpNA7NAEIDF5l6uwMLLbGPQ5jreOdP
uzHpHONR1WWAzw2dj3v+5IcLDQ4sLi9VRgJqtMasTd8TpqMCVNcMArDBiy5hRF/e
XWEkf19Nb8qrdg==
=OEiT
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def b_fp():
return "8DF09DE54AB67F6031D71D6F21E59FD705B38781"
@pytest.fixture
def gpg_keys_present(gnupg, a_pubkey, b_pubkey, a_fp, b_fp):
pubkeys = [a_pubkey, b_pubkey]
fingerprints = [a_fp, b_fp]
gnupg.import_keys("\n".join(pubkeys))
present_keys = gnupg.list_keys()
for fp in fingerprints:
assert any(x["fingerprint"] == fp for x in present_keys)
yield
# cleanup is taken care of by gpghome and tmp_path
def _format_ids(key, value):
return "{}={}".format(key, value)
@ -801,6 +921,113 @@ def test_verify_ssl_https_source(file, tmp_path, ssl_webserver, verify_ssl):
assert name.exists()
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
@pytest.mark.parametrize("signature", [True, ".asc"])
def test_file_managed_signature(
file, tmp_path, signature, remote_grail_scene33, gpghome
):
name = tmp_path / "test_file_managed_signature.txt"
source = remote_grail_scene33.url
if signature is True:
source += ".clearsign.asc"
contents_file = remote_grail_scene33.file_clearsign
source_hash = remote_grail_scene33.hash_clearsign
else:
signature = source + signature
contents_file = remote_grail_scene33.file
source_hash = remote_grail_scene33.hash
ret = file.managed(
str(name),
source=source,
source_hash=source_hash,
signature=signature,
gnupghome=str(gpghome),
)
assert ret.result is True
assert ret.changes
assert name.exists()
assert name.read_text() == contents_file.read_text()
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
def test_file_managed_signature_fail(
file, tmp_path, remote_grail_scene33, gpghome, modules
):
name = tmp_path / "test_file_managed_signature_fail.txt"
source = remote_grail_scene33.url
signature = source + ".asc"
source_hash = remote_grail_scene33.hash
# although there are valid signatures, this will be denied since the one below is required
signed_by_all = ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"]
ret = file.managed(
str(name),
source=source,
source_hash=source_hash,
signature=signature,
gnupghome=str(gpghome),
signed_by_all=signed_by_all,
)
assert ret.result is False
assert not ret.changes
assert not name.exists()
# Ensure that a new state run will attempt to redownload the source
# instead of verifying the invalid signature again
assert not modules.cp.is_cached(source)
assert not modules.cp.is_cached(signature)
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
@pytest.mark.parametrize("sig", [True, ".asc"])
def test_file_managed_source_hash_sig(
file, tmp_path, sig, remote_grail_scene33, gpghome
):
name = tmp_path / "test_file_managed_source_hash_sig.txt"
source = remote_grail_scene33.url
source_hash = remote_grail_scene33.url_hash
contents_file = remote_grail_scene33.file
if sig is True:
source_hash += ".clearsign.asc"
else:
sig = source_hash + sig
ret = file.managed(
str(name),
source=source,
source_hash=source_hash,
source_hash_sig=sig,
gnupghome=str(gpghome),
)
assert ret.result is True
assert ret.changes
assert name.exists()
assert name.read_text() == contents_file.read_text()
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
def test_file_managed_source_hash_sig_fail(
file, tmp_path, remote_grail_scene33, gpghome
):
name = tmp_path / "test_file_managed_source_hash_sig.txt"
source = remote_grail_scene33.url
source_hash = remote_grail_scene33.url_hash
sig = source_hash + ".asc"
signed_by_all = ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"]
ret = file.managed(
str(name),
source=source,
source_hash=source_hash,
source_hash_sig=sig,
gnupghome=str(gpghome),
signed_by_all=signed_by_all,
)
assert ret.result is False
assert not ret.changes
assert not name.exists()
def test_issue_60203(
file,
tmp_path,

View file

@ -6,12 +6,23 @@ import os
import random
import shutil
import socket
import subprocess
import sys
from contextlib import closing
from pathlib import Path
import psutil
import pytest
import salt.utils.files
from tests.support.runtests import RUNTIME_VARS
try:
import gnupg as gnupglib
HAS_GNUPG = True
except ImportError:
HAS_GNUPG = False
class TestRequestHandler(http.server.SimpleHTTPRequestHandler):
@ -224,3 +235,222 @@ def test_archive_extracted_web_source_etag_operation(
# The modified time of the cached file now changes
assert cached_file_mtime != os.path.getmtime(cached_file)
@pytest.fixture
def gpghome(tmp_path):
root = tmp_path / "gpghome"
root.mkdir(mode=0o0700)
try:
yield root
finally:
# Make sure we don't leave any gpg-agents running behind
gpg_connect_agent = shutil.which("gpg-connect-agent")
if gpg_connect_agent:
gnupghome = root / ".gnupg"
if not gnupghome.is_dir():
gnupghome = root
try:
subprocess.run(
[gpg_connect_agent, "killagent", "/bye"],
env={"GNUPGHOME": str(gnupghome)},
shell=False,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
# This is likely CentOS 7 or Amazon Linux 2
pass
# If the above errored or was not enough, as a last resort, let's check
# the running processes.
for proc in psutil.process_iter():
try:
if "gpg-agent" in proc.name():
for arg in proc.cmdline():
if str(root) in arg:
proc.terminate()
except Exception: # pylint: disable=broad-except
pass
@pytest.fixture
def gnupg(gpghome):
return gnupglib.GPG(gnupghome=str(gpghome))
@pytest.fixture
def a_pubkey():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY9pxawEEAPpBbXxRYFUm6np5h746Nch7+OrbLdtBxP8x7VDOockr/x7drssb
llVFuK4HmiJg+Nkyakn3XmVYBHY2yBIkN/MP+R1zRxiFmniKOTD15UuHSQaWZTqh
qac6XrLZ20BiWl1fKweCz1wGUcMZaOBs0WVB0sIupqfS90Ub93VC/+oxABEBAAG0
JlNhbHRTdGFjayBBIFRlc3QgPGF0ZXN0QHNhbHRzdGFjay5jb20+iNEEEwEIADsW
IQT4zMjLXh2IaNqlhZqx+apXxJfnHAUCY9pxawIbAwULCQgHAgIiAgYVCgkICwIE
FgIDAQIeBwIXgAAKCRCx+apXxJfnHFDhA/47t5yYdCcjxXu/1Kn9sQwI+aq/S3x9
/ZKE+RodlryqA43BUT7N6JLQ5zJO6p+kRhMwCcVfBeDNJANqVi63HEDp8q3633BF
q1Cbi3BG0ugBdCADIETYBwl/ytMSgYwRO8b4TkYCyhWuWAgliVF3ceX0AVsng8pF
o6Vh4A3SqosQgA==
=eHpb
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def a_fp():
return "F8CCC8CB5E1D8868DAA5859AB1F9AA57C497E71C"
@pytest.fixture
def b_pubkey():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY9pxigEEANbHCh566IEbp9Ez1WE3oEi+XXyf7H3GDgrVc8v9COMexpAFkJa1
gG+yCm4bOZ5vHAXbP2rGvlOEcao3y3evj2TWahg0+05CDugRjL0pO4JcMUBV1mBZ
ynUGoQ5T+WtKilJ5k/JrSRpJW3y//46q0g5c470qVNn9ZX0YZW/b7DFXABEBAAG0
JlNhbHRTdGFjayBCIFRlc3QgPGJ0ZXN0QHNhbHRzdGFjay5jb20+iNEEEwEIADsW
IQSN8J3lSrZ/YDHXHW8h5Z/XBbOHgQUCY9pxigIbAwULCQgHAgIiAgYVCgkICwIE
FgIDAQIeBwIXgAAKCRAh5Z/XBbOHgTCuA/9mYXAehM9avvq0Jm2dVbPidqxLstki
tgo3gCWmO1b5dXEBrhOZ8pZAktQ3WWoRrbwpNA7NAEIDF5l6uwMLLbGPQ5jreOdP
uzHpHONR1WWAzw2dj3v+5IcLDQ4sLi9VRgJqtMasTd8TpqMCVNcMArDBiy5hRF/e
XWEkf19Nb8qrdg==
=OEiT
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def b_fp():
return "8DF09DE54AB67F6031D71D6F21E59FD705B38781"
@pytest.fixture
def gpg_keys_present(gnupg, a_pubkey, b_pubkey, a_fp, b_fp):
pubkeys = [a_pubkey, b_pubkey]
fingerprints = [a_fp, b_fp]
gnupg.import_keys("\n".join(pubkeys))
present_keys = gnupg.list_keys()
for fp in fingerprints:
assert any(x["fingerprint"] == fp for x in present_keys)
yield
# cleanup is taken care of by gpghome and tmp_path
@pytest.fixture(scope="module", autouse=True)
def sig_files_present(web_root, modules):
base = Path(RUNTIME_VARS.BASE_FILES)
for file in [
"custom.tar.gz",
"custom.tar.gz.asc",
"custom.tar.gz.SHA256",
"custom.tar.gz.SHA256.clearsign.asc",
"custom.tar.gz.SHA256.asc",
]:
modules.file.copy(base / file, Path(web_root) / file)
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
def test_archive_extracted_signature(tmp_path, gpghome, free_port, modules, states):
name = tmp_path / "test_archive_extracted_signature"
source = f"http://localhost:{free_port}/custom.tar.gz"
signature = source + ".asc"
source_hash = source + ".SHA256"
ret = states.archive.extracted(
str(name),
source=source,
source_hash=source_hash,
archive_format="tar",
options="z",
signature=signature,
gnupghome=str(gpghome),
)
assert ret.result is True
assert ret.changes
assert name.exists()
assert modules.file.find(str(name))
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
def test_archive_extracted_signature_fail(
tmp_path, gpghome, free_port, modules, states
):
name = tmp_path / "test_archive_extracted_signature_fail"
source = f"http://localhost:{free_port}/custom.tar.gz"
signature = source + ".asc"
source_hash = source + ".SHA256"
# although there are valid signatures, this will be denied since the one below is required
signed_by_all = ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"]
ret = states.archive.extracted(
str(name),
source=source,
source_hash=source_hash,
archive_format="tar",
options="z",
signature=signature,
signed_by_all=signed_by_all,
gnupghome=str(gpghome),
)
assert ret.result is False
assert not ret.changes
assert not name.exists()
assert not modules.cp.is_cached(source)
assert not modules.cp.is_cached(signature)
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
@pytest.mark.parametrize("sig", [True, ".asc"])
def test_archive_extracted_source_hash_sig(
tmp_path, sig, gpghome, free_port, modules, states
):
name = tmp_path / "test_archive_extracted_source_hash_sig"
source = f"http://localhost:{free_port}/custom.tar.gz"
source_hash = source + ".SHA256"
if sig is True:
source_hash += ".clearsign.asc"
else:
sig = source_hash + sig
ret = states.archive.extracted(
str(name),
source=source,
source_hash=source_hash,
archive_format="tar",
options="z",
source_hash_sig=sig,
gnupghome=str(gpghome),
)
assert ret.result is True
assert ret.changes
assert name.exists()
assert modules.file.find(str(name))
@pytest.mark.skipif(HAS_GNUPG is False, reason="Needs python-gnupg library")
@pytest.mark.usefixtures("gpg_keys_present")
@pytest.mark.parametrize("sig", [True, ".asc"])
def test_archive_extracted_source_hash_sig_fail(
tmp_path, sig, gpghome, free_port, modules, states
):
name = tmp_path / "test_archive_extracted_source_hash_sig_fail"
source = f"http://localhost:{free_port}/custom.tar.gz"
source_hash = source + ".SHA256.clearsign.asc"
signed_by_any = ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"]
ret = states.archive.extracted(
str(name),
source=source,
source_hash=source_hash,
archive_format="tar",
options="z",
source_hash_sig=True,
signed_by_any=signed_by_any,
gnupghome=str(gpghome),
)
assert ret.result is False
assert not ret.changes
assert not name.exists()
assert not modules.cp.is_cached(source)
assert not modules.cp.is_cached(source_hash)