Search code examples
ansible

How to install deb822 apt repositories using ansible's apt_repository module?


I have started using ansible in the past year or so to automate the setup of my development environment in WSL Ubuntu distros. However I have avoided the usage of apt_repository since in Ubuntu 20 it was trying to install repository keys to the deprecated /etc/apt/trusted.gpg, while Ubuntu 20 and Ubuntu 22 with add-apt-repository would install repository keys to /etc/apt/trusted.gpg.d/. A number of answers here on stackoverflow encourage storing keys to etc/apt/keyring rather than /etc/apt/trusted.gpg.d/ which they say is deprecated, but Ubuntu 20 and Ubuntu 22 will always install keys to /etc/apt/trusted.gpg.d/ while considering /etc/apt/trusted.gpg to be deprecated.

I have now been testing adding repositories to Ubuntu 24, and I see it installs keys directly inside /etc/apt/sources.list.d in the same file as the repository, for example sudo add-apt-repository ppa:ondrej/php will result in a /etc/apt/sources.list.d/ondrej-ubuntu-php-noble.sources file with contents:

Types: deb
URIs: https://ppa.launchpadcontent.net/ondrej/php/ubuntu/
Suites: noble
Components: main
Signed-By:
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 .
 mQINBGYo0vEBEAC0Semxy5I2b8exRUxJfTKkHR4f5uyS0dTd9vYgMI5T3gsa7ypH
 HtE+GiZC+T9m/F9h66+XJMxhuNsKRs7T2In5NSeso9H/ytlSTayUaBtCFfRp6y6b
 6ozuRBfqYJGxhjAnIzvNF/Wpp2BvfQm3OrQ7uJJrt5IvzLDC4jPxl/Xs3sTT+Hbk
 bkKKprZ3xmy2enuwBaNWR/CUtAz3hbkzL1kGbhX9m3QidFJagVVdDw3aNEwo8ush
 djWfF+BajNvpDFYJKBGQbCeagB753Baa5yIN62x+THLnLiKTMDS1e7U0ZDiV9671
 noTbtN5TeZeyfsEmeZ8X60x11JIP3yYHYZT70/DyTYX3WC9yQFyIgVOfRlGklMKI
 k3TLMmtq8w5Hz1vovwzV7PzaQnmY+uNP2ZbAP4fJ3iFAj0L+u0i1nOFgTy0Lq058
 O/FjRrQxuceDDCF+9ThspXMw3Puvz8giuBDCdEda84uC7XWMdqgz/maLfFQjAmyP
 Ixi1EMxMlHYyZajpR1cdCfrAIQlnQjHSWmyeCFgXPPfRA71aCcJ7oSrDjogW6Ahd
 HRkQRKf1FF9BFzycgSQotfR+7CKfPQh1kghufM9W/spARzA709nGZjXJzgEJLQd3
 CDB6dIIxT/0YI36h3Qgfmiiw4twO24MMEqEEPIELz2WJKeWGkdQdcekpxQARAQAB
 tB9MYXVuY2hwYWQgUFBBIGZvciBPbmTFmWVqIFN1csO9iQJOBBMBCgA4FiEEuNx+
 U5RmVu+85MHdcdrqq0rUyrYFAmYo0vECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC
 F4AACgkQcdrqq0rUyrYOPQ/+IArA4s1J3op/w7cXek0ieFHWHFDrxPYS+78/LF/J
 LoYZw0nIU5Ovr+LzehFMIQU6esgPXwbeCVgwLwat57augAkAYWT0UzH5dE6RKAGr
 C2vsHWVfPhQn6UndfzwXc0mTLGQni25aQaZ6k60Dbm/vblejrTQrtAUWoMO3Z1cr
 NDGJ3Z9DCxtr2o9gRYUI6HwLHJtobTIeI5xsr5x+GvXiIAVCPa3ZEuRL6jMQfqfS
 C43mpuiS1kGgsnQLs2DbN7EFCfiJoNX1QzZu25zg+IS9PXbCJnheZWnH0rwUSb/N
 hZPcSefGlNlhr824OfT30v79hQnw59XbsfV270O9jPbD4kttN+OiszbU66zsuiOh
 BO46XCckQPqDkBMw56GPFuVrQgGb1thXvn67URJgPyJhwauBWKPNAJ9Ojuo+yVq/
 hdR1VNWThXQbZgaGSWrbjt6FdYtQb9VX88uu5gFDmr180HogHNUDUcqNLLdnjfFs
 4DyJlusQ5I/a7cQ7nlkNgxAmHszwO/mGLBuGljDUYkwZDW9nqP1Q5Q2jMtrhgXvR
 2SOtufvecUbB7+eoRSaOnu7CNMATG6LocFEMzhKUde1uZTfWSqnYEcdqoFJMi46y
 qaNxhiNLsQ5OBMbgSp2zCbQxRBdITMVvBR5YjCetUIGEs6T1yQ5wh5Xpoi34ShHn
 v38=
 =kFlZ
 -----END PGP PUBLIC KEY BLOCK-----

