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
Recent bugfix prevents me from using Jinja2 if/else/endif syntax in a task #8233
Comments
I also happen to use Jinja conditionals in tasks, this seems a big regression to me too. |
I use this as well. It would be a shame if it went away. It's really handy for certain use cases, for example: |
Ansible from version 1.6.7 fails if there is a duplicate initialization of a variable. Refer: ansible/ansible#8233 Change-Id: Ia25c59c6230a3169286798c875979a836ea0a327
I would just rewrite the example:
|
I have done some quick analysis on it. The idea of the recent security fix is this: count parameters before and after rendering the template and if the counts are different, fail. This is done to prevent from a var being crafted so that it injects a param when it is rendered by jinja. The failure message comes from an earlier check for duplicate params while counting the parameters. To fix this situation, the check for duplicate params should be done only on the rendered version. Also, not a count of, but a specific list of the exact parameters found for each version should be calculated. Then, check that the list of parameters found on the rendered version is a subset of the list of parameters found in the original version, or else fail. Theoretically, this is even more secure than just counting, because the counts could be the same but the actual parameters different. I could provide a fix for this on my own, but since this is security-related, I think my suggestion should be first reviewed by more people. |
* Strip lookup calls out of inventory variables and clean unsafe data returned from lookup plugins (CVE-2014-4966) * Make sure vars don't insert extra parameters into module args and prevent duplicate params from superseding previous params (CVE-2014-4967)
Ansible has never endorsed Jinja2 inside of playbooks much more than to assign variables. if you remember, the first support was $foo and we didn't have this at all. Indeed, we cannot use untrusted variables to ADD parameters, not just provide duplicates. We'll look at this a bit deeper, but there is a reason Ansible has a "when" operator, it's to make cleaner playbooks, and it should be used. In this case the value of the variable should be defined like this: install_path: "{% if x %}foo{% else %}bar{% endif %}" In which case your playbook doesn't have the hideous inline YAML and you can just do path={{ install_path }} and things like that. That being said, my finding of your syntax overcomplicated and non-ansible like (there's a reason the docs don't show it this way) doesn't mean we won't evaluate this a bit closer. |
There's a second problem that @Ernest0x's solution doesn't 100% fix. Say you're using the command module in a playbook: vars:
real_world_command: "virt-install -n {{ inventory_hostname }} -r {{ mem_size }}
--disk bus=virtio,path={{ volgroup }}/{{ inventory_hostname }}
--vcpus={{ num_cpus }} -l {{ ks_repo }} -x
''ksdevice=eth0 ks={{ ks_url }} console=tty0 console=ttyS0
hostname={{ inventory_hostname }} nameserver={{ dns }}
ip={{ eth0_ip }}::{{ gw }}:{{ nm }}:{{ inventory_hostname }}:eth0:none''
--network=bridge=br0,model=virtio
--autostart --noautoconsole"
even_worse_contrived_cmd: echo chdir=test is going to cause problems >> ansible.test
tasks:
- shell: "{{ real_world_command }}"
- shell: "{{ even_worse_contrived_command }}" The above real_world_command will continue to fail even if the check is done post render as console= is present twice in the command. It will probably be okay if the runner is taught to only check for parameters that the module actually uses as "console" isn't a parameter to the command/shell module. The contrived command is more problematic as it contains a substring that would be recognized as a valid parameter (chdir is a valid parameter to the command and shell module). A brief look at the code last night makes me think that this will be much harder to fix. In the 0.6.6 code, the "chdir=test" would be treated as a parameter to command. In the 0.6.7 code it will raise an error. What we'd really need is for ansible to recognize that the "chdir=test" is part of the "free_form" parameter to command/shell and not a new parameter. That change has to be added in both the checking code and the module code that parses the parameters. |
@mpdehaan I'm using {% if %} syntax in tasks to basically move logic inside public playbook and allow configuration of various parameters using inventory variables, for example this inline YAML syntax allows me to define simple list of users, like:
in Because of that my playbook can be completely public and used in different environments, and inventory can stay private and contain actual configuration. I'm afraid that without {% if %} functionality this whole idea of writing playbook like that becomes very limited, or even impossible to do in a way that I would want. |
Yes, this is safer.
What error does it raise? |
@Ernest0x: fatal: [127.0.0.1] => A variable inserted a new parameter into the module args. Be sure to quote variables if they contain equal signs (for example: "{{var}}"). Which is correct to some extent. The code in 0.6.6 is going to treat these as equivalent: vars:
command: "echo 'chdir=test is problematic'"
tasks:
- shell: "{{ command }}"
- shell: "echo 'is problematic'"
args:
- chdir: test which is probably not what was intended. So the error in 0.6.7 is better but still leaves a cornercase unresolved. |
@abadger: I see. I wonder if a filter named 'ansible_safe' or something like this would make sense for these cases. For example: {{ expression | ansible_safe }} would exclude that jinja expression from the 'inserted a new param' check. |
Could you confirm that this fix essentially disables something like where |
As I said before: Until that commit I used to have: tmpfs_tar_options: "--preserve-permissions --numeric-owner --auto-compress --xattrs --xattrs-include='*' --selinux --acls" - name: create archive
shell: >-
tar {{ tmpfs_tar_options }}
{% for item in tmpfs_tar_exclude %} --exclude '{{ item }}'{% endfor %}
{% for item in tmpfs_tar_exclude_extra|default([]) %} --exclude '{{ item }}'{% endfor %}
--exclude '{{tmpfs_target[1:]}}'
--directory /
--create --file /{{tmpfs_target}}/{{ansible_hostname}}_rootfs{{ tmpfs_tar_ext }} . The error message is not very clear:
Maybe I do not declare correctly my variable. Anyway how should I declare a variable with an equal sign in a string? I guess many people are OK with changes as long as it does not involve rewriting all code. On the other hand I was wondering what is the difference between the two syntaxes: - name: Create groups
group:
name: '{{ item.name }}'
gid: '{{ item.gid }}'
state: '{{ item.state|default("present")}}'
system: '{{ item.state|default("no")}}'
with_items: credentials_groups
when: credentials_groups is defined and - name: Create groups
group:
name='{{ item.name }}'
gid='{{ item.gid }}'
state='{{ item.state|default("present")}}'
system='{{ item.state|default("no")}}'
with_items: credentials_groups
when: credentials_groups is defined |
For your specific issue, can you change your variable to this?
That's only a workaround but for a lot of command line tools changing the equals into a space should work. |
This change is due to the security fix in Ansible v1.6.7 and problems with old playbook format used in ginas. More information: ansible/ansible#8233 This is a temporary change to allow currently working code in a pull request to be merged cleanly into main repository.
I'm also seeing this problem with the following task:
I get this error:
|
Note that mine is not using conditionals, just basic variable expansion. |
@abadger yes that workaround for that particular use case |
This problem is actually much more basic (and insidious). We have something like the following in a vars file:
Note that the ssh pubkey is simply a base64 encoded string and therefore may contain an
|
@SegFaultAX do you have the same error with single quotes? |
I'm having "kind of" the same issue on 1.6.7 when trying to pipe some variable into a local shell script: - name: build env directory
local_action: >
shell echo "{{ env_dict | to_nice_yaml }}" | utils/buildenv Since this Error:
This works without error on 1.6.6. Update: looks like there are no j2 snippets in the |
I'm seeing this on ~1.6.8.
|
I'am getting problems with strings.
Ansible doesn't like |
Ansible 1.6.8:
|
Spul, I posted more details about this last one in #8254 |
@renard |
All, since this is a bit of a miscellaneous ticket now, just wanted to update you all and call your attention to some new integration tests. This contains a list of MOST things above, and once clear, we'll know they are all resolved. As such, if there are parsing scenarios you think are uncovered, adding them to the test here can help solve them. Some things like Jinja2 param duplications do need to be handled carefully and will be a little different, but I still like @Ernest0x's plan above. In any event, this is a codification of the above issues and proof they remain operational. Again, stay tuned, as we're working on this one. Thanks! |
All, you can see fixes applied to the devel branch now. All of the integration tests in "test_good_parsing" and "test_bad_parsing" pass now. There are some duplicate args things like what are mentioned above that are legit error conditions that we still need to filter, though this resolves nearly all false positives. Please take a look if you would like to try the development branch, and see how this works for you. Note if I've replied and said this is a condition that needs to be filtered out, that remains true in most cases. |
@mpdehaan
Also I had an issue with
|
I can trigger this same error using what I would think is supported and encouraged syntax given this conversation.
Gives "A variable inserted a new parameter into the module args. Be sure to quote variables if they contain equal signs (for example: "{{var}}")." I tried quoting the whole line after "set_fact:" with the same results. |
Same problem started happening to me with a very straightforward task:
Throws error "A variable inserted a new parameter into the module args. Be sure to quote variables if they contain equal signs (for example: "{{var}}")." Had to downgrade to 1.6.6 until this gets fixed. |
@mpdehaan Ok fair enough or the dynamic script generation. Thanks |
@mpeters I can confirm your example works now in 1.6.9/devel: # ansible-playbook -vv 8233_mpeters.yml PLAY [localhost] ************************************************************** TASK: [shell id -u sg] ******************************************************** <127.0.0.1> REMOTE_MODULE command id -u sg #USE_SHELL failed: [127.0.0.1] => {"changed": true, "cmd": "id -u sg", "delta": "0:00:00.007091", "end": "2014-07-25 10:09:45.608184", "rc": 1, "start": "2014-07-25 10:09:45.601093"} stderr: id: sg: no such user ...ignoring TASK: [set_fact run_bootstrap="true"] ***************************************** ok: [127.0.0.1] => {"ansible_facts": {"run_bootstrap": "true"}} PLAY RECAP ******************************************************************** 127.0.0.1 : ok=2 changed=1 unreachable=0 failed=0 |
@nocive can you also test with 1.6.9/devel to see if your issue is resolved? |
@jsmirnov I can confirm that your example also works on 1.6.9/devel now, using the following example playbook: - hosts: localhost connection: local gather_facts: no tasks: - copy: "dest=/tmp/8233_jsmirnov.txt content='start ALL=(ALL) NOPASSWD: ALL\n'" - name: Sudoers | update sudoers file and validate lineinfile: "dest=/tmp/8233_jsmirnov.txt insertafter=EOF line='{{ item.name }} ALL=(ALL) NOPASSWD: ALL' regexp='{{ item.name }} ALL=\\(ALL\\) NOPASSWD: ALL' state=present validate='visudo -cf %s'" with_items: - { name: foo } - { name: bar } - { name: baz } And after the run, the /tmp file contains: $ cat /tmp/8233_jsmirnov.txt start ALL=(ALL) NOPASSWD: ALL foo ALL=(ALL) NOPASSWD: ALL bar ALL=(ALL) NOPASSWD: ALL baz ALL=(ALL) NOPASSWD: ALL |
@jimi-c yep, just give me a few minutes to set it up. |
@jimi-c I can confirm it works with 1.6.9 |
Excellent. Are there any other issues above that we've missed or not addressed? Definitely a little difficult to parse through all the comments to be sure. |
@jimi-c But on devel I am still experiencing problem which I mentioned at: #8233 (comment) I am still getting issue with:
part of
|
I'll run my use cases when I get into the office, this is looking much better though |
+1 our scripts work with the |
@jsmirnov thanks, I'll look into the lookup issue. |
@jsmirnov I've tried to reproduce this, however I haven't had any luck. Could you please open a new issue for this, with a full playbook that reproduces the issue? As for this issue, we're going to go ahead and close it out. If there are any further outstanding issues we've missed, please open new issues for them. Thanks! |
…aybook "Demo" (bug: ansible/ansible#8233) [skip CI]
…aybook "Demo" (bug: ansible/ansible#8233) [skip CI]
…#8233, playbooks work on versions < 1.6.6 and > 1.6.9
ansible 1.7.1 fatal: [hostname] => a duplicate parameter was found in the argument string (--exclude)
|
Issue Type:
Bug Report
Ansible Version:
v1.6.7+, devel
Environment:
Debian, Ubuntu
Summary:
Recent security fix prevents me from using Jinja2
{% if %}{% else %}{% endif %}
syntax in a task. This prevents me from creating more flexible roles that can be easily customized using inventory lists. Basically 80% of my playbook is based around this... :-(Steps To Reproduce:
Expected Results:
Two directories are created,
/tmp/my_directory/
and/tmp/other_directory/
.Actual Results:
The text was updated successfully, but these errors were encountered: