How to verify an Ansible project signature?
A step to step guide to verify the signature using the ansible-sign
of a GPG-signed Ansible project.
I’m going to show you a live Playbook with some simple Ansible code.
I’m Luca Berton, and welcome to today’s episode of Ansible Pilot.
ansible-sign
- available since 2022
- command line
- GPG signature
The ansible-sign
command has been available since 2022 for installation in the most modern operating system.
It is a command line tool so simplify the Project signing process using your terminal.
Using the ansible-sign
command, we can verify the GPG signature of an Ansible project.
Playbook
- GPG sign verification a project
I’m going to show you how to verify the signature of an Ansible project using the ansible-sign command line utility. At the beginning of this example, we start with a project with all our Ansible files already signed with a GPG signature. By the end of this Playbook, we will verify if the signature is correct for the current Ansible project directory.
Project directory files:
- playbooks/ping.yml
---
- name: ping module Playbook
hosts: all
tasks:
- name: test connection
ansible.builtin.ping:
- inventory
localhost ansible_connection=local
- MANIFEST.in
recursive-exclude .git *
include inventory
recursive-include playbooks *.yml
Project signature files:
.ansible-sign/sha256sum.txt
8fda56fd3288141367f151fcaf8e3fca5d4b46cfe3ba7d8dfc66b17205284efd MANIFEST.in
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763 inventory
1c666ccae8a05445d2c8b36341dec1671093999d995944e2ecdce671fc474f7c playbooks/ping.yml
.ansible-sign/sha256sum.txt.sig
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQRFr0MY44L/nHcn7m7siNPNNZbCMAUCY8KpMQAKCRDsiNPNNZbC
MJvaAP97QUbnymXHaco5ZnF6vrReOc/7C7e8YDxSfh+6fsjbPAEAsnk4t4jTihkm
O15QNa+3EwelTLjjeZkHfFyY1YPD6QE=
=4rnz
-----END PGP SIGNATURE-----
1. install ansible-sign
Verify if the ansible-sign command is available in your terminal. When you obtain a command not found error, you should install it.
$ ansible-sign
command not found: ansible-sign
When the package is not available on our favorite package manager (apt, DNF, yum, zypper, brew, conda), we can rely on the PIP Python package manager:
$ pip3 install ansible-sign
Expected output:
$ pip3 install ansible-sign
Collecting ansible-sign
Downloading ansible_sign-0.1.1-py3-none-any.whl (15 kB)
Requirement already satisfied: distlib in /opt/homebrew/lib/python3.10/site-packages (from ansible-sign) (0.3.6)
Requirement already satisfied: python-gnupg in /opt/homebrew/lib/python3.10/site-packages (from ansible-sign) (0.5.0)
Installing collected packages: ansible-sign
Successfully installed ansible-sign-0.1.1
By the end of this step, the command will be available with the following output:
$ ansible-sign
usage: ansible-sign [-h] [--version] [--debug] [--nocolor] CONTENT_TYPE ...
ansible-sign: error: the following arguments are required: CONTENT_TYPE
2. ensure the GPG utility is installed
When the GPG utility (gpg
command) is not present in our system, we obtain the following message on the screen:
OSError: Unable to run gpg (gpg) - it may not be available.
The full error stack track is the following:
$ ansible-sign project gpg-sign .
[2023-01-13 19:38:25] ERROR:gnupg:Unable to run gpg (gpg) - it may not be available.
Traceback (most recent call last):
File "/opt/homebrew/lib/python3.10/site-packages/gnupg.py", line 941, in __init__
p = self._open_subprocess(['--version'])
File "/opt/homebrew/lib/python3.10/site-packages/gnupg.py", line 1007, in _open_subprocess
result = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=si, env=self.env)
File "/opt/homebrew/Cellar/[email protected]/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/subprocess.py", line 971, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/opt/homebrew/Cellar/[email protected]/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/subprocess.py", line 1847, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'gpg'
Traceback (most recent call last):
File "/opt/homebrew/lib/python3.10/site-packages/gnupg.py", line 941, in __init__
p = self._open_subprocess(['--version'])
File "/opt/homebrew/lib/python3.10/site-packages/gnupg.py", line 1007, in _open_subprocess
result = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=si, env=self.env)
File "/opt/homebrew/Cellar/[email protected]/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/subprocess.py", line 971, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/opt/homebrew/Cellar/[email protected]/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/subprocess.py", line 1847, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'gpg'During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/homebrew/bin/ansible-sign", line 8, in <module>
sys.exit(run())
File "/opt/homebrew/lib/python3.10/site-packages/ansible_sign/cli.py", line 374, in run
return main(sys.argv[1:])
File "/opt/homebrew/lib/python3.10/site-packages/ansible_sign/cli.py", line 364, in main
exitcode = cli.run_command()
File "/opt/homebrew/lib/python3.10/site-packages/ansible_sign/cli.py", line 46, in run_command
return self.args.func()
File "/opt/homebrew/lib/python3.10/site-packages/ansible_sign/cli.py", line 346, in gpg_sign
result = signer.sign()
File "/opt/homebrew/lib/python3.10/site-packages/ansible_sign/signing/gpg/signer.py", line 45, in sign
gpg = gnupg.GPG(gnupghome=self.gpg_home)
File "/opt/homebrew/lib/python3.10/site-packages/gnupg.py", line 945, in __init__
raise OSError(msg)
OSError: Unable to run gpg (gpg) - it may not be available
We can install it using our favorite package manager (apt, DNF, yum, zypper, brew):
$ brew install gpg
[...]
==> Installing gnupg
A successful installation report makes the gpg command available and usable:
$ gpg
gpg: directory '/Users/lberton/.gnupg' created
gpg: keybox '/Users/lberton/.gnupg/pubring.kbx' created
gpg: WARNING: no command supplied. Trying to guess what you mean ...
gpg: Go ahead and type your message ...
3. GPG key successfully imported
Make sure that the key used by the Ansible project is successfully imported into the verification system. We can list all the GPG keys using the following command:
$ gpg --list-keys
/Users/lberton/.gnupg/pubring.kbx
---------------------------------
pub ed25519 2023-01-14 [SC] [expires: 2025-01-13]
45AF4318E382FF9C7727EE6EEC88D3CD3596C230
uid [ultimate] Luca Berton <[email protected]>
sub cv25519 2023-01-14 [E] [expires: 2025-01-13]
We can easily import a GPG public key using the command:
$ gpg --import luca.gpg
gpg: key 45AF4318: public key imported
gpg: Total number processed: 1
gpg: imported: 1
The GPG public key was previously exported using the command:
$ gpg --armor --export [email protected]
4. Ansible Project verification
A successful Ansible project verification is like the following:
$ ansible-sign project gpg-verify .
[OK ] GPG signature verification succeeded.
[OK ] Checksum validation succeeded.
All our files are the same as the creator; the checksum matches, and the signature is valid.
File mismatch
When additional files are present or removed in the current directory the ansible-sign utility print the full list on the screen:
$ ansible-sign project gpg-verify .
[OK ] GPG signature verification succeeded.
[ERROR] Checksum validation failed.
[ERROR] {'added': ['.DS_Store'], 'removed': []}
In the above output, the additional file .DS_Store
is present in the current directory and not present in the Ansible checksum manifest.
Signature mismatch
When the signature is mismatched or invalid, we obtain the following message:
$ ansible-sign project gpg-verify .
[2023-01-14 13:41:38] WARNING:gnupg:gpg returned a non-zero error code: 2
[ERROR] GPG signature verification failed.
[NOTE ] Re-run with the global --debug flag for more information.
The root cause might be a wrong GPG signature, a file tempered, or a mismatch. Something that we should definitely investigate.
Full debug output
[2023-01-14 14:13:31] DEBUG:ansible_sign.cli:Running requested command/passing to function
[2023-01-14 14:13:31] DEBUG:gnupg:20291: gpg --status-fd 2 --no-tty --no-verbose --fixed-list-mode --batch --with-colons --version
[2023-01-14 14:13:31] DEBUG:gnupg:stderr reader: <Thread(Thread-1 (_read_response), initial daemon)>
[2023-01-14 14:13:31] DEBUG:gnupg:stdout reader: <Thread(Thread-2 (_read_data), initial daemon)>
[2023-01-14 14:13:31] DEBUG:gnupg:verify_file: <_io.BufferedReader name='./.ansible-sign/sha256sum.txt.sig'>, './.ansible-sign/sha256sum.txt'
[2023-01-14 14:13:31] DEBUG:gnupg:Handling detached verification
[2023-01-14 14:13:31] DEBUG:gnupg:Wrote to temp file: b'-----BEGIN PGP SIGNATURE-----\n\nMJvaAP97QUbnymXHaco5ZnF6vrReOc/7C7e8YDxSfh+6fsjbPAEAsnk4t4jTihkm\nO15QNa+3EwelTLjjeZkHfFyY1YPD6QE=\n=4rnz\n-----END PGP SIGNATURE-----\n'
[2023-01-14 14:13:31] DEBUG:gnupg:20292: gpg --status-fd 2 --no-tty --no-verbose --fixed-list-mode --batch --with-colons --verify /var/folders/mp/q66z6zq57f18s7s6gvw6zkwh0000gn/T/pygpg-ab8lbsju ./.ansible-sign/sha256sum.txt
[2023-01-14 14:13:31] DEBUG:gnupg:stderr reader: <Thread(Thread-3 (_read_response), initial daemon)>
[2023-01-14 14:13:31] DEBUG:gnupg:stdout reader: <Thread(Thread-4 (_read_data), initial daemon)>
[2023-01-14 14:13:31] DEBUG:gnupg:gpg: CRC error; 4DD76C - E2B9F3
[2023-01-14 14:13:31] DEBUG:gnupg:gpg: [don't know]: invalid packet (ctb=30)
[2023-01-14 14:13:31] DEBUG:gnupg:[GNUPG:] NODATA 3
[2023-01-14 14:13:31] DEBUG:gnupg:[GNUPG:] NODATA 4
[2023-01-14 14:13:31] DEBUG:gnupg:gpg: no signature found
[2023-01-14 14:13:31] DEBUG:gnupg:gpg: the signature could not be verified.
[2023-01-14 14:13:31] DEBUG:gnupg:Please remember that the signature file (.sig or .asc)
[2023-01-14 14:13:31] DEBUG:gnupg:should be the first file given on the command line.
[2023-01-14 14:13:31] WARNING:gnupg:gpg returned a non-zero error code: 2
[ERROR] GPG signature verification failed.
[NOTE ] Re-run with the global --debug flag for more information.
[2023-01-14 14:13:31] DEBUG:ansible_sign.cli:{'stderr': "gpg: CRC error; 4DD76C - E2B9F3\ngpg: [don't know]: invalid packet (ctb=30)\n[GNUPG:] NODATA 3\n[GNUPG:] NODATA 4\ngpg: no signature found\ngpg: the signature could not be verified.\nPlease remember that the signature file (.sig or .asc)\nshould be the first file given on the command line.\n"}
[2023-01-14 14:13:31] INFO:ansible_sign.cli:Script ends here, rc=3
Conclusion
Now you know how to verify an Ansible project signed with a GPG signature.
Subscribe to the YouTube channel, Medium, and Website, X (formerly Twitter) to not miss the next episode of the Ansible Pilot.Academy
Learn the Ansible automation technology with some real-life examples in my Udemy 300+ Lessons Video Course.
My book Ansible By Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps
Donate
Want to keep this project going? Please donate