Passing variable contains single, double quotes, space, ... to cmd.script?

3,713 views
Skip to first unread message

Quan Tong Anh

unread,
Mar 18, 2015, 11:01:11 AM3/18/15
to salt-...@googlegroups.com
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 is different from the filter regex.

So, how to preserve all special characters in `message` variable?

Quan Tong Anh

unread,
Mar 18, 2015, 12:31:04 PM3/18/15
to salt-...@googlegroups.com
OK, 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 case when message just contains the single quote.

But then, 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>"
) }}    <======================

"expected token ',', got 'float';" --> If I tried to escape the double quote with a backslash, 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 ...
                                         ^

Bruno Clermont

unread,
Mar 20, 2015, 5:02:47 AM3/20/15
to salt-...@googlegroups.com
Instead of cmd.run and deal with troublesome command line arguments, just use a custom salt module:


# _modules/syslog.py

import syslog

def emit(facility, level, tag, message):
   
# switch to tag
    syslog
.closelog()
    syslog
.openlog(tag)
   
# log
    fac
= getattr(syslog, 'LOG_' +
facility.upper())
    lvl
= getattr(syslog, 'LOG_' + level.upper())
    syslog
.syslog(fac|lvl, message)
   
# force to revert to default
    syslog
.closelog()

and in your .sls:

fake_{{ formula }}_login:
  module:
    - run
    - name: syslog.emit
    - tag: {{ tag|default(formula) }} 
    - message: {{ message }}
    - facility: auth
    - level: info

Quan Tong Anh

unread,
Mar 20, 2015, 1:32:01 PM3/20/15
to salt-...@googlegroups.com
Bruno, thanks.

Unfortunately, it didn't work as expected.
You have just converted bash to python, but the main problem is passing a variable with special characters to that script.

With your code, and a message like this:


{{ 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>") }}

the error is:

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>: Recipi$

nt address rejected: Access denied; from=<us...@sender.com> to=<us...@example.com> proto=ESMTP helo=<mg01d1.sender.com>"
) }}    <======================

Seth House

unread,
Apr 3, 2015, 7:02:39 PM4/3/15
to salt users list
YAML's multi-line strings are often useful for this pattern That
bypasses having to quote/escape things in YAML entirely and just
passes a raw string through in the argument. Try something like this
(just watch your indentation):

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

Reply all
Reply to author
Forward
0 new messages