Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Harden Nextcloud 17+ with Fail2Ban, GUI and WebDAV - Ubuntu 20.04

Fail2ban and Nextcloud

Prerequsits

  • Ubuntu 20.04
  • nextcloud, fail2ban and e.g. iptables are installed

Short how-to harden your Nextcloud Server with Fail2Ban

Install fail2ban:

sudo apt update && sudo apt install fail2ban -y

Create the Nextcloud-filter:

sudo nano /etc/fail2ban/filter.d/nextcloud.conf

Paste the following lines, this will cover GUI Failed login and WebDAV:

[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"

If you want to protect also from direct IP access or wrong Domain name access configure it as below (as per https://github.com/nextcloud/vm/blob/master/apps/fail2ban.sh)

[Definition]
_groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
failregex = ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
            ^\{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"

Create a new jail:

sudo nano /etc/fail2ban/jail.d/nextcloud.local

Paste the following rows:

[nextcloud]
backend = auto
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
#Number of retrys before to ban
maxretry = 3
#time in seconds
bantime = 36000
findtime = 36000
#Log path, on Ubuntu usually is following
logpath = /var/www/nextcloud/data/nextcloud.log
#For Univention Appliances the logfile is in /var/lib/univention-appcenter/apps/nextcloud/data/nextcloud-data/nextcloud.log
#logpath = /var/lib/univention-appcenter/apps/nextcloud/data/nextcloud-data/nextcloud.log

Re-start the fail2ban-service:

sudo service fail2ban restart

and enjoy your Nextcloud-Server!


P.S.

Pattern For GUI:

#Nextcloud 17+
{"reqId":"BRHKHyh1lVFon5D33u4K","level":2,"time":"2020-01-13T12:34:00+00:00","remoteAddr":"10.11.12.13","user":"--","app":"no app in context","method":"POST","url":"\/index.php\/login","message":"Login failed: Tralololjlkl (Remote IP: 10.11.12.13)","userAgent":"Mozilla\/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko\/20100101 Firefox\/72.0","version":"17.0.2.1"}
{"reqId":"y6OUpenieOevULRMaAFi","level":1,"time":"2020-06-10T13:19:06+00:00","remoteAddr":"10.11.12.13","user":"--","app":"core","method":"GET","url":"/","message":"Trusted domain error. \"10.11.12.13\" tried to access using \"Trololo.com\" as host.","userAgent":"curl/7.58.0","version":"18.0.6.0"}

#Nextcloud <= 16
{"reqId":"bFnTdevf7ZdCMQ5ddmNl","level":2,"time":"2019-04-03T21:49:30+00:00","remoteAddr":"10.11.12.13","user":"--","app":"core","method":"POST","url":"\/index.php\/login","message":"Login failed: 'Tralololjlkl' (Remote IP: '10.11.12.13')","userAgent":"Mozilla\/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko\/20100101 Firefox\/66.0","version":"15.0.5.3"}

For WebDav:

#Nextcloud 17+
{"reqId":"lqdbtb7y3pZSv8sGM0mD","level":2,"time":"2020-01-26T22:20:32+00:00","remoteAddr":"10.11.12.13","user":"--","app":"core","method":"PUT","url":"\/remote.php\/webdav\/files","message":"Login failed: 'webdavlogin' (Remote IP: '10.11.12.13')","userAgent":"curl\/7.58.0","version":"17.0.2.1"}

#Nextcloud <= 16
{"reqId":"Q4wX3I4LOUeuMV4wRK91","level":2,"time":"2019-04-03T22:14:11+00:00","remoteAddr":"10.11.12.13","user":"--","app":"core","method":"GET","url":"\/remote.php\/dav\/files","message":"Login failed: 'test' (Remote IP: '10.11.12.13')","userAgent":"Mozilla\/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko\/20100101 Firefox\/66.0","version":"15.0.5.3"}

Test WebUI and Webdav and Trusted domain error.

# fail2ban-regex /var/nextcloud/data/nextcloud.log /etc/fail2ban/filter.d/nextcloud.local -v --print-all-matched

Running tests
=============

Use   failregex filter file : nextcloud, basedir: /etc/fail2ban
Use      datepattern : ,?\s*"time"\s*:\s*"Year-Month-Day[T ]24hour:Minute:Second(Zone offset)?"
Use         log file : /var/nextcloud/data/nextcloud.log
Use         encoding : UTF-8


Results
=======

Failregex: 3 total
|-  #) [# of hits] regular expression
|   1) [2] ^\{(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*),?\s*"remoteAddr":"<HOST>"(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*),?\s*"message":"Login failed:
|      10.11.12.13  Wed Jun 10 13:07:43 2020
|      10.11.12.13  Wed Jun 10 13:07:50 2020
|   2) [1] ^\{(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*),?\s*"remoteAddr":"<HOST>"(?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*),?\s*"message":"Trusted domain error.
|      10.11.12.13  Wed Jun 10 15:19:06 2020
`-

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [1654] ,?\s*"time"\s*:\s*"Year-Month-Day[T ]24hour:Minute:Second(Zone offset)?"
`-

Lines: 1654 lines, 0 ignored, 3 matched, 1651 missed
[processed in 0.61 sec]

|- Matched line(s):
|  {"reqId":"2uzhhzlkkzXEp9yICWXE","level":2,"time":"2020-06-10T11:07:43+00:00","remoteAddr":"10.11.12.13","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: Gggggg (Remote IP: 10.11.12.13)","userAgent":"Mozilla/5.0 (Android 9; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0","version":"18.0.6.0"}
|  {"reqId":"2QPK9SkSVBj6r2d9ojnk","level":2,"time":"2020-06-10T11:07:50+00:00","remoteAddr":"10.11.12.13","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: Gggggvvvvg (Remote IP: 10.11.12.13)","userAgent":"Mozilla/5.0 (Android 9; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0","version":"18.0.6.0"}
|  {"reqId":"y6OUpenieOevULRMaAFi","level":1,"time":"2020-06-10T13:19:06+00:00","remoteAddr":"10.11.12.13","user":"--","app":"core","method":"GET","url":"/","message":"Trusted domain error. \"10.11.12.13\" tried to access using \"Trololo.com\" as host.","userAgent":"curl/7.58.0","version":"18.0.6.0"}
`-
Missed line(s): too many to print.  Use --print-all-missed to print all 1651 lines
@tiagofreire-pt

This comment has been minimized.

Copy link

@tiagofreire-pt tiagofreire-pt commented Jul 18, 2019

For Univention Appliances the logfile is in: /var/lib/univention-appcenter/apps/nextcloud/data/nextcloud-data/nextcloud.log

@badgateway666

This comment has been minimized.

Copy link

@badgateway666 badgateway666 commented Jan 8, 2020

The Pattern did change: There are no '' around the login-name and the ip anymore.

[Definition]
failregex=^{.Login failed: . (Remote IP: ).*}$
ignoreregex =

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Jan 13, 2020

Thanks!
Did it changed for Trusted Domain error also?

@stucksubstitute

This comment has been minimized.

Copy link

@stucksubstitute stucksubstitute commented Jan 26, 2020

The pattern doesn't work. See i.e. here. Any suggestions?

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Jan 26, 2020

The pattern doesn't work. See i.e. here. Any suggestions?

Could you please try it? What you provide is independend of this solution. I will try to push correction into nextcloudpi repo --> nextcloud/nextcloudpi#1070

@datdomse

This comment has been minimized.

Copy link

@datdomse datdomse commented Feb 27, 2020

The Pattern doesn't work with webdav.
I used this from here.

[Definition] failregex = ^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)","level":2,"time":".*"}$ ^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","app":"core".*","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)".*}$ ^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user":".*","app":".*","method":".*","url":".*","message":"Login failed: .* \(Remote IP: <HOST>\).*}$ ignoreregex =

Now it finally works with Nextcloud 18 stable.

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Feb 27, 2020

@datdomse you are right, I check source code of NC and it seems that it is producing different logs output depends on different issues. This is not the best practice how to implement it.
I added 2nd line to enable checks of WebDav in a config and also test. Thanks you for reporting.

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Apr 2, 2020

Thanks to Bernie_O we have one common line to catch it all now!
Check this for more info: https://help.nextcloud.com/t/fail2ban-native-support/602/26?u=gas85

@cfanatic

This comment has been minimized.

Copy link

@cfanatic cfanatic commented Apr 2, 2020

Came to say thanks. Great how-to.

@hansbaer443

This comment has been minimized.

Copy link

@hansbaer443 hansbaer443 commented Apr 16, 2020

First of all thank you very much for this how-to.
With my setup fail2ban didn't match trusted domain errors with the RE ^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*\$
I'm no expert with REs but can it be, that there shouldn't be a backslash just before the new line ("$")?
I tried it with ^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*$ and it seems to be working.

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Apr 16, 2020

You means it should looks like this? ^."remoteAddr":"<HOST>".Trusted domain error.$

@hansbaer443

This comment has been minimized.

Copy link

@hansbaer443 hansbaer443 commented Apr 16, 2020

I updated my earlyer post. Sorry it was unclear because I dindn't use the "insert code" function.

You means it should looks like this? ^."remoteAddr":"".Trusted domain error.$

Nearley, I think it should be ^.*\"remoteAddr\":\"<HOST>\".*Trusted domain error.*$

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Apr 16, 2020

Thanks! I did update the post.

@Hydranet

This comment has been minimized.

Copy link

@Hydranet Hydranet commented Jun 10, 2020

I tried the regex used in the filter you mentioned here for nextcloud 18 but it didn't work for me. I found another one that did work for me, might be worth looking into.

@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Jun 10, 2020

@Hydranet Could you please provide more information what is the issue?

Just tested it on a 18.0.6 and it works as expected. You can see that IP 10.10.10.10 is recognized by fail2ban, but due to whitelist it is not banned and Ignored.

2020-06-10 12:47:05,573 fail2ban.actions        [1726]: NOTICE  [apache-directIPAccess] Ban 256.257.258.259
2020-06-10 13:07:43,693 fail2ban.filter         [1726]: INFO    [nextcloud] Ignore 10.10.10.10 by ip
2020-06-10 13:07:50,002 fail2ban.filter         [1726]: INFO    [nextcloud] Ignore 10.10.10.10 by ip
2020-06-10 13:09:27,006 fail2ban.actions        [1726]: NOTICE  [apache-directIPAccess] Unban 257.258.259.260
@Hydranet

This comment has been minimized.

Copy link

@Hydranet Hydranet commented Jun 10, 2020

@GAS85

Nexcloud version: 18.0.6
Php version: 7.2

I tried attaching a text file so it would be easier to read, but for some reason github is refusing my txt file.

This is my log output when trying to use the nextcloud_fail2ban.md filter:
2020-06-10 13:21:16,905 fail2ban.filter         [9248]: WARNING Found a match for '{"reqId":"XuDCEyXVf4x1q0Gp@5g80QAAAIg","level":2,"time":"2020-06-10T11:21:16+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}' but no valid date/time found for '{"reqId":"XuDCEyXVf4x1q0Gp@5g80QAAAIg","level":2,"time":"2020-06-10T11:21:16+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}'. 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.
2020-06-10 13:22:16,206 fail2ban.filter         [9248]: WARNING Found a match for '{"reqId":"XuDCTiXVf4x1q0Gp@5g86QAAAI4","level":2,"time":"2020-06-10T11:22:16+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}' but no valid date/time found for '{"reqId":"XuDCTiXVf4x1q0Gp@5g86QAAAI4","level":2,"time":"2020-06-10T11:22:16+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}'. 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.
2020-06-10 13:23:19,521 fail2ban.filter         [9248]: WARNING Found a match for '{"reqId":"XuDCjiXVf4x1q0Gp@5g9BgAAAIM","level":2,"time":"2020-06-10T11:23:19+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}' but no valid date/time found for '{"reqId":"XuDCjiXVf4x1q0Gp@5g9BgAAAIM","level":2,"time":"2020-06-10T11:23:19+00:00","remoteAddr":"10.31.251.11","user":"--","app":"no app in context","method":"POST","url":"/index.php/login","message":"Login failed: testuser (Remote IP: 10.31.251.11)","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36","version":"18.0.6.0"}'. 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.

This my log output when using the nextcloudpie filter:
2020-06-10 13:25:46,850 fail2ban.filter         [9433]: INFO    [nextcloud] Found 10.31.251.11 - 2020-06-10 13:25:46
2020-06-10 13:27:04,187 fail2ban.filter         [9433]: INFO    [nextcloud] Found 10.31.251.11 - 2020-06-10 13:27:04
2020-06-10 13:27:58,284 fail2ban.filter         [9433]: INFO    [nextcloud] Found 10.31.251.11 - 2020-06-10 13:27:58
2020-06-10 13:27:59,006 fail2ban.actions        [9433]: NOTICE  [nextcloud] Ban 10.31.251.11
2020-06-10 13:29:59,267 fail2ban.actions        [9433]: NOTICE  [nextcloud] Unban 10.31.251.11
@GAS85

This comment has been minimized.

Copy link
Owner Author

@GAS85 GAS85 commented Jun 10, 2020

Have not Idea why it is not causing any warning on my system, but here is description of this issue with a good explanation fail2ban/fail2ban#2485 (comment)

So I added new rules and also updated test section. Thanks, @Hydranet for notice it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.