New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ansible-core 2.16.1/2.16.2 break action plugin parameters due to changed casting #82414
Comments
Files identified in the description: None If these files are incorrect, please update the |
@ansibot component =lib/ansible/utils/unsafe_proxy.py |
Files identified in the description: If these files are incorrect, please update the |
I do understand what's happening, but I don't understand how you have gotten to a point where you are actually experiencing this issue. Are you directly consuming other Python APIs within ansible-core? Generally speaking, by the time that a module receives the arguments, you wouldn't be dealing with unsafe, as there is no concept of unsafe within a module. Could you expand the example a little more to give more context? You are correct that the decision to make |
Hi @sivel Sure. In this case there is a collection which consists of multiple hundred custom ansible modules which are specific to custom services we have built. We execute these modules (as usual) through ansible-playbook and are NOT using, e.g. direct python APIs within core. We first detected this yesterday in an action module and were hoping for 2.16.2 to fix that but that didn't help. I totally agree with you on the "there is no concept of unsafe within a module" as we never dealt with that so far. Nevertheless, even in lower Ansible Versions (e.g. 2.16.0) the type of a passed variable to an Action module was of type AnsibleUnsafeText but since the AnsibleUnsafeText was inherited from text_type (which effectively was str) we didn't experience trouble. Indeed my first thought was "we should just be getting the type we describe in the arguments, e.g. str" but indeed we get that AnsibleUnsafeText passed over. Since the security fix implementation the only occurrence of this issue is in Pathlib based modules where Pathlib tries a call to sys.intern and that fails since it cannot cast the variable properly. Here is the demo repo (collection): https://github.com/SirUli/ansibleunsafetext To show the trouble: While building my demo repository i was able to further isolate the problem: We use heavily imports of variable files and sometimes lookups in these files to serve variables from the environment. When the variable in the imported file is non-jinja, it works. See here when you uncomment line 6 instead of line 4. Does that help? |
Yes, it does. The clarity on using an action plugins, and the example make it more clear. I don't have any immediate info to give you about when we may have an answer for this use case. Our next release is scheduled for 29 January 2024, so should a release be a required, you may not see movement until closer to that date. As an short term solution, using Just as a heads up, As for another note, anything that was originally unsafe, if it is somehow converted to safe, should probably be converted back to unsafe before being returned to the user. |
This looks a little funny, but seems to work, and although seeming redundant, keeps within the same realm of dealing with paths:
I'll want to talk with some other devs about this, and what recommendations we might be able to officially make going forward. That will unfortunately have to wait until January. But I've given some work arounds that should suffice for now. |
in most cases, for arguments they should be using teh |
FWIW, this is a minimum reproducible example (without pathlib): import sys
from ansible.utils.unsafe_proxy import AnsibleUnsafeText
x = AnsibleUnsafeText("asdf")
sys.intern(str(x)) # fails on 2.16.2, passes on 2.16.0 Python 3.12.1 |
Python isn't really designed to prevent you from calling such methods. There are countless ways to workaround this patch and keep your code working. Tbh, I also don't understand why it's such a big deal that you could intentionally loose the Unsafe. It would be different if it was unintentional, but having a public Also all of these exist, too:
If it's a hotfix on a friday evening I'd have probably settled with a search and replace of
Don't you all like reflection, introspection, and polymorphic code? 🙃 But before you use any of these, as always 'just because we could, doesn't mean we should' 😉 |
Generally agree - to fix the problem I can see countless ways. I just had the issue in various places across multiple action modules and I initially had a hard time figuring out where this exactly comes from as I didn't expect a patch release to change such major areas. The fix is quite easy for the Friday afternoon... Roll back to ansible-core 2.16.0 and care about it on Monday ;) Cheers! |
Thanks for mentioning this here, I've been in the middle of working on trying to fix problems in my collection caused by the CVE patch, and was looking at that method as an option. I was actually about to report a bug with it (that it seems to be completely broken in As for why I was stripping unsafe in the first place, it's due to the change in
Looking now, it seems that they did in fact relax the requirements: So I'll look into what versions of |
I am sorry, but I can't follow you. We have a simple ActionPlugin that just consumes the task args, which happens to be a list of from ansible.plugins.action import ActionBase
from ansible.utils.unsafe_proxy import AnsibleUnsafeText
class ActionModule(ActionBase):
def run(self, tmp=None, task_vars=None):
super().run(tmp, task_vars)
for c in self._task.args["certs"]:
assert type(c) == AnsibleUnsafeText
return dict() The action plugin is part of a role: - name: 'Generate cacert.pem'
cpng_cacerts:
certs: '{{ query("ansible.builtin.fileglob", role_path ~ "/files/certs/*.pem") }}' Are we using the Ansible API incorrectly? Or is it actually a bug in ansible that the |
For me now, the solution ended up being In []: x, type(x)
Out[]: ('some text', ansible.utils.unsafe_proxy.AnsibleUnsafeText)
In []: x = "".join(x)
In []: x, type(x)
Out[]: ('some text', str) hope there will be a better way to convert the string. Is this only a problem for pathlib? |
… with Python `pathlib` (ansible#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes ansible#82414. (cherry picked from commit c6a652c) Co-authored-by: Matt Martz <matt@sivel.net>
… with Python `pathlib` (ansible#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes ansible#82414. (cherry picked from commit c6a652c) Co-authored-by: Matt Martz <matt@sivel.net>
…ble#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes ansible#82414. (cherry picked from commit c6a652c) Co-authored-by: Matt Martz <matt@sivel.net>
…ble#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes ansible#82414. (cherry picked from commit c6a652c) Co-authored-by: Matt Martz <matt@sivel.net>
This has been resolved in #82510 and will be included in 2.16.3, 2.15.9, and 2.14.14 scheduled for rc1 release on Jan 22, and GA on Jan 29. If you have further questions please stop by IRC or the mailing list:
|
v2.16.3 ======= Release Summary --------------- | Release Date: 2024-01-29 | `Porting Guide <https://docs.ansible.com/ansible-core/2.16/porting_guides/porting_guide_core_2.16.html>`__ Security Fixes -------------- - ANSIBLE_NO_LOG - Address issue where ANSIBLE_NO_LOG was ignored (CVE-2024-0690) Bugfixes -------- - Run all handlers with the same ``listen`` topic, even when notified from another handler (ansible/ansible#82363). - ``ansible-galaxy role import`` - fix using the ``role_name`` in a standalone role's ``galaxy_info`` metadata by disabling automatic removal of the ``ansible-role-`` prefix. This matches the behavior of the Galaxy UI which also no longer implicitly removes the ``ansible-role-`` prefix. Use the ``--role-name`` option or add a ``role_name`` to the ``galaxy_info`` dictionary in the role's ``meta/main.yml`` to use an alternate role name. - ``ansible-test sanity --test runtime-metadata`` - add ``action_plugin`` as a valid field for modules in the schema (ansible/ansible#82562). - ansible-config init will now dedupe ini entries from plugins. - ansible-galaxy role import - exit with 1 when the import fails (ansible/ansible#82175). - ansible-galaxy role install - normalize tarfile paths and symlinks using ``ansible.utils.path.unfrackpath`` and consider them valid as long as the realpath is in the tarfile's role directory (ansible/ansible#81965). - delegate_to when set to an empty or undefined variable will now give a proper error. - dwim functions for lookups should be better at detectging role context even in abscense of tasks/main. - roles, code cleanup and performance optimization of dependencies, now cached, and ``public`` setting is now determined once, at role instantiation. - roles, the ``static`` property is now correctly set, this will fix issues with ``public`` and ``DEFAULT_PRIVATE_ROLE_VARS`` controls on exporting vars. - unsafe data - Enable directly using ``AnsibleUnsafeText`` with Python ``pathlib`` (ansible/ansible#82414)
…pathlib` (ansible#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes ansible#82414 (cherry picked from commit c6a652c)
* Ensure that unsafe is more difficult to lose [stable-2.16] (#82293) * Ensure that unsafe is more difficult to lose * Add Task.untemplated_args, and switch assert over to use it * Don't use re in first_found, switch to using native string methods * If nested templating results in unsafe, just error, don't continue * ci_complete (cherry picked from commit 270b39f) * Fix various issues in unsafe_proxy (#82326) - Use str/bytes directly instead of text_type/binary_type - Fix AnsibleUnsafeBytes.__str__ implementation - Fix AnsibleUnsafeBytes.__format__ return type - Remove invalid methods from AnsibleUnsafeBytes (casefold, format, format_map) - Use `chars` instead of `bytes` to match stdlib naming - Remove commented out code (cherry picked from commit 59aa014) * Additional Unsafe fixes (#82376) * Allow older pickle protocols to pickle unsafe classes. Fixes #82356 * Address issues when iterating or getting single index from AnsibleUnsafeBytes. Fixes #82375 * clog frag (cherry picked from commit afe3fc1) * [stable-2.16] Enable directly using `AnsibleUnsafeText` with Python `pathlib` (#82510) * Enable directly using `AnsibleUnsafeText` with Python `pathlib`. Fixes #82414 (cherry picked from commit c6a652c) * Prevent failures due to unsafe plugin name (#82759) (cherry picked from commit 56f3112) * Address issues from merge conflicts --------- Co-authored-by: Matt Clay <matt@mystile.com> Co-authored-by: Martin Krizek <martin.krizek@gmail.com>
Summary
When upgrading to ansible-core 2.16.1 or 2.16.2, the behaviour of module parameters has changed thanks to various changes resulting from CVE-2023-5764. For us the biggest change in our custom modules was that a AnsibleUnsafeText cast to "str" leads to an AnsibleUnsafeText object which to me is a breaking change and could have been annnounced more clearly.
One of the visible changes is that when you are using Pathlib.Path in a custom module, the library dumps with an
TypeError: can't intern AnsibleUnsafeText
as the Pathlib cannot cast the string properly to str.Today our workaround is:
which i believe shouldn't be the workaround. The alternative is serializing to json and deserializing again but agin - i believe this shouldn't work this way.
The issue came up after the changes from the CVE remediation, e.g. this one where str was modified to return the AnsibleUnsafeText instead of str.
I'm not too deep in ansible-core development but i think there are different ways of solving that:
Building a sample repository would take quite an effort - hope you can see the challenge already otherwise let me know what to provide.
Thanks in advance!
Best Regards,
Uli
Issue Type
Bug Report
Component Name
core
Ansible Version
Configuration
Doesn't matter - irrespective of the environment.
OS / Environment
RHEL8 - but doesn't matter.
Steps to Reproduce
In a custom module call to retrieve the path variable
Expected Results
Pathlib's Path is created properly
Actual Results
TypeError: can't intern AnsibleUnsafeText
Code of Conduct
The text was updated successfully, but these errors were encountered: