From 9f67cbbe36de0c09477a600a2f513e53d81f1274 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Wed, 22 Mar 2023 13:15:32 +0100 Subject: [PATCH] rhsm modules: cleanly fail when not run as root (#6211) subscription-manager on RHEL installs a symlink in /usr/bin to console-helper (part of usermode), which triggers an interactive prompt for root credentials when run as user. It seems that console-helper does not handle well non-interactive contexts (e.g. without a TTY for input), and thus it will hang waiting for input when run as user in an Ansible task. Since subscription-manager requires root already anyway (and it will fail when explicitly run as user), then apply the same logic locally on all the modules that interact with it: redhat_subscription, rhsm_release, and rhsm_repository. --- changelogs/fragments/6211-rhsm-require-root.yml | 6 ++++++ plugins/modules/redhat_subscription.py | 9 ++++++++- plugins/modules/rhsm_release.py | 8 ++++++++ plugins/modules/rhsm_repository.py | 12 +++++++++--- .../unit/plugins/modules/test_redhat_subscription.py | 2 ++ tests/unit/plugins/modules/test_rhsm_release.py | 7 +++++++ 6 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 changelogs/fragments/6211-rhsm-require-root.yml diff --git a/changelogs/fragments/6211-rhsm-require-root.yml b/changelogs/fragments/6211-rhsm-require-root.yml new file mode 100644 index 0000000000..4172484513 --- /dev/null +++ b/changelogs/fragments/6211-rhsm-require-root.yml @@ -0,0 +1,6 @@ +bugfixes: + - redhat_subscription, rhsm_release, rhsm_repository - cleanly fail when not running as root, + rather than hanging on an interactive ``console-helper`` prompt; they all interact with + ``subscription-manager``, which already requires to be run as root + (https://github.com/ansible-collections/community.general/issues/734, + https://github.com/ansible-collections/community.general/pull/6211). diff --git a/plugins/modules/redhat_subscription.py b/plugins/modules/redhat_subscription.py index 2995e5a03e..d5f343f23b 100644 --- a/plugins/modules/redhat_subscription.py +++ b/plugins/modules/redhat_subscription.py @@ -24,6 +24,8 @@ notes: I(server_proxy_hostname), I(server_proxy_port), I(server_proxy_user) and I(server_proxy_password) are no longer taken from the C(/etc/rhsm/rhsm.conf) config file and default to None. + - It is possible to interact with C(subscription-manager) only as root, + so root permissions are required to successfully run this module. requirements: - subscription-manager - Optionally the C(dbus) Python library; this is usually included in the OS @@ -291,7 +293,7 @@ subscribed_pool_ids: ''' from os.path import isfile -from os import unlink +from os import getuid, unlink import re import shutil import tempfile @@ -1074,6 +1076,11 @@ def main(): required_if=[['state', 'present', ['username', 'activationkey', 'token'], True]], ) + if getuid() != 0: + module.fail_json( + msg="Interacting with subscription-manager requires root permissions ('become: true')" + ) + rhsm.module = module state = module.params['state'] username = module.params['username'] diff --git a/plugins/modules/rhsm_release.py b/plugins/modules/rhsm_release.py index 8ad763a778..037c3fbec8 100644 --- a/plugins/modules/rhsm_release.py +++ b/plugins/modules/rhsm_release.py @@ -18,6 +18,8 @@ notes: - This module will fail on an unregistered system. Use the C(redhat_subscription) module to register a system prior to setting the RHSM release. + - It is possible to interact with C(subscription-manager) only as root, + so root permissions are required to successfully run this module. requirements: - Red Hat Enterprise Linux 6+ with subscription-manager installed extends_documentation_fragment: @@ -63,6 +65,7 @@ current_release: from ansible.module_utils.basic import AnsibleModule +import os import re # Matches release-like values such as 7.2, 5.10, 6Server, 8 @@ -109,6 +112,11 @@ def main(): supports_check_mode=True ) + if os.getuid() != 0: + module.fail_json( + msg="Interacting with subscription-manager requires root permissions ('become: true')" + ) + target_release = module.params['release'] # sanity check: the target release at least looks like a valid release diff --git a/plugins/modules/rhsm_repository.py b/plugins/modules/rhsm_repository.py index 0517582403..eea6e38579 100644 --- a/plugins/modules/rhsm_repository.py +++ b/plugins/modules/rhsm_repository.py @@ -19,6 +19,8 @@ author: Giovanni Sciortino (@giovannisciortino) notes: - In order to manage RHSM repositories the system must be already registered to RHSM manually or using the Ansible C(redhat_subscription) module. + - It is possible to interact with C(subscription-manager) only as root, + so root permissions are required to successfully run this module. requirements: - subscription-manager @@ -100,9 +102,7 @@ def run_subscription_manager(module, arguments): lang_env = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C') rc, out, err = module.run_command("%s %s" % (rhsm_bin, " ".join(arguments)), environ_update=lang_env) - if rc == 1 and (err == 'The password you typed is invalid.\nPlease try again.\n' or os.getuid() != 0): - module.fail_json(msg='The executable file subscription-manager must be run using root privileges') - elif rc == 0 and out == 'This system has no repositories available through subscriptions.\n': + if rc == 0 and out == 'This system has no repositories available through subscriptions.\n': module.fail_json(msg='This system has no repositories available through subscriptions') elif rc == 1: module.fail_json(msg='subscription-manager failed with the following error: %s' % err) @@ -243,6 +243,12 @@ def main(): ), supports_check_mode=True, ) + + if os.getuid() != 0: + module.fail_json( + msg="Interacting with subscription-manager requires root permissions ('become: true')" + ) + name = module.params['name'] state = module.params['state'] purge = module.params['purge'] diff --git a/tests/unit/plugins/modules/test_redhat_subscription.py b/tests/unit/plugins/modules/test_redhat_subscription.py index 58a9a8bea5..bc1bfffe66 100644 --- a/tests/unit/plugins/modules/test_redhat_subscription.py +++ b/tests/unit/plugins/modules/test_redhat_subscription.py @@ -29,6 +29,8 @@ def patch_redhat_subscription(mocker): return_value='/testbin/subscription-manager') mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.Rhsm._can_connect_to_dbus', return_value=False) + mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.getuid', + return_value=0) @pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) diff --git a/tests/unit/plugins/modules/test_rhsm_release.py b/tests/unit/plugins/modules/test_rhsm_release.py index 9d371cec03..c5696962b5 100644 --- a/tests/unit/plugins/modules/test_rhsm_release.py +++ b/tests/unit/plugins/modules/test_rhsm_release.py @@ -30,9 +30,16 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase): self.get_bin_path = self.mock_get_bin_path.start() self.get_bin_path.return_value = '/testbin/subscription-manager' + # subscription-manager needs to be run as root + self.mock_os_getuid = patch('ansible_collections.community.general.plugins.modules.rhsm_release.' + 'os.getuid') + self.os_getuid = self.mock_os_getuid.start() + self.os_getuid.return_value = 0 + def tearDown(self): self.mock_run_command.stop() self.mock_get_bin_path.stop() + self.mock_os_getuid.stop() super(RhsmRepositoryReleaseModuleTestCase, self).tearDown() def module_main(self, exit_exc):