diff --git a/conf/fail2ban/dovecotimap.conf b/conf/fail2ban/filter.d/dovecotimap.conf similarity index 100% rename from conf/fail2ban/dovecotimap.conf rename to conf/fail2ban/filter.d/dovecotimap.conf diff --git a/conf/fail2ban/filter.d/miab-management-daemon.conf b/conf/fail2ban/filter.d/miab-management-daemon.conf new file mode 100644 index 00000000..0b0489c2 --- /dev/null +++ b/conf/fail2ban/filter.d/miab-management-daemon.conf @@ -0,0 +1,12 @@ +# Fail2Ban filter Mail-in-a-Box management daemon + +[INCLUDES] + +before = common.conf + +[Definition] + +_daemon = mailinabox + +failregex = Mail-in-a-Box Management Daemon: Failed login attempt from ip - timestamp .* +ignoreregex = diff --git a/conf/fail2ban/filter.d/miab-munin.conf b/conf/fail2ban/filter.d/miab-munin.conf new file mode 100644 index 00000000..b254cc62 --- /dev/null +++ b/conf/fail2ban/filter.d/miab-munin.conf @@ -0,0 +1,7 @@ +[INCLUDES] + +before = common.conf + +[Definition] +failregex= - .*GET /admin/munin/.* HTTP/1.1\" 401.* +ignoreregex = diff --git a/conf/fail2ban/filter.d/miab-owncloud.conf b/conf/fail2ban/filter.d/miab-owncloud.conf new file mode 100644 index 00000000..a9a13f2c --- /dev/null +++ b/conf/fail2ban/filter.d/miab-owncloud.conf @@ -0,0 +1,7 @@ +[INCLUDES] + +before = common.conf + +[Definition] +failregex=Login failed: .*Remote IP: '[\)'] +ignoreregex = diff --git a/conf/fail2ban/filter.d/miab-postfix-submission.conf b/conf/fail2ban/filter.d/miab-postfix-submission.conf new file mode 100644 index 00000000..236e1331 --- /dev/null +++ b/conf/fail2ban/filter.d/miab-postfix-submission.conf @@ -0,0 +1,7 @@ +[INCLUDES] + +before = common.conf + +[Definition] +failregex=postfix/submission/smtpd.*warning.*\[\]: .* authentication (failed|aborted) +ignoreregex = diff --git a/conf/fail2ban/filter.d/miab-roundcube.conf b/conf/fail2ban/filter.d/miab-roundcube.conf new file mode 100644 index 00000000..c6979c85 --- /dev/null +++ b/conf/fail2ban/filter.d/miab-roundcube.conf @@ -0,0 +1,9 @@ +[INCLUDES] + +before = common.conf + +[Definition] + +failregex = IMAP Error: Login failed for .*? from \. AUTHENTICATE.* + +ignoreregex = diff --git a/conf/fail2ban/jail.d/dovecot.conf b/conf/fail2ban/jail.d/dovecot.conf new file mode 100644 index 00000000..29b0e65a --- /dev/null +++ b/conf/fail2ban/jail.d/dovecot.conf @@ -0,0 +1,5 @@ +[dovecot] +enabled = true +filter = dovecotimap +findtime = 30 +maxretry = 20 diff --git a/conf/fail2ban/jail.d/miab-management-daemon.conf b/conf/fail2ban/jail.d/miab-management-daemon.conf new file mode 100644 index 00000000..f5920dfe --- /dev/null +++ b/conf/fail2ban/jail.d/miab-management-daemon.conf @@ -0,0 +1,7 @@ +[miab-management-daemon] +enabled = true +filter = miab-management-daemon +port = http,https +logpath = /var/log/syslog +maxretry = 20 +findtime = 30 diff --git a/conf/fail2ban/jail.d/miab-munin.conf b/conf/fail2ban/jail.d/miab-munin.conf new file mode 100644 index 00000000..9d72c4f2 --- /dev/null +++ b/conf/fail2ban/jail.d/miab-munin.conf @@ -0,0 +1,7 @@ +[miab-munin] +enabled = true +port = http,https +filter = miab-munin +logpath = /var/log/nginx/access.log +maxretry = 20 +findtime = 30 diff --git a/conf/fail2ban/jail.d/miab-owncloud.conf b/conf/fail2ban/jail.d/miab-owncloud.conf new file mode 100644 index 00000000..edb3a949 --- /dev/null +++ b/conf/fail2ban/jail.d/miab-owncloud.conf @@ -0,0 +1,7 @@ +[miab-owncloud] +enabled = true +port = http,https +filter = miab-owncloud +logpath = STORAGE_ROOT/owncloud/owncloud.log +maxretry = 20 +findtime = 30 diff --git a/conf/fail2ban/jail.d/miab-postfix-submission.conf b/conf/fail2ban/jail.d/miab-postfix-submission.conf new file mode 100644 index 00000000..6033214f --- /dev/null +++ b/conf/fail2ban/jail.d/miab-postfix-submission.conf @@ -0,0 +1,7 @@ +[miab-postfix-submission] +enabled = true +port = 587 +filter = miab-postfix-submission +logpath = /var/log/mail.log +maxretry = 20 +findtime = 30 diff --git a/conf/fail2ban/jail.d/miab-roundcube.conf b/conf/fail2ban/jail.d/miab-roundcube.conf new file mode 100644 index 00000000..e84cc4d1 --- /dev/null +++ b/conf/fail2ban/jail.d/miab-roundcube.conf @@ -0,0 +1,7 @@ +[miab-roundcube] +enabled = true +port = http,https +filter = miab-roundcube +logpath = /var/log/roundcubemail/errors +maxretry = 20 +findtime = 30 diff --git a/conf/fail2ban/jail.d/recidive.conf b/conf/fail2ban/jail.d/recidive.conf new file mode 100644 index 00000000..0867e5ca --- /dev/null +++ b/conf/fail2ban/jail.d/recidive.conf @@ -0,0 +1,14 @@ +[recidive] +enabled = true +maxretry = 10 +action = iptables-allports[name=recidive] +# In the recidive section of jail.conf the action contains: +# +# action = iptables-allports[name=recidive] +# sendmail-whois-lines[name=recidive, logpath=/var/log/fail2ban.log] +# +# The last line on the action will sent an email to the configured address. This mail will +# notify the administrator that someone has been repeatedly triggering one of the other jails. +# By default we don't configure this address and no action is required from the admin anyway. +# So the notification is ommited. This will prevent message appearing in the mail.log that mail +# can't be delivered to fail2ban@$HOSTNAME. diff --git a/conf/fail2ban/jail.d/sasl.conf b/conf/fail2ban/jail.d/sasl.conf new file mode 100644 index 00000000..b01f79de --- /dev/null +++ b/conf/fail2ban/jail.d/sasl.conf @@ -0,0 +1,2 @@ +[sasl] +enabled = true diff --git a/conf/fail2ban/jail.d/ssh-ddos.conf b/conf/fail2ban/jail.d/ssh-ddos.conf new file mode 100644 index 00000000..522ae99f --- /dev/null +++ b/conf/fail2ban/jail.d/ssh-ddos.conf @@ -0,0 +1,2 @@ +[ssh-ddos] +enabled = true diff --git a/conf/fail2ban/jail.d/ssh.conf b/conf/fail2ban/jail.d/ssh.conf new file mode 100644 index 00000000..0d0f6aab --- /dev/null +++ b/conf/fail2ban/jail.d/ssh.conf @@ -0,0 +1,3 @@ +[ssh] +maxretry = 7 +bantime = 3600 diff --git a/conf/fail2ban/jail.local b/conf/fail2ban/jail.local index cc741c80..4150eeeb 100644 --- a/conf/fail2ban/jail.local +++ b/conf/fail2ban/jail.local @@ -5,36 +5,3 @@ # ping services over the public interface so we should whitelist that address of # ours too. The string is substituted during installation. ignoreip = 127.0.0.1/8 PUBLIC_IP - -# JAILS - -[ssh] -maxretry = 7 -bantime = 3600 - -[ssh-ddos] -enabled = true - -[sasl] -enabled = true - -[dovecot] -enabled = true -filter = dovecotimap -findtime = 30 -maxretry = 20 - -[recidive] -enabled = true -maxretry = 10 -action = iptables-allports[name=recidive] -# In the recidive section of jail.conf the action contains: -# -# action = iptables-allports[name=recidive] -# sendmail-whois-lines[name=recidive, logpath=/var/log/fail2ban.log] -# -# The last line on the action will sent an email to the configured address. This mail will -# notify the administrator that someone has been repeatedly triggering one of the other jails. -# By default we don't configure this address and no action is required from the admin anyway. -# So the notification is ommited. This will prevent message appearing in the mail.log that mail -# can't be delivered to fail2ban@$HOSTNAME. diff --git a/management/daemon.py b/management/daemon.py index 5400925f..9bc6429b 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -1,7 +1,8 @@ #!/usr/bin/python3 -import os, os.path, re, json +import os, os.path, re, json, time import subprocess + from functools import wraps from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response @@ -45,6 +46,9 @@ def authorized_personnel_only(viewfunc): privs = [] error = "Incorrect username or password" + # Write a line in the log recording the failed login + log_failed_login(request) + # Authorized to access an API view? if "admin" in privs: # Call view func. @@ -117,6 +121,9 @@ def me(): try: email, privs = auth_service.authenticate(request, env) except ValueError as e: + # Log the failed login + log_failed_login(request) + return json_response({ "status": "invalid", "reason": "Incorrect username or password", @@ -583,6 +590,22 @@ def munin_cgi(filename): app.logger.warning("munin_cgi: munin-cgi-graph returned 404 status code. PATH_INFO=%s", env['PATH_INFO']) return response +def log_failed_login(request): + # We need to figure out the ip to list in the message, all our calls are routed + # through nginx who will put the original ip in X-Forwarded-For. + # During setup we call the management interface directly to determine the user + # status. So we can't always use X-Forwarded-For because during setup that header + # will not be present. + if request.headers.getlist("X-Forwarded-For"): + ip = request.headers.getlist("X-Forwarded-For")[0] + else: + ip = request.remote_addr + + # We need to add a timestamp to the log message, otherwise /dev/log will eat the "duplicate" + # message. + app.logger.warning( "Mail-in-a-Box Management Daemon: Failed login attempt from ip %s - timestamp %s" % (ip, time.time())) + + # APP if __name__ == '__main__': diff --git a/setup/owncloud.sh b/setup/owncloud.sh index 94dd70b6..6abaa3f9 100755 --- a/setup/owncloud.sh +++ b/setup/owncloud.sh @@ -163,7 +163,10 @@ fi # so set it here. It also can change if the box's PRIMARY_HOSTNAME changes, so # this will make sure it has the right value. # * Some settings weren't included in previous versions of Mail-in-a-Box. +# * We need to set the timezone to the system timezone to allow fail2ban to ban +# users within the proper timeframe # Use PHP to read the settings file, modify it, and write out the new settings array. +TIMEZONE=$(cat /etc/timezone) CONFIG_TEMP=$(/bin/mktemp) php < $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php; /etc/fail2ban/jail.local -cp conf/fail2ban/dovecotimap.conf /etc/fail2ban/filter.d/dovecotimap.conf + +cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/ +cp -f conf/fail2ban/jail.d/* /etc/fail2ban/jail.d/ + +sed -i "s#STORAGE_ROOT#$STORAGE_ROOT#" /etc/fail2ban/jail.d/miab-owncloud.conf restart_service fail2ban