3

I have a script that use logger to simulate some "fake" logins:

#!/bin/bash

{%- set maxretry = salt['pillar.get']('fail2ban:maxretry', 3) %}

tag="$1"
message="$2"

logger -p auth.info "The {{ maxretry + 1 }} \"$tag\" below lines are generated by logger to test Fail2ban"

for i in $(seq {{ maxretry + 1 }}); do
    logger -p auth.warning -t "$tag" "$message"
done

It is called in a macro:

fake_{{ formula }}_login:
  cmd:
    - script
    - source: salt://fail2ban/fake_login.jinja2
    - template: jinja
    - args: "{{ tag|default(formula) }} '{{ message }}'"
    - require:
      - sls: bash
      - sls: fail2ban

The thing is {{ message }} can contain single/double quotes, space, square bracket,... According to the cmd.script doc, to pass a string containing a space, we will have to double-quote it. But if I have something like this:

{{ fail2ban_regex_test('mysql', tag='mysqld', message="150114  3:40:50 [Warning] Access denied for user 'root'@'5.6.7.8' (using password: YES)") }}

it will be logged to the syslog without the single quote around the user/host, just:

mysqld: 150114  3:40:50 [Warning] Access denied for user root@5.6.7.8 (using password: YES)

that make fail2ban failed to recognized as it does not match the filter regex.

I can change the single quote to double quote and use backslash to escape:

fake_{{ formula }}_login:
  cmd:
    - script
    - source: salt://fail2ban/fake_login.jinja2
    - template:
jinja
    - args: "{{ tag|default(formula) }} \"{{ message|safe }}\""

    - require:
      - sls: bash
      - sls: fail2ban

it handle the above case when message just contains the single quotes.

But if it contains the double quotes:

{{ fail2ban_regex_test('postfix', tag='postfix/smtpd[20228]', message="NOQUEUE: reject: RCPT from sender.com["5.6.7.8"]: 554 5.7.1 <us...@example.com>: Recipient address rejected: Access denied; from=<us...@sender.com> to=<us...@example.com> proto=ESMTP helo=<mg01d1.sender.com>") }}

I got this error:

local:
    Data failed to compile:
----------
    Rendering SLS "base:postfix.test" failed: Jinja syntax error: expected token ',', got 'float'; line 29


---
[...]
- sls: openldap
- sls: openldap.diamond
- sls: openldap.nrpe
{%- endcall %}


{{ fail2ban_regex_test('postfix', tag='postfix/smtpd[20228]', message="NOQUEUE: reject: RCPT from sender.com["5.6.7.8"]: 554 5.7.1 <us...@example.com>: Recipie
nt address rejected: Access denied; from=<us...@sender.com> to=<us...@example.com> proto=ESMTP helo=<mg01d1.sender.com>") }}    <======================

If I tried to escape the double quote with a backslash:

... message="NOQUEUE: reject: RCPT from sender.com[\"5.6.7.8\"] ...

then I got another error:

local:
    Data failed to compile:
----------
    Rendering SLS postfix.test failed, render error: while parsing a block mapping
  in "<unicode string>", line 84, column 7:
        - args: "postfix/smtpd[20228] \"NO ...
          ^
expected <block end>, but found '<scalar>'
  in "<unicode string>", line 84, column 76:
     ... : reject: RCPT from sender.com["5.6.7.8"]: 554 5.7.1 <user@examp ...

How to handle both cases?

quanta
  • 3,960
  • 4
  • 40
  • 75
  • 1
    How about passing that string over `stdin` & use `read` to read it into a variable. Then use that variable within double quotes... – anishsane Mar 19 '15 at 04:31
  • All are automated. There is no `stdin` involved here. – quanta Mar 19 '15 at 04:46
  • My understanding is as this: You have the string data in your framework, using which, you want to trigger another script. You are using some string concatenation logic, to get this done. Instead of that, change that 'another script' to read the data from stdin. Then, instead of using string concatenation, pipe the string to the script. – anishsane Mar 19 '15 at 09:29
  • Did you try prefiltering with to convert the string to exclusively single or double quotes? That may do the trick for you, otherwise you can try to escape every character. You may run into the backslash at the end of the line problem - if you do you will have to count the backslashes and escape the odd number of ending backslashes to make it even. Other than that I think it should work fine - in the worst case if you really need to be robust you can rip out / convert the troublesome characters if you are allowed to. –  Mar 30 '15 at 00:28
  • @A.Danischewski: What do you mean by "prefilter"? Please give an example. – quanta Mar 30 '15 at 01:18
  • You can try something like sed: E.g. `message=$(sed 's/./\\&/g' <<< "${message}")` –  Mar 30 '15 at 02:31

2 Answers2

4

saltstack extended jinja builtin filters with some custom filters:

  • yaml_dquote: Serializes a string into a properly-escaped YAML double-quoted string.
  • yaml_encode: Serializes a single object into a YAML scalar with any necessary handling for escaping special characters.

Something like:

{%- set foo = 7.7 %}
{%- set bar = none %}
{%- set baz = true %}
{%- set zap = 'The word of the day is "salty".' %}
{%- set zip = '"The quick brown fox . . ."' %}

foo: {{ foo|yaml_encode }}
bar: {{ bar|yaml_encode }}
baz: {{ baz|yaml_encode }}
zap: {{ zap|yaml_encode }}
zip: {{ zip|yaml_dquote }}

give you:

foo: 7.7
bar: null
baz: true
zap: "The word of the day is \"salty\"."
zip: "\"The quick brown fox . . .\""

With arbitrary string, even {{ var|yaml_encode|yaml_decode }} may not work. It's better if you can encode the string then decode it in script.

cuonglm
  • 2,766
  • 1
  • 22
  • 33
  • `yaml_dquote` just convert to double quotes and escape the double quotes inside the string by a backslash. That is equal to what I tried, and actually, I got the same error. – quanta Mar 25 '15 at 14:53
0

You have message variable, which can contain special characters.

- args: "{{ tag|default(formula) }} '{{ message }}'"

My understanding is that your bash script takes 2 arguments:

#!/bin/bash
tag=$1
message=$2
echo "$message" #some use of message

You are calling your script with cmd.script.

Instead of this, you can perhaps change your scripts as below:

- args: "{{ tag|default(formula) }}"
- stdin: "{{ message }}\n"

& bash script to:

#!/bin/bash
tag=$1
read message
echo "$message" #some use of message
anishsane
  • 20,270
  • 5
  • 40
  • 73