diff --git a/ChangeLog b/ChangeLog index 6182255a..04b06c31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20464,3 +20464,7 @@ * Added an 'Encoder Profiles' dialog to rdadmin(1). * Removed static content dependencies from the WebGet service. * Refactored the WebGet service to use Encoder Profiles. +2020-10-15 Fred Gleason + * Added a 'RDApplication::logAuthenticationFailure()' method. + * Added a sample Fail2Ban jail filter for the WebAPI in + 'conf/rivendell-webapi.conf'. diff --git a/conf/Makefile.am b/conf/Makefile.am index 0997b1b2..f1cfc221 100644 --- a/conf/Makefile.am +++ b/conf/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am ## -## (C) Copyright 2002-2019 Fred Gleason +## (C) Copyright 2002-2020 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as @@ -33,6 +33,7 @@ EXTRA_DIST = asound.conf-sample\ rd-bin.conf.in\ rd.conf-sample\ rivendell.pam\ + rivendell-webapi.conf\ syslog.conf-sample CLEANFILES = *~ diff --git a/conf/rivendell-webapi.conf b/conf/rivendell-webapi.conf new file mode 100644 index 00000000..aee982d9 --- /dev/null +++ b/conf/rivendell-webapi.conf @@ -0,0 +1,38 @@ +# Fail2Ban jail filter for Rivendell WebAPI +# +# (C) Copyright 2020 Fred Gleason +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# To enable this, put entries like this in your '/etc/fail2ban/jail.local' +# file: +# +# [rivendell-webapi] +# enabled = true +# filter = rivendell-webapi +# logpath = /var/log/rivendell/operations +# port = http,https +# + +[INCLUDES] + +before = common.conf + +after = rivendell-webapi.local + +[Definition] + +failregex = failed WebAPI login from +ignoreregex = +journalmatch = diff --git a/lib/rdapplication.cpp b/lib/rdapplication.cpp index e5a39e23..d0070a11 100644 --- a/lib/rdapplication.cpp +++ b/lib/rdapplication.cpp @@ -312,6 +312,21 @@ void RDApplication::addTempFile(const QString &pathname) } +void RDApplication::logAuthenticationFailure(const QHostAddress &orig_addr, + const QString &login_name) +{ + if(login_name.isEmpty()) { + syslog(LOG_NOTICE,"failed WebAPI login from %s", + orig_addr.toString().toUtf8().constData()); + } + else { + syslog(LOG_NOTICE,"failed WebAPI login from %s for user \"%s\"", + orig_addr.toString().toUtf8().constData(), + login_name.toUtf8().constData()); + } +} + + void RDApplication::syslog(int priority,const char *fmt,...) const { va_list args; diff --git a/lib/rdapplication.h b/lib/rdapplication.h index 51772713..1b08679c 100644 --- a/lib/rdapplication.h +++ b/lib/rdapplication.h @@ -66,6 +66,8 @@ class RDApplication : public QObject bool dropTable(const QString &tbl_name); void addTempFile(const QString &pathname); void syslog(int priority,const char *fmt,...) const; + void logAuthenticationFailure(const QHostAddress &orig_addr, + const QString &login_name=QString()); static void syslog(RDConfig *config,int priority,const char *fmt,...); private slots: diff --git a/rivendell.spec.in b/rivendell.spec.in index 37477450..fcb82244 100644 --- a/rivendell.spec.in +++ b/rivendell.spec.in @@ -138,6 +138,8 @@ cat conf/rd.conf-sample | sed s/SyslogFacility=1/SyslogFacility=23/ > $RPM_BUILD cp conf/asound.conf-sample $RPM_BUILD_ROOT/@DOC_PATH@/ mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/misc cp conf/syslog.conf-sample $RPM_BUILD_ROOT/@DOC_PATH@/ +mkdir -p $RPM_BUILD_ROOT/etc/fail2ban/filter.d +cp conf/rivendell-webapi.conf $RPM_BUILD_ROOT/etc/fail2ban/filter.d/ cp docs/misc/colors $RPM_BUILD_ROOT/@DOC_PATH@/misc/ cp docs/misc/reports.txt $RPM_BUILD_ROOT/@DOC_PATH@/misc/ cp docs/misc/ALSA.txt $RPM_BUILD_ROOT/@DOC_PATH@/misc/ @@ -387,6 +389,7 @@ rm -rf $RPM_BUILD_ROOT /etc/security/console.apps/rddbconfig-root /etc/pam.d/rddbconfig-root /etc/pam.d/rivendell +/etc/fail2ban/filter.d/rivendell-webapi.conf /lib/systemd/system/rivendell.service %{_mandir}/man1/rdairplay.1.gz %{_mandir}/man1/rdalsaconfig.1.gz diff --git a/web/rdxport/rdxport.cpp b/web/rdxport/rdxport.cpp index e7acad6a..e1a4df46 100644 --- a/web/rdxport/rdxport.cpp +++ b/web/rdxport/rdxport.cpp @@ -416,13 +416,16 @@ bool Xport::Authenticate() // Next, check the whitelist // if(!xport_post->getValue("LOGIN_NAME",&name)) { + rda->logAuthenticationFailure(xport_post->clientAddress()); return false; } if(!xport_post->getValue("PASSWORD",&passwd)) { + rda->logAuthenticationFailure(xport_post->clientAddress(),name); return false; } rda->user()->setName(name); if(!rda->user()->exists()) { + rda->logAuthenticationFailure(xport_post->clientAddress(),name); return false; } if((xport_post->clientAddress().toIPv4Address()>>24)==127) { // Localhost @@ -443,6 +446,7 @@ bool Xport::Authenticate() // Finally, try password // if(!rda->user()->checkPassword(passwd,false)) { + rda->logAuthenticationFailure(xport_post->clientAddress(),name); return false; } TryCreateTicket(name); diff --git a/web/webget/webget.cpp b/web/webget/webget.cpp index 8b312143..6685fc4d 100644 --- a/web/webget/webget.cpp +++ b/web/webget/webget.cpp @@ -365,15 +365,18 @@ bool MainObject::Authenticate() QString passwd; if(!webget_post->getValue("LOGIN_NAME",&name)) { + rda->logAuthenticationFailure(webget_post->clientAddress()); return false; } if(!webget_post->getValue("PASSWORD",&passwd)) { + rda->logAuthenticationFailure(webget_post->clientAddress(),name); return false; } RDUser *user=new RDUser(name); if((!user->exists())|| (!user->checkPassword(passwd,false))|| (!user->webgetLogin())) { + rda->logAuthenticationFailure(webget_post->clientAddress(),name); return false; }