Fail2ban: Custom action failed | Bad substitution

Created on 23 Nov 2020  Â·  6Comments  Â·  Source: fail2ban/fail2ban

Environment:

General Information:
Distribution: Ubuntu 18.04
Fail2Ban v0.10.2

  • Fail2Ban version (including any possible distribution suffixes):
  • OS, including release name/version:
  • [x] Fail2Ban installed via OS/distribution mechanisms
  • [ ] You have not applied any additional foreign patches to the codebase
  • [ ] Some customizations were done to the configuration (provide details below is so)

The issue:

Hello,

I'm using now a WAF to secure my owncloud instance.
The Sophos UTM needs 2 parts; first creating the host object (attacker) and second update the reverse proxy object. The RESTful API works from my own script, but not triggered by my custom action file. Step one creating the object works well; variable <ip> is only needed here.

But the second part crashes, because of my variable definition block:

2020-11-23 16:15:12,158 fail2ban.actions        [28653]: NOTICE  [owncloud] Ban 80.187.101.140
2020-11-23 16:15:12,175 fail2ban.utils          [28653]: Level 39 7f612c005a90 -- exec: UTM2B=80.187.101.140;
UTM=${UTM2B//./};
REF="REF_NetHos";
REF_ID="${UTM:0:10}";
DN="$REF$REF_ID";
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' \
--header 'Authorization: Basic access_token' -d '{"address":"80.187.101.140","address6":"","comment":"","duids":[],"hostnames":[],"interface":"","macs":[],"name":"80.187.101.140","resolved":false,"resolved6":false,"reverse_dns":false}' \
'https://host.domain/api/objects/network/host/' > /dev/null
curl -X PATCH --header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Restd-Err-Ack: all' \
--header 'X-Restd-Lock-Override: yes' \
--header 'Authorization: Basic access_token' -d \
'{"access_control":"1","allowed_networks":["REF_NetworkAny"],"auth_profile":"","backend":["REF_RevBacWEBHost"],"be_path":"","comment":"","denied_networks":["'"$DN"'"],"hot_standby":false,"name":"ProxyN","path":"/subtree","status":true,"stickysession_id":"ROUTEID","stickysession_status":false,"websocket_passthrough":true}' \
'https://my.fw/api/objects/reverse_proxy/location/REF_RevLocProxyN'
2020-11-23 16:15:12,175 fail2ban.utils          [28653]: ERROR   7f612c005a90 -- stderr: '/bin/sh: 2: Bad substitution'
2020-11-23 16:15:12,175 fail2ban.utils          [28653]: ERROR   7f612c005a90 -- returned 2

Steps to reproduce

actionban = UTM2B=<ip>;
            UTM=${UTM2B//./};
            REF="REF_NetHos";
            REF_ID="${UTM:0:10}";
            DN="$REF$REF_ID";
            curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' \
            --header 'Authorization: Basic access_token' -d '{"address":"<ip>","address6":"","comment":"","duids":[],"hostnames":[],"interface":"","macs":[],"name":"<ip>","resolved":false,"resolved6":false,"reverse_dns":false}' \
            'https://my.fw/api/objects/network/host/' > /dev/null
            curl -X PATCH --header 'Content-Type: application/json' \
            --header 'Accept: application/json' \
            --header 'X-Restd-Err-Ack: all' \
            --header 'X-Restd-Lock-Override: yes' \
            --header 'Authorization: Basic access_token' -d \
            '{"access_control":"1","allowed_networks":["REF_NetworkAny"],"auth_profile":"","backend":["REF_RevBacWEBHost"],"be_path":"","comment":"","denied_networks":["'"$DN"'"],"hot_standby":false,"name":"ProxyN","path":"/subtree","status":true,"stickysession_id":"ROUTEID","stickysession_status":false,"websocket_passthrough":true}' \
            'https://my.fw/api/objects/reverse_proxy/location/REF_RevLocProxyN' > /dev/null

Expected behavior

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   492  100   307  100   185    291    175  0:00:01  0:00:01 --:--:--   466
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   841  100   504  100   337   7411   4955 --:--:-- --:--:-- --:--:-- 12367
80.187.101.140
80187101140
REF_NetHos
8018710114
REF_NetHos8018710114

Any additional information

actionban inside add_host.sh
just add echo $VAR to the end for debugging

Could you please help me and shed some light into this - How do I have to declare variables ? What am I doing wrong?
I really appreciate any help you can provide.

3rd party issue

Most helpful comment

Maybe you identify this immediately ...
Failed during configuration: Error in action definition 'UTM9': '%' must be followed by '%' or ...

Sure, as the error message (and documentation) says, the % char in (python) config files is a special character, e. g. used for substitution of variables and parameters, like %(var)s, etc.
So simply escape this (with additional %), like:

- VAR=${VAR1%?};
+ VAR=${VAR1%%?};

it will be interpolated by read of config to the single %-char.

All 6 comments

The RESTful API works from my own script, but not triggered by my custom action file.

I assume you used bash for your script? (because ${UTM2B//./} is a bashism).
As you can see fail2ban using sh (default shell for user running fail2ban on your system), so it would not work per design.
Try this example:

$ sh -c 'UTM2B=192.0.2.1; UTM=${UTM2B//./};'
sh: 1: Bad substitution

Either you should rewrite the action without unsupported constructs in sh shell, or you write it to the script using shebang for bash and then calling your script from action (so the script is executed in bash instead of sh).

Thank you for your fast feedback.
I followed your suggestion rewrite to shell did the trick!
Are there any possibility to get a variable with all banned IPs ?

Are there any possibility to get a variable with all banned IPs ?

It could be possible (but in which format)...
And I'm also wondering for which purposes it should be good.

One can also use fail2ban-client get <JAIL> banned (see 54b2208690e3c2fff00fbd9b197984d880e29a02, released with newest version).

Well, I'm testing my construction :D It is not bad at all, but some silly thing is that the WAF
can only handle host objects to block and not host groups. So each time f2b detects a new ip, my script must send all in the banned list! If not, that’s at the moment the case, only the newest ip is banned. :( My client is outdated but I could work with
fail2ban-client status owncloud |grep Banned

Done! Now a protocol is written that knows all blocked IP addresses,
In case of someone else is playing around this will be added there and the script use all of these to update the access_control list from the proxy with only one call “curl patch” - So nobody goes through my fingers anymore: D

Btw. Maybe you identify this immediately why it was not possible to use;
Just to format the value to my needs:
No Issues in sh shell: sh -c 'VAR=${VAR1%?};'
but in fail2ban: fail2ban-server[4540]: Failed during configuration: Error in action definition 'UTM9': '%' must be followed by '%' or '(', found: '%?};\ncurl -X POST --header \'Content-Type: application

Thanks for this absolutely brilliant software!

Maybe you identify this immediately ...
Failed during configuration: Error in action definition 'UTM9': '%' must be followed by '%' or ...

Sure, as the error message (and documentation) says, the % char in (python) config files is a special character, e. g. used for substitution of variables and parameters, like %(var)s, etc.
So simply escape this (with additional %), like:

- VAR=${VAR1%?};
+ VAR=${VAR1%%?};

it will be interpolated by read of config to the single %-char.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xtrmbuster picture xtrmbuster  Â·  3Comments

4Syno picture 4Syno  Â·  6Comments

eNTi picture eNTi  Â·  4Comments

thereporter42 picture thereporter42  Â·  7Comments

Madh93 picture Madh93  Â·  6Comments