ftp: ban password-bruteforcing IPs

This commit is contained in:
ed 2022-11-20 11:06:07 +00:00
parent ab655a56af
commit d326ba9723
3 changed files with 41 additions and 11 deletions

View file

@ -15,7 +15,7 @@ from pyftpdlib.servers import FTPServer
from .__init__ import PY2, TYPE_CHECKING, E from .__init__ import PY2, TYPE_CHECKING, E
from .bos import bos from .bos import bos
from .util import Daemon, Pebkac, exclude_dotfiles, fsenc from .util import Daemon, Pebkac, exclude_dotfiles, fsenc, ipnorm
try: try:
from pyftpdlib.ioloop import IOLoop from pyftpdlib.ioloop import IOLoop
@ -42,21 +42,41 @@ class FtpAuth(DummyAuthorizer):
def validate_authentication( def validate_authentication(
self, username: str, password: str, handler: Any self, username: str, password: str, handler: Any
) -> None: ) -> None:
handler.username = "{}:{}".format(username, password)
ip = handler.addr[0]
if ip.startswith("::ffff:"):
ip = ip[7:]
ip = ipnorm(ip)
bans = self.hub.bans
if ip in bans:
rt = bans[ip] - time.time()
if rt < 0:
logging.info("client unbanned")
del bans[ip]
else:
raise AuthenticationFailed("banned")
asrv = self.hub.asrv asrv = self.hub.asrv
if username == "anonymous": if username == "anonymous":
password = "" uname = "*"
else:
creds = password or username
uname = asrv.iacct.get(creds, "") if creds else "*"
uname = "*" if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
if password: g = self.hub.gpwd
uname = asrv.iacct.get(password, "") if g.lim:
bonk, ip = g.bonk(ip, handler.username)
if bonk:
logging.warning("client banned: invalid passwords")
bans[ip] = bonk
raise AuthenticationFailed("Authentication failed.")
handler.username = uname handler.username = uname
if (password and not uname) or not (
asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)
):
raise AuthenticationFailed("Authentication failed.")
def get_home_dir(self, username: str) -> str: def get_home_dir(self, username: str) -> str:
return "/" return "/"

View file

@ -35,6 +35,7 @@ from .up2k import Up2k
from .util import ( from .util import (
VERSIONS, VERSIONS,
Daemon, Daemon,
Garda,
HLog, HLog,
HMaccas, HMaccas,
alltrace, alltrace,
@ -85,6 +86,11 @@ class SvcHub(object):
self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8) self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
# for non-http clients (ftp)
self.bans: dict[str, int] = {}
self.gpwd = Garda(self.args.ban_pw)
self.g404 = Garda(self.args.ban_404)
if args.sss or args.s >= 3: if args.sss or args.s >= 3:
args.ss = True args.ss = True
args.no_dav = True args.no_dav = True

View file

@ -412,8 +412,12 @@ class HLog(logging.Handler):
if record.name == "pyftpdlib": if record.name == "pyftpdlib":
m = self.ptn_ftp.match(msg) m = self.ptn_ftp.match(msg)
if m: if m:
record.name = ip = m.group(1) ip = m.group(1)
msg = msg[len(ip) + 1 :] msg = msg[len(ip) + 1 :]
if ip.startswith("::ffff:"):
record.name = ip[7:]
else:
record.name = ip
elif record.name.startswith("impacket"): elif record.name.startswith("impacket"):
if self.ptn_smb_ign.match(msg): if self.ptn_smb_ign.match(msg):
return return