So I'm wondering (seeing the apparent lack of ansible documentation in these regards), how does ansible now in 2024 handle apt repositories in Ubuntu 24 environments?

UPDATE: Here is some more information from my current tests. Running this task in ansible on Ubuntu 24:

- name: Verify that the Ondrej PHP PPA is present
  apt_repository:
    repo: ppa:ondrej/php
    state: present

I can see that it is still using the deprecated apt-key and trying to install public keys to the deprecated /etc/apt/trusted.gpg.

fatal: [localhost]: FAILED! => {"changed": false, "cmd": "/usr/bin/apt-key adv --recv-keys --no-tty --keyserver hkp://keyserver.ubuntu.com:80 B8DC7E53946656EFBCE4C1DD71DAEAAB4AD4CAB6", "msg": "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).\ngpg: key 71DAEAAB4AD4CAB6: public key \"Launchpad PPA for Ondřej Surý\" imported\ngpg: Total number processed: 1\ngpg:               imported: 1\n/usr/bin/apt-key: 391: cannot create /etc/apt/trusted.gpg: Permission denied\ncat: /tmp/apt-key-gpghome.1pZUPb5Fp7/gpgoutput.log: No such file or directory", "rc": 1, "stderr": "Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).\ngpg: key 71DAEAAB4AD4CAB6: public key \"Launchpad PPA for Ondřej Surý\" imported\ngpg: Total number processed: 1\ngpg:               imported: 1\n/usr/bin/apt-key: 391: cannot create /etc/apt/trusted.gpg: Permission denied\ncat: /tmp/apt-key-gpghome.1pZUPb5Fp7/gpgoutput.log: No such file or directory\n", "stderr_lines": ["Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).", "gpg: key 71DAEAAB4AD4CAB6: public key \"Launchpad PPA for Ondřej Surý\" imported", "gpg: Total number processed: 1", "gpg:               imported: 1", "/usr/bin/apt-key: 391: cannot create /etc/apt/trusted.gpg: Permission denied", "cat: /tmp/apt-key-gpghome.1pZUPb5Fp7/gpgoutput.log: No such file or directory"], "stdout": "Executing: /tmp/apt-key-gpghome.1pZUPb5Fp7/gpg.1.sh --recv-keys --no-tty --keyserver hkp://keyserver.ubuntu.com:80 B8DC7E53946656EFBCE4C1DD71DAEAAB4AD4CAB6\n", "stdout_lines": ["Executing: /tmp/apt-key-gpghome.1pZUPb5Fp7/gpg.1.sh --recv-keys --no-tty --keyserver hkp://keyserver.ubuntu.com:80 B8DC7E53946656EFBCE4C1DD71DAEAAB4AD4CAB6"]}

So the question is: how should apt repositories be added correctly in Ubuntu distros using ansible?


