diff --git a/changelogs/fragments/apt-verify-checksum.yml b/changelogs/fragments/apt-verify-checksum.yml new file mode 100644 index 00000000000..a027ad3509a --- /dev/null +++ b/changelogs/fragments/apt-verify-checksum.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - "`ansible.builtin.apt` learned to be able to verify packages specified via the `deb` parameter using `:` syntax" diff --git a/lib/ansible/modules/apt.py b/lib/ansible/modules/apt.py index 705616bef00..ae5a5a03643 100644 --- a/lib/ansible/modules/apt.py +++ b/lib/ansible/modules/apt.py @@ -145,6 +145,20 @@ options: type: path required: false version_added: "1.6" + checksum: + description: + - 'If a checksum is passed to this parameter, the digest of the + `deb` will be calculated (after it is downloaded) to ensure + its integrity and verify that the transfer completed successfully. + Format: :, for example C(checksum="sha256:D98291AC[...]B6DC7B97").' + - If you worry about portability, only the sha1 algorithm is available + on all platforms and python versions. + - The Python C(hashlib) module is responsible for providing the available algorithms. + The choices vary based on Python version and OpenSSL version. + - On systems running in FIPS compliant mode, the C(md5) algorithm may be unavailable. + type: str + required: false + version_added: "2.21" autoremove: description: - If V(true), remove unused dependency packages for all module states except V(build-dep). It can also be used as the only option. @@ -307,6 +321,11 @@ EXAMPLES = """ ansible.builtin.apt: deb: /tmp/mypackage.deb +- name: Install a verified .deb package + ansible.builtin.apt: + deb: /tmp/mypackage.deb + checksum: 'sha256:6cfd68f9160b620f26d34a82dc0f6bee28e97d6e78f61774bba63dbb143144ae' + - name: Install the build dependencies for package "foo" ansible.builtin.apt: pkg: foo @@ -316,6 +335,11 @@ EXAMPLES = """ ansible.builtin.apt: deb: https://example.com/python-ppq_0.1-1_all.deb +- name: Install a verified .deb package from the internet + ansible.builtin.apt: + deb: https://example.com/python-ppq_0.1-1_all.deb + checksum: 'sha256:6cfd68f9160b620f26d34a82dc0f6bee28e97d6e78f61774bba63dbb143144ae' + - name: Remove useless packages from the cache ansible.builtin.apt: autoclean: yes @@ -1230,6 +1254,7 @@ def main(): purge=dict(type='bool', default=False), package=dict(type='list', elements='str', aliases=['pkg', 'name']), deb=dict(type='path'), + checksum=dict(type='str', default=None), default_release=dict(type='str', aliases=['default-release']), install_recommends=dict(type='bool', aliases=['install-recommends']), force=dict(type='bool', default=False), @@ -1473,6 +1498,22 @@ def main(): module.fail_json(msg="deb only supports state=present") if '://' in p['deb']: p['deb'] = fetch_file(module, p['deb']) + if p['checksum']: + try: + algorithm, checksum = p['checksum'].split(':', 1) + except ValueError: + module.fail_json(msg="The checksum parameter has to be in format :", **retvals) + + actual_checksum = module.digest_from_file(p['deb'], algorithm) + + if checksum != actual_checksum: + d = { + 'algorithm': algorithm, + 'expected': checksum, + 'actual': actual_checksum, + } + module.fail_json(msg="The %(algorithm)s checksum did not match: expected %(expected)s, got %(actual)s" % d, **retvals) + install_deb(module, p['deb'], cache, install_recommends=install_recommends, allow_unauthenticated=allow_unauthenticated, diff --git a/test/integration/targets/apt/tasks/apt_deb_depend.yml b/test/integration/targets/apt/tasks/apt_deb_depend.yml index 0cb26b6e6d0..93845626306 100644 --- a/test/integration/targets/apt/tasks/apt_deb_depend.yml +++ b/test/integration/targets/apt/tasks/apt_deb_depend.yml @@ -9,6 +9,7 @@ - name: Install packageone from deb URL apt: deb: https://ci-files.testing.ansible.com/test/integration/targets/apt/packageone_1.0_all.deb + checksum: 'sha256:703061ea00465f41b89ed47701b79a4ee7f32cdc87e17491df4186476f4c1588' register: packageone_installed - name: Check if packagetwo is installed as part of packageone installation diff --git a/test/integration/targets/apt/tasks/url-with-deps.yml b/test/integration/targets/apt/tasks/url-with-deps.yml index 7e628c95b75..be0ed64b0b8 100644 --- a/test/integration/targets/apt/tasks/url-with-deps.yml +++ b/test/integration/targets/apt/tasks/url-with-deps.yml @@ -17,6 +17,7 @@ - name: Install deb file with dependencies from URL (check_mode) apt: deb: https://ci-files.testing.ansible.com/test/integration/targets/apt/echo-hello_1.0_all.deb + checksum: 'sha256:60c5d4dac1c83ea4d42d30c9e16dcec215f537e5053415e0d7bdb06957d32ae8' check_mode: true register: apt_url_deps_check_mode