How to sign an Ansible project?
From a non-signed to 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
- GPG signature
- command line
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, you sign a project using a GPG signature.
Playbook
- GPG sign a project
I’m going to show you how to sign 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 without any GPG signature files.
By the end of this Playbook, you are obtaining a GPG-signed 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
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. create a MANIFEST.in file
When the MANIFEST.in file is not present, we obtain the following message on the screen:
$ ansible-sign project gpg-sign .
[ERROR] Could not find a MANIFEST.in file in the specified project.
[NOTE ] If you are attempting to sign a project, please create this file.
[NOTE ] See the ansible-sign documentation for more information.
When some parts of the MANIFEST.in file is not correct; we obtain the following error on the screen:
[ERROR] An error was encountered while parsing MANIFEST.in: 'recursive-include' expects <dir> <pattern1> <pattern2> ...
[NOTE ] You can use the --debug global flag to view the full traceback.
3. 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 ...
4. GPG key successfully created
When no GPG key is present the ansible-sign command shows the following error:
$ ansible-sign project gpg-sign .
[2023-01-13 19:39:18] WARNING:gnupg:potential problem: FAILURE: sign 17
[2023-01-13 19:39:18] WARNING:gnupg:gpg returned a non-zero error code: 2
[ERROR] GPG signing FAILED!
[NOTE ] Re-run with the global --debug flag for more information.
[NOTE ] Checksum manifest: ./.ansible-sign/sha256sum.txt
[NOTE ] GPG summary: None
We can specify a debugging feature for more information:
gpg: no default secret key: No secret key\n[GNUPG:] INV_SGNR 9
The full stack trace:
$ ansible-sign --debug project gpg-sign .
[2023-01-13 19:39:37] DEBUG:ansible_sign.cli:Running requested command/passing to function
[2023-01-13 19:39:37] DEBUG:ansible_sign.cli:Full calculated checksum manifest (.):
da18c88f14299362a2073b4f340ae5abec55e280af82b9e1b399dfabef079983 MANIFEST.in
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763 inventory
1c666ccae8a05445d2c8b36341dec1671093999d995944e2ecdce671fc474f7c playbooks/ping.yml
[2023-01-13 19:39:37] INFO:ansible_sign.cli:Wrote to file: ./.ansible-sign/sha256sum.txt
[2023-01-13 19:39:37] DEBUG:gnupg:96724: gpg --status-fd 2 --no-tty --no-verbose --fixed-list-mode --batch --with-colons --version
[2023-01-13 19:39:37] DEBUG:gnupg:stderr reader: <Thread(Thread-1 (_read_response), initial daemon)>
[2023-01-13 19:39:37] DEBUG:gnupg:stdout reader: <Thread(Thread-2 (_read_data), initial daemon)>
[2023-01-13 19:39:37] DEBUG:gnupg:sign_file: <_io.BufferedReader name='./.ansible-sign/sha256sum.txt'>
[2023-01-13 19:39:37] DEBUG:gnupg:96725: gpg --status-fd 2 --no-tty --no-verbose --fixed-list-mode --batch --with-colons -sa --detach-sign --output ./.ansible-sign/sha256sum.txt.sig
[2023-01-13 19:39:37] DEBUG:gnupg:data copier: <Thread(Thread-3 (_copy_data), initial daemon)>, <_io.BufferedReader name='./.ansible-sign/sha256sum.txt'>, <_io.BufferedWriter name=5>
[2023-01-13 19:39:37] DEBUG:gnupg:closed output, 239 bytes sent
[2023-01-13 19:39:37] DEBUG:gnupg:stderr reader: <Thread(Thread-4 (_read_response), initial daemon)>
[2023-01-13 19:39:37] DEBUG:gnupg:stdout reader: <Thread(Thread-5 (_read_data), initial daemon)>
[2023-01-13 19:39:37] DEBUG:gnupg:gpg: no default secret key: No secret key
[2023-01-13 19:39:37] DEBUG:gnupg:[GNUPG:] INV_SGNR 9
[2023-01-13 19:39:37] DEBUG:gnupg:message ignored: INV_SGNR, 9
[2023-01-13 19:39:37] DEBUG:gnupg:[GNUPG:] FAILURE sign 17
[2023-01-13 19:39:37] WARNING:gnupg:potential problem: FAILURE: sign 17
[2023-01-13 19:39:37] DEBUG:gnupg:gpg: signing failed: No secret key
[2023-01-13 19:39:37] WARNING:gnupg:gpg returned a non-zero error code: 2
[ERROR] GPG signing FAILED!
[NOTE ] Re-run with the global --debug flag for more information.
[NOTE ] Checksum manifest: ./.ansible-sign/sha256sum.txt
[NOTE ] GPG summary: None
[2023-01-13 19:39:37] DEBUG:ansible_sign.cli:GPG Details: {'stderr': 'gpg: no default secret key: No secret key\n[GNUPG:] INV_SGNR 9\n[GNUPG:] FAILURE sign 17\ngpg: signing failed: No secret key\n', 'fingerprint': None, 'hash_algo': None, 'returncode': 2}
[2023-01-13 19:39:37] INFO:ansible_sign.cli:Script ends here, rc=4
We can create our GPG key using the command line
$ gpg --generate-key
gpg (GnuPG) 2.4.0; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: Luca Berton
Email address: [email protected]
You selected this USER-ID:
"Luca Berton <[email protected]>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /Users/lberton/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/Users/lberton/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/lberton/.gnupg/openpgp-revocs.d/D5F7F6089E75CFE10DBF50ECC64CA1040CBCE82C.rev'
public and secret key created and signed.
pub ed25519 2023-01-13 [SC] [expires: 2025-01-12]
D5F7F6089E75CFE10DBF50ECC64CA1040CBCE82C
uid Luca Berton <[email protected]>
sub cv25519 2023-01-13 [E] [expires: 2025-01-12]
5. Sign our project directory
When all the previous steps is satisfied, we obtain a successful execution:
$ ansible-sign project gpg-sign .
[OK ] GPG signing successful!
[NOTE ] Checksum manifest: ./.ansible-sign/sha256sum.txt
[NOTE ] GPG summary: signature created
Verification
The output is two files under the .ansible-sign
directory:
- sha256sum.txt
- sha256sum.txt.sig
The content of the sha256sum.txt
file:
8fda56fd3288141367f151fcaf8e3fca5d4b46cfe3ba7d8dfc66b17205284efd MANIFEST.in
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763 inventory
1c666ccae8a05445d2c8b36341dec1671093999d995944e2ecdce671fc474f7c playbooks/ping.yml
The content of the sha256sum.txt.sig
file:
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQTV9/YInnXP4Q2/UOzGTKEEDLzoLAUCY8G45gAKCRDGTKEEDLzo
LOIxAP9G0zYcy4CvtkuxfDAjeG98+ok+iXpkV9Dejx6HxEz1iAD/R+yFvpPfNxvC
5HBzKa4EEkySXZpJ3yrah9MogBKX+QQ=
=zl60
-----END PGP SIGNATURE-----
Conclusion
Now you know how to sign an Ansible project 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