Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon

main
Bob Mottram 2021-06-20 19:19:12 +01:00
commit e6ca85c08e
4 changed files with 101 additions and 51 deletions

View File

@ -207,6 +207,7 @@ from shares import addShare
from shares import removeShare from shares import removeShare
from shares import expireShares from shares import expireShares
from categories import setHashtagCategory from categories import setHashtagCategory
from utils import userAgentDomain
from utils import isLocalNetworkAddress from utils import isLocalNetworkAddress
from utils import permittedDir from utils import permittedDir
from utils import isAccountDir from utils import isAccountDir
@ -452,37 +453,43 @@ class PubServer(BaseHTTPRequestHandler):
else: else:
print('ERROR: unable to create vote') print('ERROR: unable to create vote')
def _userAgentDomain(self) -> str: def _blockedUserAgent(self, callingDomain: str) -> bool:
"""Returns the domain specified within User-Agent header
"""
if not self.headers.get('User-Agent'):
return None
agentStr = self.headers.get('User-Agent')
if '+' not in agentStr:
return None
agentDomain = agentStr.split('+')[1].strip()
if '://' in agentDomain:
agentDomain = agentDomain.split('://')[1]
if '/' in agentDomain:
agentDomain = agentDomain.split('/')[0]
if ' ' in agentDomain:
agentDomain = agentDomain.replace(' ', '')
if ';' in agentDomain:
agentDomain = agentDomain.replace(';', '')
if '.' not in agentDomain:
return None
return agentDomain
def _blockedUserAgent(self) -> bool:
"""Should a GET or POST be blocked based upon its user agent? """Should a GET or POST be blocked based upon its user agent?
""" """
agentDomain = self._userAgentDomain() agentDomain = None
if not agentDomain: agentStr = None
if self.server.userAgentDomainRequired: if self.headers.get('User-Agent'):
agentStr = self.headers['User-Agent']
# is this a web crawler? If so the block it
agentStrLower = agentStr.lower()
if 'bot/' in agentStrLower or 'bot-' in agentStrLower:
print('Blocked Crawler: ' + agentStr)
return True return True
# get domain name from User-Agent
agentDomain = userAgentDomain(agentStr, self.server.debug)
else:
# no User-Agent header is present
return True
# is the User-Agent type blocked? eg. "Mastodon"
if self.server.userAgentsBlocked:
blockedUA = False
for agentName in self.server.userAgentsBlocked:
if agentName in agentStr:
blockedUA = True
break
if blockedUA:
return True
if not agentDomain:
return False return False
# is the User-Agent domain blocked
blockedUA = False
if not agentDomain.startswith(callingDomain):
blockedUA = isBlockedDomain(self.server.baseDir, agentDomain) blockedUA = isBlockedDomain(self.server.baseDir, agentDomain)
if blockedUA and self.server.debug: # if self.server.debug:
if blockedUA:
print('Blocked User agent: ' + agentDomain) print('Blocked User agent: ' + agentDomain)
return blockedUA return blockedUA
@ -10628,7 +10635,7 @@ class PubServer(BaseHTTPRequestHandler):
self._400() self._400()
return return
if self._blockedUserAgent(): if self._blockedUserAgent(callingDomain):
self._400() self._400()
return return
@ -14130,6 +14137,10 @@ class PubServer(BaseHTTPRequestHandler):
self._400() self._400()
return return
if self._blockedUserAgent(callingDomain):
self._400()
return
self.server.POSTbusy = True self.server.POSTbusy = True
if not self.headers.get('Content-type'): if not self.headers.get('Content-type'):
print('Content-type header missing') print('Content-type header missing')
@ -14881,7 +14892,7 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
break break
def runDaemon(userAgentDomainRequired: bool, def runDaemon(userAgentsBlocked: [],
logLoginFailures: bool, logLoginFailures: bool,
city: str, city: str,
showNodeInfoAccounts: bool, showNodeInfoAccounts: bool,
@ -15008,9 +15019,8 @@ def runDaemon(userAgentDomainRequired: bool,
httpd.keyShortcuts = {} httpd.keyShortcuts = {}
loadAccessKeysForAccounts(baseDir, httpd.keyShortcuts, httpd.accessKeys) loadAccessKeysForAccounts(baseDir, httpd.keyShortcuts, httpd.accessKeys)
# if set to True then the calling domain must be specified # list of blocked user agent types within the User-Agent header
# within the User-Agent header httpd.userAgentsBlocked = userAgentsBlocked
httpd.userAgentDomainRequired = userAgentDomainRequired
httpd.unitTest = unitTest httpd.unitTest = unitTest
httpd.allowLocalNetworkAccess = allowLocalNetworkAccess httpd.allowLocalNetworkAccess = allowLocalNetworkAccess

View File

@ -104,6 +104,9 @@ def str2bool(v) -> bool:
parser = argparse.ArgumentParser(description='ActivityPub Server') parser = argparse.ArgumentParser(description='ActivityPub Server')
parser.add_argument('--userAgentBlocks', type=str,
default=None,
help='List of blocked user agents, separated by commas')
parser.add_argument('-n', '--nickname', dest='nickname', type=str, parser.add_argument('-n', '--nickname', dest='nickname', type=str,
default=None, default=None,
help='Nickname of the account to use') help='Nickname of the account to use')
@ -274,12 +277,6 @@ parser.add_argument("--repliesEnabled", "--commentsEnabled",
type=str2bool, nargs='?', type=str2bool, nargs='?',
const=True, default=True, const=True, default=True,
help="Enable replies to a post") help="Enable replies to a post")
parser.add_argument("--userAgentDomainRequired",
dest='userAgentDomainRequired',
type=str2bool, nargs='?',
const=True, default=False,
help="Whether User-Agent header must " +
"contain the calling domain")
parser.add_argument("--showPublishAsIcon", parser.add_argument("--showPublishAsIcon",
dest='showPublishAsIcon', dest='showPublishAsIcon',
type=str2bool, nargs='?', type=str2bool, nargs='?',
@ -2522,10 +2519,17 @@ showNodeInfoVersion = \
if showNodeInfoVersion is not None: if showNodeInfoVersion is not None:
args.showNodeInfoVersion = bool(showNodeInfoVersion) args.showNodeInfoVersion = bool(showNodeInfoVersion)
userAgentDomainRequired = \ userAgentsBlocked = []
getConfigParam(baseDir, 'userAgentDomainRequired') if args.userAgentBlocks:
if userAgentDomainRequired is not None: userAgentsBlockedStr = args.userAgentBlocks
args.userAgentDomainRequired = bool(userAgentDomainRequired) setConfigParam(baseDir, 'userAgentsBlocked', userAgentsBlockedStr)
else:
userAgentsBlockedStr = \
getConfigParam(baseDir, 'userAgentsBlocked')
if userAgentsBlockedStr:
agentBlocksList = userAgentsBlockedStr.split(',')
for agentBlockStr in agentBlocksList:
userAgentsBlocked.append(agentBlockStr.strip())
city = \ city = \
getConfigParam(baseDir, 'city') getConfigParam(baseDir, 'city')
@ -2563,7 +2567,7 @@ if args.registration:
print('New registrations closed') print('New registrations closed')
if __name__ == "__main__": if __name__ == "__main__":
runDaemon(args.userAgentDomainRequired, runDaemon(userAgentsBlocked,
args.logLoginFailures, args.logLoginFailures,
args.city, args.city,
args.showNodeInfoAccounts, args.showNodeInfoAccounts,

View File

@ -37,13 +37,14 @@ from follow import clearFollows
from follow import clearFollowers from follow import clearFollowers
from follow import sendFollowRequestViaServer from follow import sendFollowRequestViaServer
from follow import sendUnfollowRequestViaServer from follow import sendUnfollowRequestViaServer
from siteactive import siteIsActive
from utils import userAgentDomain
from utils import camelCaseSplit from utils import camelCaseSplit
from utils import decodedHost from utils import decodedHost
from utils import getFullDomain from utils import getFullDomain
from utils import validNickname from utils import validNickname
from utils import firstParagraphFromString from utils import firstParagraphFromString
from utils import removeIdEnding from utils import removeIdEnding
from siteactive import siteIsActive
from utils import updateRecentPostsCache from utils import updateRecentPostsCache
from utils import followPerson from utils import followPerson
from utils import getNicknameFromActor from utils import getNicknameFromActor
@ -519,9 +520,9 @@ def createServerAlice(path: str, domain: str, port: int,
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False logLoginFailures = False
userAgentDomainRequired = False userAgentsBlocked = []
print('Server running: Alice') print('Server running: Alice')
runDaemon(userAgentDomainRequired, runDaemon(userAgentsBlocked,
logLoginFailures, city, logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
@ -624,9 +625,9 @@ def createServerBob(path: str, domain: str, port: int,
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False logLoginFailures = False
userAgentDomainRequired = False userAgentsBlocked = []
print('Server running: Bob') print('Server running: Bob')
runDaemon(userAgentDomainRequired, runDaemon(userAgentsBlocked,
logLoginFailures, city, logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
@ -684,9 +685,9 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
showNodeInfoVersion = True showNodeInfoVersion = True
city = 'London, England' city = 'London, England'
logLoginFailures = False logLoginFailures = False
userAgentDomainRequired = False userAgentsBlocked = []
print('Server running: Eve') print('Server running: Eve')
runDaemon(userAgentDomainRequired, runDaemon(userAgentsBlocked,
logLoginFailures, city, logLoginFailures, city,
showNodeInfoAccounts, showNodeInfoAccounts,
showNodeInfoVersion, showNodeInfoVersion,
@ -3938,10 +3939,21 @@ def _testRoles() -> None:
assert not actorHasRole(actorJson, "artist") assert not actorHasRole(actorJson, "artist")
def _testUserAgentDomain() -> None:
print('testUserAgentDomain')
userAgent = \
'http.rb/4.4.1 (Mastodon/9.10.11; +https://mastodon.something/)'
assert userAgentDomain(userAgent, False) == 'mastodon.something'
userAgent = \
'Mozilla/70.0 (X11; Linux x86_64; rv:1.0) Gecko/20450101 Firefox/1.0'
assert userAgentDomain(userAgent, False) is None
def runAllTests(): def runAllTests():
print('Running tests...') print('Running tests...')
updateDefaultThemesList(os.getcwd()) updateDefaultThemesList(os.getcwd())
_testFunctions() _testFunctions()
_testUserAgentDomain()
_testRoles() _testRoles()
_testSkills() _testSkills()
_testSpoofGeolocation() _testSpoofGeolocation()

View File

@ -2433,3 +2433,27 @@ def permittedDir(path: str) -> bool:
path.startswith('/accounts'): path.startswith('/accounts'):
return False return False
return True return True
def userAgentDomain(userAgent: str, debug: bool) -> str:
"""If the User-Agent string contains a domain
then return it
"""
if '+http' not in userAgent:
return None
agentDomain = userAgent.split('+http')[1].strip()
if '://' in agentDomain:
agentDomain = agentDomain.split('://')[1]
if '/' in agentDomain:
agentDomain = agentDomain.split('/')[0]
if ')' in agentDomain:
agentDomain = agentDomain.split(')')[0].strip()
if ' ' in agentDomain:
agentDomain = agentDomain.replace(' ', '')
if ';' in agentDomain:
agentDomain = agentDomain.replace(';', '')
if '.' not in agentDomain:
return None
if debug:
print('User-Agent Domain: ' + agentDomain)
return agentDomain