In this guide, I will show you how to configure your ownCloud server so that brute force attacks are one less thing to worry about. Not only will fail2ban block someone from having X number of failed login attempts to your ownCloud server, it will also notify you via pushbullet that an attempt has been blocked.

So lets begin!

Step 1: Install fail2ban and ownCloud filters

Following this excellent guide here (src: https://github.com/AykutCevik/owncloud-fail2ban) you can have fail2ban and ownCloud filters configured in no time. Simply run the command:

curl -s "https://raw.githubusercontent.com/AykutCevik/owncloud-fail2ban/master/automated-install/install.sh" | bash

.. and follow the instructions. Make sure you enter the logfile path correctly; i.e. /var/log/owncloud.log, or /var/www/owncloud/owncloud.log, etc.

Once configured, this will add two main files of interest:

/etc/fail2ban/filter.d/owncloud.conf
/etc/fail2ban/jail.d/owncloud.conf

The first, ‘/filter.d/’, is the regex that fail2ban will use to parse your specified ownCloud log for failed login attempts.

The second, ‘/jail.d/’, tells fail2ban to actively use that filter, what port to monitor on, and the logpath (edit the logpath here if you entered it wrong during setup).

The logfile, /var/log/owncloud.log (for example) must log using a few specifics. To set these specifics, crack open your config.php (i.e. /var/www/owncloud/config/config.php) and set the following directives:

 'logtimezone' => 'Europe/London',
 'logfile' => '/var/log/owncloud.log',
 'loglevel' => 2,
 'log_authfailip' => true,

Make sure that you have the logtimezone set to your local timezone, otherwise fail2ban wont work.

.. and thats fail2ban setup. So how does it work?

Fail2ban will create a new iptables chain called ‘f2b-owncloud’, which is visible if you run ‘iptables -nL –lin’ as below:

Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 127.0.0.1 0.0.0.0/0 /* allow anything that comes from lo */
2 f2b-owncloud tcp -- 0.0.0.0/0 192.168.0.22 tcp dpt:443 /* HTTPS to owncloud chain */
...
21 REJECT all -- 0.0.0.0/0 0.0.0.0/0 /* drop everything else */ reject-with icmp-port-unreachable
...
Chain f2b-owncloud (1 references)
num target prot opt source destination

Basically, iptables will detect if traffic has come into the server on the port specified earlier (https), and will direct all traffic to the new chain ‘f2b-owncloud’. When fail2ban detects an IP has had 5 failed logins, it will then add a iptables REJECT rule for that specific IP, at the top of the f2b-owncloud chain. This means that the IP will be rejected, but all other traffic will pass.

Obviously this means that HTTPS traffic will bypass all other iptables rules in your INPUT chain as it is being diverted to the f2b-owncloud chain, so ensure you add your rules to that chain also, i.e.:

Chain f2b-owncloud (1 references)
num target prot opt source destination
1 ACCEPT tcp -- 0.0.0.0/0 192.168.0.22 tcp dpt:443 match-set gb src /* Only GB IPs allowed from WWW to 443 */
2 REJECT all -- 0.0.0.0/0 0.0.0.0/0 /* drop everything else */ reject-with icmp-port-unreachable

Using ipset, I only allow GB IP addresses access to HTTPS, so I’ve had to add this rule to the f2b-owncloud chain, along with a REJECT all. Now when fail2ban bans an IP, it’ll get added to the top. The flow will be then be:

  1. Is the source IP banned? If yes, then reject, if not ..
  2. Is the source IP from the GB IP range? If no, then reject, if yes, then allow.
  3. Reject everything else.

Step 2: Testing

Next we need to test that fail2ban will actually block the malicious IP. Simply attempt some invalid logins, and then run the command ‘fail2ban-client status owncloud’:

root@server:/tmp/1076290# fail2ban-client status owncloud
Status for the jail: owncloud
|- Filter
| |- Currently failed: 0
| |- Total failed: 47
| `- File list: /var/log/owncloud.log
`- Actions
 |- Currently banned: 1
 |- Total banned: 5
 `- Banned IP list: 134.225.2.12

Here you can see you have now been banned from accessing the ownCloud server via HTTPS. You can confirm this via iptables -nl –lin:

Chain f2b-owncloud (1 references)
num target prot opt source destination
1 REJECT all -- 134.225.2.12 0.0.0.0/0 reject-with icmp-port-unreachable
2 ACCEPT tcp -- 0.0.0.0/0 192.168.0.22 tcp dpt:443 match-set gb src /* Only GB IPs allowed from WWW to 443 */
3 REJECT all -- 0.0.0.0/0 0.0.0.0/0 /* drop everything else */ reject-with icmp-port-unreachable

You should now be unable to access your owncloud server. To unban yourself, simply run:

fail2ban-client set owncloud unbanip 134.225.2.12

Where 134.. is your IP. If your not being being, double check the regex is valid and picking up your logs correctly using the command:

fail2ban-regex /var/log/owncloud.log /etc/fail2ban/filter.d/owncloud.conf -v

This should show an output similar to the below:

root@server:/tmp/1076290# fail2ban-regex /var/log/owncloud.log /etc/fail2ban/filter.d/owncloud.conf -v
Running tests
=============
Use failregex filter file : owncloud, basedir: /etc/fail2ban
Use log file : /var/log/owncloud.log
Use encoding : UTF-8
Results
=======
Failregex: 79 total
|- #) [# of hits] regular expression
| 1) [79] {"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: ''\)","level":2,"time":".*"}

<strong>| 134.225.2.12 Wed Jan 13 10:37:30 2016</strong>
<strong>| 134.225.2.12 Wed Jan 13 10:37:32 2016</strong>
<strong>| 134.225.2.12 Wed Jan 13 10:37:33 2016</strong>
<strong>| 134.225.2.12 Wed Jan 13 10:37:34 2016</strong>
<strong>| 134.225.2.12 Wed Jan 13 10:37:35 2016</strong>
`-
Ignoreregex: 0 total
Date template hits:
|- [# of hits] date format
| [114] Year-Month-Day[T ]24hour:Minute:Second(?:\.Microseconds)?(?:Zone offset)?
| [0] (?:DAY )?MON Day 24hour:Minute:Second(?:\.Microseconds)?(?: Year)?
| [0] Year(?P[-/.])Month(?P=_sep)Day 24hour:Minute:Second(?:,Microseconds)?
| [0] Day(?P[-/])Month(?P=_sep)(?:Year|Year2) 24hour:Minute:Second
| [0] Day(?P[-/])MON(?P=_sep)Year[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)?
| [0] Month/Day/Year:24hour:Minute:Second
| [0] Month-Day-Year 24hour:Minute:Second\.Microseconds
| [0] TAI64N
| [0] Epoch
| [0] ^24hour:Minute:Second
| [0] ^
| [0] ^Year2MonthDay ?24hour:Minute:Second
| [0] MON Day, Year 12hour:Minute:Second AMPM
| [0] ^MON-Day-Year2 24hour:Minute:Second
`-
Lines: 151 lines, 0 ignored, 79 matched, 72 missed [processed in 0.06 sec]
Missed line(s): too many to print. Use --print-all-missed to print all 72 lines

If you have issues, double check your /var/log/fail2ban.log. If you see an error like the below, then double check the config.php file for your owncloud server:

016-01-12 15:58:32,824 fail2ban.filter [13341]: WARNING Found a match for '{"reqId":"VpUHnn8AAQEAAEROfHIAAAAg","remoteAddr":"134.225.2.12","app":"core","message":"Login failed: \'ada\' (Remote IP: \'134.225.2.12\')","level":2,"time":"January 12, 2016 14:03:10"}' but no valid date/time found for '{"reqId":"VpUHnn8AAQEAAEROfHIAAAAg","remoteAddr":"134.225.2.12","app":"core","message":"Login failed: \'ada\' (Remote IP: \'134.225.2.12\')","level":2,"time":"January 12, 2016 14:03:10"}'. Please try setting a custom date pattern (see man page jail.conf(5)). If format is complex, please file a detailed issue on https://github.com/fail2ban/fail2ban/issues in order to get support for this format.

I saw this issue when I had a specific ‘logdateformat’ entry in config.php. Simply removing this entry solved my problem.

Step 3: Notifications

Now that fail2ban is actively blocking brute force attackers, we want to be notified about it. Doing this is rather simple, thanks to this excellent guide here (src: http://blog.meinside.pe.kr/How-to-get-Pushbullet-notification-on-Fail2ban-ban-actions/).

Firstly, install Go:

apt-get install golang-go

Next, setup your users .bashrc profile to contain the necessary paths by adding the following lines to ~/.bashrc:

export PATH=$PATH:$GOPATH/bin
export GOPATH=$HOME/work

Next, download the pushbullet-fail2ban.go code:

wget https://gist.githubusercontent.com/meinside/a5afeda2a854919dae12/raw/1e7ea48d5cef31bf024337bcd69b81919c9a51b0/pushbullet-fail2ban.go

Next, edit that file and enter your pushbullet API key (to find out your key, visit: https://www.pushbullet.com/account 

nano pushbullet-fail2ban.go

Find the line ‘MyPushbulletToken = ‘ ‘ and add your API key within the quotes (no spaces at the start or end).

Once done, build the notification script:

go get -u github.com/mitsuse/pushbullet-go
go build pushbullet-fail2ban.go

This will leave you with a file called ‘pushbullet-fail2ban’. Move this file to /etc/fail2ban/ using the command:

cp pushbullet-fail2ban /etc/fail2ban/

Next, test that the notification works using the command:

cd /etc/fail2ban/
./pushbullet-fail2ban "SSH" "8.8.8.8"

You should get an alert on your mobile phone similar to:

38786f12-83c7-11e5-95b4-f431a84c53d9

Next, we need to modify fail2ban to use this new pushbullet-fail2ban method. To do this, simply copy the method fail2ban uses to ban IP’s using iptables, and edit the copied method:

cd /etc/fail2ban/action.d
sudo cp iptables-multiport.conf iptables-multiport-letmeknow.conf
sudo nano iptables-multiport-letmeknow.conf

Within this file, find the line ‘actionban =’ and replace it with the following:

# (example)
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
            /etc/fail2ban/pushbullet-fail2ban "<name>" "<ip>"

 

Next, lets tell fail2ban to use this new method and not the existing ‘iptables-multiport.conf’. Edit the file /etc/fail2ban/jail.conf:

nano /etc/fail2ban/jail.conf

.. and amend the line ‘banaction’ (line 157) to look like:

banaction = iptables-multiport-letmeknow

And thats it. Simply restart fail2ban, update your iptables rules (if you have modified them as per my step 1) and your fail2ban protected owncloud is now ready:

sudo service fail2ban restart

Next time a naughty hacker tries to brute force your box, you’ll get a push notification similar to:

Screenshot 2016-01-13 11.23.49

Notes

My iptables rules for fail2ban/owncloud look like the below:

iptables -I INPUT 2 -d 192.168.0.22/32 -p tcp --dport 443 -j f2b-owncloud -m comment --comment "HTTPS to owncloud chain"
iptables -I INPUT 19 -m state --state ESTABLISHED,RELATED -j ACCEPT -m comment --comment "allow anything established/related"
iptables -A INPUT -j REJECT -m comment --comment "drop everything else"
# Fail2ban
 iptables -I f2b-owncloud 1 -p tcp -d 192.168.0.22/32 --dport 443 -m set --match-set gb src -j ACCEPT -m comment --comment "Only GB IPs allowed from WWW/443"
 iptables -A f2b-owncloud -j REJECT -m comment --comment "drop everything else"

These rules send all HTTPS traffic to the f2b-owncloud chain, and then the HTTPS traffic is parsed through that traffic. I have an ipset rule here, and then a reject to end the chain. Hope this helps!