Solution

  • Taking into account @vladimir-botka's comment which set me on the right track to finding a solution to adding apt repositories in Ubuntu 24.04 using Ansible, I also found @geerlingguy's blog post entitled apt_key deprecated in Debian/Ubuntu - how to fix in Ansible, with it's 2023 update that mentions Ansible's new ansible.builtin.deb822_repository. Upon attempting to use this module I found that it requires the python3-debian package:

    sudo apt install python3-debian
    

    Or better yet, using Ansible within a python virtual environment:

    pip install python-debian
    

    Here is my working example of installing Ondrej Sury's PHP PPA as closely as the system would when running sudo add-apt-respository ppa:ondrej/php:

    - name: Manage PHP PPA repository (deb822_repository)
      become: true
      ansible.builtin.deb822_repository:
        state: present
        name: "ondrej-ubuntu-php-{{ansible_distribution_release}}"
        types: [deb]
        uris: [https://ppa.launchpadcontent.net/ondrej/php/ubuntu]
        suites: ["{{ ansible_facts['distribution_release'] }}"]
        components: [main]
        signed_by: |
          -----BEGIN PGP PUBLIC KEY BLOCK-----
          .
          mQINBGYo0vEBEAC0Semxy5I2b8exRUxJfTKkHR4f5uyS0dTd9vYgMI5T3gsa7ypH
          HtE+GiZC+T9m/F9h66+XJMxhuNsKRs7T2In5NSeso9H/ytlSTayUaBtCFfRp6y6b
          6ozuRBfqYJGxhjAnIzvNF/Wpp2BvfQm3OrQ7uJJrt5IvzLDC4jPxl/Xs3sTT+Hbk
          bkKKprZ3xmy2enuwBaNWR/CUtAz3hbkzL1kGbhX9m3QidFJagVVdDw3aNEwo8ush
          djWfF+BajNvpDFYJKBGQbCeagB753Baa5yIN62x+THLnLiKTMDS1e7U0ZDiV9671
          noTbtN5TeZeyfsEmeZ8X60x11JIP3yYHYZT70/DyTYX3WC9yQFyIgVOfRlGklMKI
          k3TLMmtq8w5Hz1vovwzV7PzaQnmY+uNP2ZbAP4fJ3iFAj0L+u0i1nOFgTy0Lq058
          O/FjRrQxuceDDCF+9ThspXMw3Puvz8giuBDCdEda84uC7XWMdqgz/maLfFQjAmyP
          Ixi1EMxMlHYyZajpR1cdCfrAIQlnQjHSWmyeCFgXPPfRA71aCcJ7oSrDjogW6Ahd
          HRkQRKf1FF9BFzycgSQotfR+7CKfPQh1kghufM9W/spARzA709nGZjXJzgEJLQd3
          CDB6dIIxT/0YI36h3Qgfmiiw4twO24MMEqEEPIELz2WJKeWGkdQdcekpxQARAQAB
          tB9MYXVuY2hwYWQgUFBBIGZvciBPbmTFmWVqIFN1csO9iQJOBBMBCgA4FiEEuNx+
          U5RmVu+85MHdcdrqq0rUyrYFAmYo0vECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC
          F4AACgkQcdrqq0rUyrYOPQ/+IArA4s1J3op/w7cXek0ieFHWHFDrxPYS+78/LF/J
          LoYZw0nIU5Ovr+LzehFMIQU6esgPXwbeCVgwLwat57augAkAYWT0UzH5dE6RKAGr
          C2vsHWVfPhQn6UndfzwXc0mTLGQni25aQaZ6k60Dbm/vblejrTQrtAUWoMO3Z1cr
          NDGJ3Z9DCxtr2o9gRYUI6HwLHJtobTIeI5xsr5x+GvXiIAVCPa3ZEuRL6jMQfqfS
          C43mpuiS1kGgsnQLs2DbN7EFCfiJoNX1QzZu25zg+IS9PXbCJnheZWnH0rwUSb/N
          hZPcSefGlNlhr824OfT30v79hQnw59XbsfV270O9jPbD4kttN+OiszbU66zsuiOh
          BO46XCckQPqDkBMw56GPFuVrQgGb1thXvn67URJgPyJhwauBWKPNAJ9Ojuo+yVq/
          hdR1VNWThXQbZgaGSWrbjt6FdYtQb9VX88uu5gFDmr180HogHNUDUcqNLLdnjfFs
          4DyJlusQ5I/a7cQ7nlkNgxAmHszwO/mGLBuGljDUYkwZDW9nqP1Q5Q2jMtrhgXvR
          2SOtufvecUbB7+eoRSaOnu7CNMATG6LocFEMzhKUde1uZTfWSqnYEcdqoFJMi46y
          qaNxhiNLsQ5OBMbgSp2zCbQxRBdITMVvBR5YjCetUIGEs6T1yQ5wh5Xpoi34ShHn
          v38=
          =kFlZ
          -----END PGP PUBLIC KEY BLOCK-----
    

    This will result in a /etc/apt/sources.list.d/ondrej-ubuntu-php-noble.sources file named just as it would have been by running sudo add-apt-repository.

    Also, Ubuntu 24.04 in adopting the deb822 repository format no longer stores the repository public keys in /etc/apt/trusted.gpg.d/, but stores the key for a repository directly in the repository .source file as shown in the original question.

    The resulting contents of the repository .source file after running the ansible task:

    Components: main
    X-Repolib-Name: ondrej-ubuntu-php-noble
    Signed-By:
        -----BEGIN PGP PUBLIC KEY BLOCK-----
        .
        mQINBGYo0vEBEAC0Semxy5I2b8exRUxJfTKkHR4f5uyS0dTd9vYgMI5T3gsa7ypH
        HtE+GiZC+T9m/F9h66+XJMxhuNsKRs7T2In5NSeso9H/ytlSTayUaBtCFfRp6y6b
        6ozuRBfqYJGxhjAnIzvNF/Wpp2BvfQm3OrQ7uJJrt5IvzLDC4jPxl/Xs3sTT+Hbk
        bkKKprZ3xmy2enuwBaNWR/CUtAz3hbkzL1kGbhX9m3QidFJagVVdDw3aNEwo8ush
        djWfF+BajNvpDFYJKBGQbCeagB753Baa5yIN62x+THLnLiKTMDS1e7U0ZDiV9671
        noTbtN5TeZeyfsEmeZ8X60x11JIP3yYHYZT70/DyTYX3WC9yQFyIgVOfRlGklMKI
        k3TLMmtq8w5Hz1vovwzV7PzaQnmY+uNP2ZbAP4fJ3iFAj0L+u0i1nOFgTy0Lq058
        O/FjRrQxuceDDCF+9ThspXMw3Puvz8giuBDCdEda84uC7XWMdqgz/maLfFQjAmyP
        Ixi1EMxMlHYyZajpR1cdCfrAIQlnQjHSWmyeCFgXPPfRA71aCcJ7oSrDjogW6Ahd
        HRkQRKf1FF9BFzycgSQotfR+7CKfPQh1kghufM9W/spARzA709nGZjXJzgEJLQd3
        CDB6dIIxT/0YI36h3Qgfmiiw4twO24MMEqEEPIELz2WJKeWGkdQdcekpxQARAQAB
        tB9MYXVuY2hwYWQgUFBBIGZvciBPbmTFmWVqIFN1csO9iQJOBBMBCgA4FiEEuNx+
        U5RmVu+85MHdcdrqq0rUyrYFAmYo0vECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC
        F4AACgkQcdrqq0rUyrYOPQ/+IArA4s1J3op/w7cXek0ieFHWHFDrxPYS+78/LF/J
        LoYZw0nIU5Ovr+LzehFMIQU6esgPXwbeCVgwLwat57augAkAYWT0UzH5dE6RKAGr
        C2vsHWVfPhQn6UndfzwXc0mTLGQni25aQaZ6k60Dbm/vblejrTQrtAUWoMO3Z1cr
        NDGJ3Z9DCxtr2o9gRYUI6HwLHJtobTIeI5xsr5x+GvXiIAVCPa3ZEuRL6jMQfqfS
        C43mpuiS1kGgsnQLs2DbN7EFCfiJoNX1QzZu25zg+IS9PXbCJnheZWnH0rwUSb/N
        hZPcSefGlNlhr824OfT30v79hQnw59XbsfV270O9jPbD4kttN+OiszbU66zsuiOh
        BO46XCckQPqDkBMw56GPFuVrQgGb1thXvn67URJgPyJhwauBWKPNAJ9Ojuo+yVq/
        hdR1VNWThXQbZgaGSWrbjt6FdYtQb9VX88uu5gFDmr180HogHNUDUcqNLLdnjfFs
        4DyJlusQ5I/a7cQ7nlkNgxAmHszwO/mGLBuGljDUYkwZDW9nqP1Q5Q2jMtrhgXvR
        2SOtufvecUbB7+eoRSaOnu7CNMATG6LocFEMzhKUde1uZTfWSqnYEcdqoFJMi46y
        qaNxhiNLsQ5OBMbgSp2zCbQxRBdITMVvBR5YjCetUIGEs6T1yQ5wh5Xpoi34ShHn
        v38=
        =kFlZ
        -----END PGP PUBLIC KEY BLOCK-----
    Suites: noble
    Types: deb
    URIs: https://ppa.launchpadcontent.net/ondrej/php/ubuntu
    

    This result is practically identical to the system add-apt-repository with the sole difference that the system command does not add an X-Repolib-Name: key while the ansible task does. For the rest it's identical.

    The only drawback here is that you would need to know the public key in ASCII format ahead of time and include it as a string within the ansible task. The closest I can come so far to automating retrieval of the key in ASCII format as a one-liner on the command line is:

    export TMPGNUPG=$(mktemp -d) && GNUPGHOME=$TMPGNUPG gpg -q --keyserver keyserver.ubuntu.com --recv-keys B8DC7E53946656EFBCE4C1DD71DAEAAB4AD4CAB6 && GNUPGHOME=$TMPGNUPG gpg -ao - --export B8DC7E53946656EFBCE4C1DD71DAEAAB4AD4CAB6 && rm -R $TMPGNUPG && unset TMPGNUPG
    

    Now, if we also want to automate the retrieval of the public key associated with a PPA without knowing it ahead of time or without having it's fingerprint, we can scrape the repository webpage to extract the key fingerprint and subsequently retrieve the public key, given that repository archive pages on the launchpad website all have a <script> element that contains a JSON object assigned to a variable LP.cache, and we can find the fingerprint within that JSON representation:

    - name: Extract signing key fingerprint from Launchpad PPA page
      hosts: localhost
      tasks:
        - name: Download PPA page content
          ansible.builtin.set_fact:
            ppa_page_content: "{{ lookup('ansible.builtin.url', 'https://launchpad.net/~ondrej/+archive/ubuntu/php/') }}"
          
        - name: Extract JSON from the PPA page content using regex
          ansible.builtin.set_fact:
            ppa_json_cache: "{{ ppa_page_content | regex_search('LP\\.cache = (\\{.*\\});</script>', '\\1') }}"
    
        - name: Parse JSON to extract signing key fingerprint
          ansible.builtin.set_fact:
            signing_key_fingerprint: "{{ ppa_json_cache | from_json | json_query('context.signing_key_fingerprint') }}"
          
        - name: Display the signing key fingerprint
          ansible.builtin.debug:
            msg: "The signing key fingerprint is: {{ signing_key_fingerprint }}"
    
        - name: Retrieve ASCII representation of the key using the signing key fingerprint
          ansible.builtin.set_fact:
            signing_key_ascii: "{{ lookup('ansible.builtin.url', 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x{{ signing_key_fingerprint }}') }}"
    

    Now we can substitute the hardcoded ASCII string in the previous Manage PHP PPA repository (deb822_repository) task with the signging_key_ascii variable, and voilà we have an automated process.

    UPDATE 7 Sept. 2024: I just found out that launchpad actually has an API, 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:

    ---
    - name: Make API request to Launchpad
      uri:
        url: "https://api.launchpad.net/devel/~ondrej/+archive/ubuntu/php"
        return_content: yes
        method: GET
        headers:
          Accept: "application/json"
      register: launchpad_response
    
    - name: Parse JSON to extract signing key fingerprint
      ansible.builtin.set_fact:
        signing_key_fingerprint: "{{ launchpad_response.json.signing_key_fingerprint }}"