From 3cdaca51a615f469b11e8f0c0bf5675daaa75a21 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 13 Jul 2015 12:15:00 +0200 Subject: [PATCH 1/3] fail2ban: make timeout & attempts configurable (#534) --- modules/fail2ban.cpp | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/modules/fail2ban.cpp b/modules/fail2ban.cpp index 2cffee80..37ae1e63 100644 --- a/modules/fail2ban.cpp +++ b/modules/fail2ban.cpp @@ -18,7 +18,11 @@ class CFailToBanMod : public CModule { public: - MODCONSTRUCTOR(CFailToBanMod) {} + MODCONSTRUCTOR(CFailToBanMod) { + AddHelpCommand(); + AddCommand("Timeout", static_cast(&CFailToBanMod::OnTimeoutCommand), "()", "The number of minutes IPs are blocked after a failed login."); + AddCommand("Attempts", static_cast(&CFailToBanMod::OnAttemptsCommand), "()", "The number of allowed failed login attempts."); + } virtual ~CFailToBanMod() {} bool OnLoad(const CString& sArgs, CString& sMessage) override { @@ -54,10 +58,36 @@ public: m_Cache.AddItem(sHost, count, m_Cache.GetTTL()); } - void OnModCommand(const CString& sCommand) override { - PutModule("This module can only be configured through its arguments."); - PutModule("The module argument is the number of minutes an IP"); - PutModule("is blocked after a failed login."); + void OnTimeoutCommand(const CString& sCommand) { + CString sArg = sCommand.Token(1); + + if (!sArg.empty()) { + unsigned int uTimeout = sArg.ToUInt(); + if (uTimeout == 0) { + PutModule("Usage: Timeout ()"); + } else { + m_Cache.SetTTL(uTimeout * 60 * 1000); + PutModule("Timeout: " + CString(uTimeout) + " min"); + } + } else { + PutModule("Timeout: " + CString(m_Cache.GetTTL() / 60 / 1000) + " min"); + } + } + + void OnAttemptsCommand(const CString& sCommand) { + CString sArg = sCommand.Token(1); + + if (!sArg.empty()) { + unsigned int uiAttempts = sArg.ToUInt(); + if (uiAttempts == 0) { + PutModule("Usage: Attempts ()"); + } else { + m_uiAllowedFailed = uiAttempts; + PutModule("Attempts: " + CString(uiAttempts)); + } + } else { + PutModule("Attempts: " + CString(m_uiAllowedFailed)); + } } void OnClientConnect(CZNCSock* pClient, const CString& sHost, unsigned short uPort) override { From f506a59993d097c824402d1574213a0ea6693b42 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 13 Jul 2015 12:15:30 +0200 Subject: [PATCH 2/3] Add TCacheMap::GetItems() --- include/znc/Utils.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/znc/Utils.h b/include/znc/Utils.h index 67566ce7..1ff5565b 100644 --- a/include/znc/Utils.h +++ b/include/znc/Utils.h @@ -334,6 +334,18 @@ public: m_mItems.clear(); } + /** + * @brief Returns all entries + */ + std::map GetItems() { + Cleanup(); + std::map mItems; + for (const auto& it : m_mItems) { + mItems[it.first] = it.second.second; + } + return mItems; + } + // Setters void SetTTL(unsigned int u) { m_uTTL = u; } // !Setters From c5d7b5778a752f89d2ce34106ca5f54662ec04a1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 13 Jul 2015 12:16:27 +0200 Subject: [PATCH 3/3] fail2ban: add BAN, UNBAN and LIST commands (#534) --- modules/fail2ban.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/modules/fail2ban.cpp b/modules/fail2ban.cpp index 37ae1e63..ff3a90d1 100644 --- a/modules/fail2ban.cpp +++ b/modules/fail2ban.cpp @@ -22,6 +22,9 @@ public: AddHelpCommand(); AddCommand("Timeout", static_cast(&CFailToBanMod::OnTimeoutCommand), "()", "The number of minutes IPs are blocked after a failed login."); AddCommand("Attempts", static_cast(&CFailToBanMod::OnAttemptsCommand), "()", "The number of allowed failed login attempts."); + AddCommand("Ban", static_cast(&CFailToBanMod::OnBanCommand), "", "Ban the specified hosts."); + AddCommand("Unban", static_cast(&CFailToBanMod::OnUnbanCommand), "", "Unban the specified hosts."); + AddCommand("List", static_cast(&CFailToBanMod::OnListCommand), "", "List banned hosts."); } virtual ~CFailToBanMod() {} @@ -58,6 +61,10 @@ public: m_Cache.AddItem(sHost, count, m_Cache.GetTTL()); } + bool Remove(const CString& sHost) { + return m_Cache.RemItem(sHost); + } + void OnTimeoutCommand(const CString& sCommand) { CString sArg = sCommand.Token(1); @@ -90,6 +97,63 @@ public: } } + void OnBanCommand(const CString& sCommand) { + CString sHosts = sCommand.Token(1, true); + + if (sHosts.empty()) { + PutStatus("Usage: Ban "); + return; + } + + VCString vsHosts; + sHosts.Replace(",", " "); + sHosts.Split(" ", vsHosts, false, "", "", true, true); + + for (const CString& sHost : vsHosts) { + Add(sHost, 0); + PutModule("Banned: " + sHost); + } + } + + void OnUnbanCommand(const CString& sCommand) { + CString sHosts = sCommand.Token(1, true); + + if (sHosts.empty()) { + PutStatus("Usage: Unban "); + return; + } + + VCString vsHosts; + sHosts.Replace(",", " "); + sHosts.Split(" ", vsHosts, false, "", "", true, true); + + for (const CString& sHost : vsHosts) { + if (Remove(sHost)) { + PutModule("Unbanned: " + sHost); + } else { + PutModule("Ignored: " + sHost); + } + } + } + + void OnListCommand(const CString& sCommand) { + CTable Table; + Table.AddColumn("Host"); + Table.AddColumn("Attempts"); + + for (const auto& it : m_Cache.GetItems()) { + Table.AddRow(); + Table.SetCell("Host", it.first); + Table.SetCell("Attempts", CString(it.second)); + } + + if (Table.empty()) { + PutModule("No bans"); + } else { + PutModule(Table); + } + } + void OnClientConnect(CZNCSock* pClient, const CString& sHost, unsigned short uPort) override { unsigned int *pCount = m_Cache.GetItem(sHost); if (sHost.empty() || pCount == nullptr || *pCount < m_uiAllowedFailed) {