21

Before apt-key was deprecated, I was using Ansible playbooks to add and update keys in my servers. At the moment, apt-key no longer updates the keys. In few searches, I found that I need to use gpg now. However, I have many servers and I don't want to do this manually for each one of them. Is there a way to manage my keyrings with gpg with Ansible?

Here are my Ansible tasks, with deprecated apt-key:

- apt_key:
  url: "https://packages.treasuredata.com/GPG-KEY-td-agent"
  state: present

- apt_repository:
  repo: "deb http://packages.treasuredata.com/3/ubuntu/{{ ansible_distribution_release }}/ {{ ansible_distribution_release }} contrib"
  state: present
  filename: "treasure-data" # Name of the pre-compiled fluentd-agent

I tried apt-key update but it is not working for me. If a key already exists but it is expired, it doesn't update it anymore.

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
ecemnaz
  • 311
  • 1
  • 2
  • 3
  • 1
    From what I read in the [documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_key_module.html#requirements), `apt_key` now uses `gpg` under the hood. What is your version of Ansible? If that is an old version, have you considered upgrading Ansible? – β.εηοιτ.βε Mar 23 '22 at 19:38
  • Hello, I am using Ansible 2.10.17 ; I can upgrade the Ansible to be honest I didn't understand under the hood comment :( – ecemnaz Mar 31 '22 at 10:20
  • What I meant there is that it is not because the module is called `apt_key` that it uses the binary `apt-key`. Actually most of Ansible modules do not uses any system OS commands, but rather Python utilities, since Ansible is all about Python. See: [_Ansible works by connecting to your nodes and pushing out small programs, called "Ansible modules" to them. These programs are written to be resource models of the desired state of the system. Ansible then executes these modules (over SSH by default), and removes them when finished._](https://www.ansible.com/overview/how-ansible-works). – β.εηοιτ.βε Mar 31 '22 at 19:10
  • 3
    But the notes https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_key_module.html#notes explicitly say that "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 it looks like `apt_key` is not meant to be used anymore... – lorenzo-bettini Apr 25 '22 at 09:42
  • Yes, I agree. It is not to be used anymore. I have to do the updates on expired keys manually on servers which takes toll on me... I couldn't find a new way of doing it. Any ideas would be really appreciated – ecemnaz Apr 26 '22 at 11:44

3 Answers3

14

In short, you need to put the GPG keys in a modern format into a separate folder that is not searched by default, and point your repository configuration at it.

For more info on why you need a separate folder, see this answer to "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead".

You can verify whether you have the old ASCII GPG format or the newer binary GPG format via file:

# file elastic-old.gpg
elastic-old.gpg: PGP public key block Public-Key (old)

# file elastic.gpg    
elastic.gpg: PGP/GPG key public ring (v4) created Mon Sep 16 17:07:54 2013 RSA (Encrypt or Sign) 2048 bits MPI=0xd70ed6cd267c5b3e...

If your key is the old format, you will need to de-armor it via gpg --dearmor elastic.gpg into the new binary format.

On Ubuntu 22.04, there's a folder you're expected to use that is not preloaded - /etc/apt/keyrings - or you can create your own directory and use that.

As for the Ansible part, you can use get_url or file to push the modern-format GPG key onto the system, and then use apt_repository like before to add the repo, with the addition of specifying the keyring.

- name: Add Example GPG key
  ansible.builtin.get_url:
    url: https://example.com/example.gpg
    dest: /etc/apt/keyrings/example.asc
    mode: '0644'
    force: true

- name: Add Example repo
  ansible.builtin.apt_repository:
    filename: example-repo
    repo: 'deb [signed-by=/etc/apt/keyrings/example.asc] https://example.com/packages/8.x/apt stable main'
double-beep
  • 5,031
  • 17
  • 33
  • 41
C0rn3j
  • 151
  • 1
  • 5
  • 2
    Is there a way to dearmor the server file (https://example.com/example.gpg) prior to saving it to the local folder (/etc/apt/keyrings/example.gpg) with Ansible? – Janthelme Jul 28 '22 at 03:06
  • 1
    Just to make the above clearer: is there a way to dearmor with, or via, Ansible? – Janthelme Jul 28 '22 at 12:12
  • 5
    As suggested in [this issue comment](https://github.com/ansible/ansible/issues/78063#issuecomment-1210837515), you can name the `dest` file with a `.asc` extension (instead of `.gpg`) to make it work without an extra dearmor step. – geerlingguy Aug 30 '22 at 15:39
  • 1
    Hint: If you create the `/etc/apt/keyrings` directory yourself as root (maybe because your OS does not ship with it), remember to give it the "o+x" permission, as it won't work otherwise. – Zero3 Dec 18 '22 at 23:40
8

To expand a bit on @geerlingguy's comment regarding using the .asc extension, this is how I ended up adding the repository for Telegraf. Take note of the use of influxdb.asc in both the get_url and apt_repository tasks.

- name: Install InfluxDB key
  get_url:
    url:  https://repos.influxdata.com/influxdb.key
    dest: /etc/apt/trusted.gpg.d/influxdb.asc

- name:  Add InfluxDB repository
  apt_repository:
    repo:  "deb [signed-by=/etc/apt/trusted.gpg.d/influxdb.asc] https://repos.influxdata.com/debian stable main"
    state: present
    update_cache: yes

- name:  Install telegraf
  package:
    name:  telegraf
    state: present

You can completely bypass the gpg --dearmor step with this method.

Joe
  • 2,352
  • 20
  • 38
  • 2
    “[The certificate MUST NOT be placed in `/etc/apt/trusted.gpg.d`](https://wiki.debian.org/DebianRepository/UseThirdParty)” – ændrük Apr 03 '23 at 23:13
0

Created a takeoff on the above contents that utilizes set_fact. Makes it easier to copy / paste to other needs using the same code.

Supply 3 vars and your off and running. I've inserted the sm- prefix just so I can clearly see my script put them there and not any other process.

- set_fact:
     repoid: nginx
     repo_key_url: https://nginx.org/keys/nginx_signing.key
     repo_sources_list_url: "http://nginx.org/packages/ubuntu {{ os_release_name }} nginx"

- name: nginx repo - add gpg key as asc so apparmor fix not needed
  get_url:
    url: "{{ repo_key_url }}"
    dest: /etc/apt/keyrings/sm-{{ repoid }}.asc
    mode: '0644'
    force: true

- name: nginx repo - add to sources.list.d 
  apt_repository:
    filename: sm-{{repoid}}-repository
    repo: 'deb [signed-by=/etc/apt/keyrings/sm-{{ repoid }}.asc] {{ repo_sources_list_url }}'
    state: present
Jay Lepore
  • 85
  • 1
  • 7