2023 Update: Ansible now has the
ansible.builtin.deb822_repository
module, which can add keys and repositories in one task. It's a little more complex than the old way, and requires Ansible 2.15 or later. See some commondeb822_repository
examples here, for example, the Jenkins tasks below can be consolidated (though the structure of the templated vars would need reworking):- name: Add Jenkins repo using key from URL. ansible.builtin.deb822_repository: name: jenkins types: [deb] uris: "https://pkg.jenkins.io/debian-stable" components: [binary] signed_by: https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key state: present enabled: true
For many packages, like Elasticsearch, Docker, or Jenkins, you need to install a trusted GPG key on your system before you can install from the official package repository.
Traditionally, you'd run a command like:
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
But if you do that in modern versions of Debian or Ubuntu, you get the following warning:
Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).
This way of adding apt keys still works for now (in mid-2022), but will stop working in the next major releases of Ubuntu and Debian (and derivatives). So it's better to stop the usage now. In Ansible, you would typically use the ansible.builtin.apt_key
module, but even that module has the following deprecation warning:
The apt-key command has been deprecated and suggests to ‘manage keyring files in trusted.gpg.d instead’. See the Debian wiki for details. This module is kept for backwards compatiblity for systems that still use apt-key as the main way to manage apt repository keys.
So traditionally, I would use a task like the following in my Ansible roles and playbooks:
- name: Add Jenkins apt repository key.
ansible.builtin.apt_key:
url: https://pkg.jenkins.io/debian-stable/jenkins.io.key
state: present
- name: Add Jenkins apt repository.
ansible.builtin.apt_repository:
repo: "deb https://pkg.jenkins.io/debian-stable binary/"
state: present
The new way to do this without adding an extra gpg --dearmor
task is to use get_url
to download the file into the trusted.gpg.d
folder with the .asc
filename. Therefore the first task above can be replaced with:
- name: Add Jenkins apt repository key.
ansible.builtin.get_url:
url: "{{ jenkins_repo_key_url }}"
dest: /etc/apt/trusted.gpg.d/jenkins.asc
mode: '0644'
force: true
See this issue in ansible/ansible for a little more background.
Comments
Just a quick note: keys in `/etc/apt/trusted.gpg.d` are trusted across all repos. The recommended approach is to put the key in `/usr/share/keyrings` and have the repo refer to it using the `signed-by` option.
If the key is in it's binary form , it needs to be with .gpg extension and we don't have to dearmor it
Why not just do this ???
wget -qO - https://somepath.com/keys.html | sudo gpg --dearmor --output /etc/apt/trusted.gpg.d/somebody.gpg
In Ansible, a command like that requires a lot of extra work to make idempotent, and it's not fun breaking idempotence :)
When you're doing it one-off, it's not a problem.
apt-key add --keyring /etc/apt/trusted.gpg.d/jenkins.pgp ....
- name: Add Jenkins apt repository key.
ansible.builtin.apt_key:
url: https://pkg.jenkins.io/debian-stable/jenkins.io.key
keyring: /etc/apt/trusted.gpg.d/jenkins.pgp
state: present
As an example/ echo Indrek and aduzsardi:
- name: Add gh cli signing key
get_url:
url: "https://cli.github.com/packages/githubcli-archive-keyring.gpg"
dest: /usr/share/keyrings/githubcli-archive-keyring.gpg
mode: 0644
force: true
- name: Add gh cli repo
apt_repository:
repo: "deb [arch=amd64 signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main"
filename: github_cli
state: present
update_cache: True
- name: Install gh cli
apt:
name: gh
state: present
What if the url sends 404 but not as http header?
with get_url and force you will break the local key
And how do I verify the apt_key.id? Without that it is like doing a Bungee Jump without rope.
The new method is the builtin.deb822_repository module.
Works very good, but needs cleanup from old configuration if a packet source was previously configured via apt_key and apt_repository.
Note that usage of the
deb822_repository
module requires thepython3-debian
package. This can be installed in a virtual environment withpip install python-debian
.Here is my working example of installing Ondrej Sury's PHP PPA as closely as the system would when using
add-apt-respository
:In following up on the previous comment, I'm thinking about ways to automate retrieval of a key from the ubuntu keyserver and successive export in ASCII format. Here's a command shell one liner that I've come up with to automate the retrieval of the key in ASCII format on
stdOut
:This could be run and registered in an ansible task to retrieve the ASCII string from
stdOut
.Perhaps a simpler solution is to use the URL https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x to retrieve the key, and that could probably be used with
get_url
to save the key in ASCII format. This would then require a furtheransible.builtin.file lookup
to read the contents of the.asc
file into a variable.In any case you would still need to have the key fingerprint ahead of time. I suppose one way of retrieving the key fingerprint would be something along the lines of:
As long as launchpad.net doesn't decide to change or remove the
LP.cache
object from archive pages, we can translate this into an automated ansible task:I just found out that launchpad actually has an API (https://api.launchpad.net/devel.html) that makes retrieval of the public signing key fingerprint much easier without having to scrape the archive webpage. Here's an example that substitutes the scraping step above with a call to the API:
Trying to use the jenkins example in an ubuntu22.04.5 system produced the following error:
E:Malformed entry 1 in sources file /etc/apt/sources.list.d/jenkins.sources (Suite), E:The list of sources could not be read.
X-Repolib-Name: jenkins
Types: deb
URIs: https://pkg.jenkins.io/debian-stable
Components: binary
Signed-By: /etc/apt/keyrings/jenkins.asc
Enabled: yes
it was missing suites: [binary]
should look like:
- name: Add Jenkins repo using key from URL.
ansible.builtin.deb822_repository:
name: jenkins
types: [deb]
uris: "https://pkg.jenkins.io/debian-stable"
components: [binary]
suites: [binary]
signed_by: https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
state: present
enabled: true