diff --git a/ansible/inventory/group_vars/all/apt b/ansible/inventory/group_vars/all/apt index 904e05176..80218456c 100644 --- a/ansible/inventory/group_vars/all/apt +++ b/ansible/inventory/group_vars/all/apt @@ -45,3 +45,12 @@ apt_repositories: [] # when replacing the distribution repositories via apt_repositories. # Default is false. apt_disable_sources_list: false + +# List of Apt auth configurations. Each item is a dict with the following keys: +# * machine: 'machine' entry in the auth file +# * login: 'login' entry in the auth file +# * password: 'password' entry in the auth file +# * filename: Name of a file in /etc/apt/auth.conf.d in which to store +# the auth configuration. The extension should be ``.conf``. +# Default is an empty list. +apt_auth: [] diff --git a/ansible/roles/apt/defaults/main.yml b/ansible/roles/apt/defaults/main.yml index 39013cd67..de6c4142b 100644 --- a/ansible/roles/apt/defaults/main.yml +++ b/ansible/roles/apt/defaults/main.yml @@ -48,3 +48,12 @@ apt_repositories: [] # when replacing the distribution repositories via apt_repositories. # Default is false. apt_disable_sources_list: false + +# List of Apt auth configurations. Each item is a dict with the following keys: +# * machine: 'machine' entry in the auth file +# * login: 'login' entry in the auth file +# * password: 'password' entry in the auth file +# * filename: Name of a file in /etc/apt/auth.conf.d in which to store +# the auth configuration. The extension should be ``.conf``. +# Default is an empty list. +apt_auth: [] diff --git a/ansible/roles/apt/files/auth_schema.json b/ansible/roles/apt/files/auth_schema.json new file mode 100644 index 000000000..977350822 --- /dev/null +++ b/ansible/roles/apt/files/auth_schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "List of Apt auth configurations", + "type": "array", + "items": { + "description": "Apt auth configuration", + "type": "object", + "required": ["machine", "login", "password", "filename"], + "properties": { + "machine": { + "type": "string", + "minLength": 1 + }, + "login": { + "type": "string", + "minLength": 1 + }, + "password": { + "type": "string", + "minLength": 1 + }, + "filename": { + "type": "string", + "minLength": 1 + } + } + } +} diff --git a/ansible/roles/apt/tasks/auth.yml b/ansible/roles/apt/tasks/auth.yml new file mode 100644 index 000000000..9718ebfc1 --- /dev/null +++ b/ansible/roles/apt/tasks/auth.yml @@ -0,0 +1,32 @@ +--- +- name: Validate Apt auth config + ansible.utils.validate: + criteria: "{{ lookup('ansible.builtin.file', 'auth_schema.json') }}" + data: "{{ apt_auth }}" + +- name: Ensure the Apt auth.conf.d directory exists + ansible.builtin.file: + path: "/etc/apt/auth.conf.d" + state: directory + owner: root + group: root + mode: 0755 + become: true + +- name: Configure Apt auth files + ansible.builtin.template: + src: "auth.conf.j2" + dest: "/etc/apt/auth.conf.d/{{ auth.filename }}" + owner: root + group: root + mode: 0600 + become: true + # apt_auth contains sensitive data, so iterate over indices to avoid exposing + # them in Ansible output. + loop: "{{ apt_auth | map(attribute='filename') }}" + loop_control: + index_var: auth_index + vars: + auth: "{{ apt_auth[auth_index] }}" + notify: + - Update apt cache diff --git a/ansible/roles/apt/tasks/main.yml b/ansible/roles/apt/tasks/main.yml index b4cb8f636..7bdf2cf01 100644 --- a/ansible/roles/apt/tasks/main.yml +++ b/ansible/roles/apt/tasks/main.yml @@ -6,3 +6,5 @@ - import_tasks: keys.yml - import_tasks: repos.yml + +- import_tasks: auth.yml diff --git a/ansible/roles/apt/templates/auth.conf.j2 b/ansible/roles/apt/templates/auth.conf.j2 new file mode 100644 index 000000000..a5abd10ae --- /dev/null +++ b/ansible/roles/apt/templates/auth.conf.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} + +machine {{ auth.machine }} +login {{ auth.login }} +password {{ auth.password }} diff --git a/doc/source/configuration/reference/hosts.rst b/doc/source/configuration/reference/hosts.rst index 19edabdb2..4dd7075ee 100644 --- a/doc/source/configuration/reference/hosts.rst +++ b/doc/source/configuration/reference/hosts.rst @@ -444,6 +444,39 @@ that is signed by the key. components: all signed_by: example-key.asc +Apt auth configuration +---------------------- + +Some repositories may require authentication using HTTP basic auth. Apt +supports specifying credentials in URLs in ``sources.list`` files, but these +files must be world-readable. A more secure setup involves writing credentials +to `auth.conf +`__ +files which can have more restrictive permissions. + +Auth configuration is defined by the ``apt_auth`` variable. The format is a +list, with each item mapping to a dict/map with the following items: + +* ``machine``: ``machine`` entry in the auth file +* ``login``: ``machine`` entry in the auth file +* ``password``: ``machine`` entry in the auth file +* ``filename``: Name of a file in ``/etc/apt/auth.conf.d`` in which to store + the auth configuration. The extension should be ``.conf``. + +The default value of ``apt_auth`` is an empty list. + +In the following example, credentials are provided for package repositories at +apt.example.com. + +.. code-block:: yaml + :caption: ``apt.yml`` + + apt_auth: + - machine: apt.example.com + login: my-username + password: my-password + filename: example.conf + Development tools ================= *tags:* diff --git a/etc/kayobe/apt.yml b/etc/kayobe/apt.yml index 9a9d88538..e4bb5b179 100644 --- a/etc/kayobe/apt.yml +++ b/etc/kayobe/apt.yml @@ -46,6 +46,17 @@ # Default is false. #apt_disable_sources_list: +# List of Apt auth configurations. Each item is a dict with the following keys: +# * machine: 'machine' entry in the auth file +# * login: 'login' entry in the auth file +# * password: 'password' entry in the auth file +# * filename: Name of a file in which to store the auth configuration. The +# extension should be '.conf'. +# * filename: Name of a file in /etc/apt/auth.conf.d in which to store +# the auth configuration. The extension should be ``.conf``. +# Default is an empty list. +#apt_auth: + ############################################################################### # Dummy variable to allow Ansible to accept this file. workaround_ansible_issue_8743: yes diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 index 2d7828f05..01497d1e1 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 +++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 @@ -146,6 +146,11 @@ apt_repositories: components: contrib signed_by: td-agent.asc apt_disable_sources_list: true +apt_auth: + - machine: https://apt.example.com + login: foo + password: bar + filename: test.conf {% endif %} {% if ansible_facts.os_family == 'RedHat' %} diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py index 1c9c1024a..f0ed4721a 100644 --- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py +++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py @@ -219,6 +219,17 @@ def test_apt_custom_package_repository_is_available(host): assert host.package("td-agent").is_installed +@pytest.mark.skipif(not _is_apt(), reason="Apt only supported on Ubuntu") +def test_apt_auth(host): + apt_auth = host.file("/etc/apt/auth.conf.d/test.conf") + assert apt_auth.exists + with host.sudo(): + auth_lines = apt_auth.content_string.splitlines() + assert "machine https://apt.example.com" in auth_lines + assert "login foo" in auth_lines + assert "password bar" in auth_lines + + @pytest.mark.parametrize('repo', ["appstream", "baseos", "extras", "epel"]) @pytest.mark.skipif(not _is_dnf_mirror(), reason="DNF OpenDev mirror only for CentOS 8") def test_dnf_local_package_mirrors(host, repo): diff --git a/releasenotes/notes/apt-auth-97d0291600836dec.yaml b/releasenotes/notes/apt-auth-97d0291600836dec.yaml new file mode 100644 index 000000000..6da3d2e39 --- /dev/null +++ b/releasenotes/notes/apt-auth-97d0291600836dec.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for auth configuration for Apt respositories and proxies using + ``auth.conf`` files.