mirror of
https://github.com/9001/copyparty.git
synced 2025-08-18 09:22:31 -06:00
ftp: ban password-bruteforcing IPs
This commit is contained in:
parent
ab655a56af
commit
d326ba9723
|
@ -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 "/"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue