Option to log login failures to file

merge-requests/30/head
Bob Mottram 2021-06-09 16:19:30 +01:00
parent 685ed0c22e
commit 1929f97bf4
4 changed files with 50 additions and 13 deletions

22
auth.py
View File

@ -11,6 +11,7 @@ import hashlib
import binascii import binascii
import os import os
import secrets import secrets
import datetime
from utils import isSystemAccount from utils import isSystemAccount
from utils import hasUsersPath from utils import hasUsersPath
@ -206,7 +207,9 @@ def createPassword(length=10):
return ''.join((secrets.choice(validChars) for i in range(length))) return ''.join((secrets.choice(validChars) for i in range(length)))
def recordLoginFailure(ipAddress: str, countDict: {}, failTime: int) -> None: def recordLoginFailure(baseDir: str, ipAddress: str,
countDict: {}, failTime: int,
logToFile: bool) -> None:
"""Keeps ip addresses and the number of times login failures """Keeps ip addresses and the number of times login failures
occured for them in a dict occured for them in a dict
""" """
@ -226,8 +229,23 @@ def recordLoginFailure(ipAddress: str, countDict: {}, failTime: int) -> None:
} }
else: else:
countDict[ipAddress]['count'] += 1 countDict[ipAddress]['count'] += 1
countDict[ipAddress]['time'] = failTime
failCount = countDict[ipAddress]['count'] failCount = countDict[ipAddress]['count']
if failCount > 4: if failCount > 4:
print('WARN: ' + str(ipAddress) + ' failed to log in ' + print('WARN: ' + str(ipAddress) + ' failed to log in ' +
str(failCount) + ' times') str(failCount) + ' times')
countDict[ipAddress]['time'] = failTime
if not logToFile:
return
failureLog = baseDir + '/accounts/loginfailures.log'
writeType = 'a+'
if not os.path.isfile(failureLog):
writeType = 'w+'
currTime = datetime.datetime.utcnow()
try:
with open(failureLog, writeType) as fp:
fp.write(currTime.strftime("%Y-%m-%d %H:%M:%SZ") +
' ' + ipAddress + '\n')
except BaseException:
pass

View File

@ -1443,8 +1443,9 @@ class PubServer(BaseHTTPRequestHandler):
ipAddress = self.headers['X-Forwarded-For'] ipAddress = self.headers['X-Forwarded-For']
else: else:
ipAddress = self.client_address[0] ipAddress = self.client_address[0]
if not isLocalNetworkAddress(ipAddress): if not domain.endswith('.onion'):
print('Login attempt from IP: ' + str(ipAddress)) if not isLocalNetworkAddress(ipAddress):
print('Login attempt from IP: ' + str(ipAddress))
if not authorizeBasic(baseDir, '/users/' + if not authorizeBasic(baseDir, '/users/' +
loginNickname + '/outbox', loginNickname + '/outbox',
authHeader, False): authHeader, False):
@ -1452,10 +1453,12 @@ class PubServer(BaseHTTPRequestHandler):
self._clearLoginDetails(loginNickname, callingDomain) self._clearLoginDetails(loginNickname, callingDomain)
failTime = int(time.time()) failTime = int(time.time())
self.server.lastLoginFailure = failTime self.server.lastLoginFailure = failTime
if not isLocalNetworkAddress(ipAddress): if not domain.endswith('.onion'):
recordLoginFailure(ipAddress, if not isLocalNetworkAddress(ipAddress):
self.server.loginFailureCount, recordLoginFailure(baseDir, ipAddress,
failTime) self.server.loginFailureCount,
failTime,
self.server.logLoginFailures)
self.server.POSTbusy = False self.server.POSTbusy = False
return return
else: else:
@ -14844,7 +14847,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
break break
def runDaemon(city: str, def runDaemon(logLoginFailures: bool,
city: str,
showNodeInfoAccounts: bool, showNodeInfoAccounts: bool,
showNodeInfoVersion: bool, showNodeInfoVersion: bool,
brochMode: bool, brochMode: bool,
@ -15113,6 +15117,7 @@ def runDaemon(city: str,
httpd.lastLoginTime = 0 httpd.lastLoginTime = 0
httpd.lastLoginFailure = 0 httpd.lastLoginFailure = 0
httpd.loginFailureCount = {} httpd.loginFailureCount = {}
httpd.logLoginFailures = logLoginFailures
httpd.maxReplies = maxReplies httpd.maxReplies = maxReplies
httpd.tokens = {} httpd.tokens = {}
httpd.tokensLookup = {} httpd.tokensLookup = {}

View File

@ -291,6 +291,11 @@ parser.add_argument("--iconsAsButtons",
type=str2bool, nargs='?', type=str2bool, nargs='?',
const=True, default=False, const=True, default=False,
help="Show header icons as buttons") help="Show header icons as buttons")
parser.add_argument("--logLoginFailures",
dest='logLoginFailures',
type=str2bool, nargs='?',
const=True, default=False,
help="Whether to log longin failures")
parser.add_argument("--rssIconAtTop", parser.add_argument("--rssIconAtTop",
dest='rssIconAtTop', dest='rssIconAtTop',
type=str2bool, nargs='?', type=str2bool, nargs='?',
@ -2510,6 +2515,11 @@ brochMode = \
if brochMode is not None: if brochMode is not None:
args.brochMode = bool(brochMode) args.brochMode = bool(brochMode)
logLoginFailures = \
getConfigParam(baseDir, 'logLoginFailures')
if logLoginFailures is not None:
args.logLoginFailures = bool(logLoginFailures)
showNodeInfoAccounts = \ showNodeInfoAccounts = \
getConfigParam(baseDir, 'showNodeInfoAccounts') getConfigParam(baseDir, 'showNodeInfoAccounts')
if showNodeInfoAccounts is not None: if showNodeInfoAccounts is not None:
@ -2539,7 +2549,8 @@ if setTheme(baseDir, themeName, domain,
print('Theme set to ' + themeName) print('Theme set to ' + themeName)
if __name__ == "__main__": if __name__ == "__main__":
runDaemon(args.city, runDaemon(args.logLoginFailures,
args.city,
args.showNodeInfoAccounts, args.showNodeInfoAccounts,
args.showNodeInfoVersion, args.showNodeInfoVersion,
args.brochMode, args.brochMode,

View File

@ -518,8 +518,9 @@ def createServerAlice(path: str, domain: str, port: int,
showNodeInfoAccounts = True showNodeInfoAccounts = True
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False
print('Server running: Alice') print('Server running: Alice')
runDaemon(city, runDaemon(logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
brochMode, brochMode,
@ -620,8 +621,9 @@ def createServerBob(path: str, domain: str, port: int,
showNodeInfoAccounts = True showNodeInfoAccounts = True
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False
print('Server running: Bob') print('Server running: Bob')
runDaemon(city, runDaemon(logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
brochMode, brochMode,
@ -677,8 +679,9 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
showNodeInfoAccounts = True showNodeInfoAccounts = True
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False
print('Server running: Eve') print('Server running: Eve')
runDaemon(city, runDaemon(logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
brochMode, brochMode,