mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
cidr-based autologin
This commit is contained in:
parent
aba680b6c2
commit
b7f9bf5a28
17
README.md
17
README.md
|
@ -80,6 +80,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|||
* [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
|
||||
* [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
|
||||
* [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
|
||||
* [ip auth](#ip-auth) - autologin based on IP range (CIDR)
|
||||
* [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
|
||||
* [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
|
||||
* [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
|
||||
|
@ -1432,6 +1433,22 @@ redefine behavior with plugins ([examples](./bin/handlers/))
|
|||
replace 404 and 403 errors with something completely different (that's it for now)
|
||||
|
||||
|
||||
## ip auth
|
||||
|
||||
autologin based on IP range (CIDR) , using the global-option `--ipu`
|
||||
|
||||
for example, if everyone with an IP that starts with `192.168.123` should automatically log in as the user `spartacus`, then you can either specify `--ipu=192.168.123.0/24=spartacus` as a commandline option, or put this in a config file:
|
||||
|
||||
```yaml
|
||||
[global]
|
||||
ipu: 192.168.123.0/24=spartacus
|
||||
```
|
||||
|
||||
repeat the option to map additional subnets
|
||||
|
||||
**be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`
|
||||
|
||||
|
||||
## identity providers
|
||||
|
||||
replace copyparty passwords with oauth and such
|
||||
|
|
|
@ -1087,6 +1087,7 @@ def add_auth(ap):
|
|||
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
|
||||
ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
|
||||
ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
|
||||
|
||||
|
||||
def add_chpw(ap):
|
||||
|
|
|
@ -76,6 +76,7 @@ class FtpAuth(DummyAuthorizer):
|
|||
else:
|
||||
raise AuthenticationFailed("banned")
|
||||
|
||||
args = self.hub.args
|
||||
asrv = self.hub.asrv
|
||||
uname = "*"
|
||||
if username != "anonymous":
|
||||
|
@ -86,6 +87,9 @@ class FtpAuth(DummyAuthorizer):
|
|||
uname = zs
|
||||
break
|
||||
|
||||
if args.ipu and uname == "*":
|
||||
uname = args.ipu_iu[args.ipu_nm.map(ip)]
|
||||
|
||||
if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
|
||||
g = self.hub.gpwd
|
||||
if g.lim:
|
||||
|
|
|
@ -589,6 +589,9 @@ class HttpCli(object):
|
|||
or "*"
|
||||
)
|
||||
|
||||
if self.args.ipu and self.uname == "*":
|
||||
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
|
||||
|
||||
self.rvol = self.asrv.vfs.aread[self.uname]
|
||||
self.wvol = self.asrv.vfs.awrite[self.uname]
|
||||
self.avol = self.asrv.vfs.aadmin[self.uname]
|
||||
|
|
|
@ -59,6 +59,8 @@ class HttpConn(object):
|
|||
self.asrv: AuthSrv = hsrv.asrv # mypy404
|
||||
self.u2fh: Util.FHC = hsrv.u2fh # mypy404
|
||||
self.pipes: Util.CachedDict = hsrv.pipes # mypy404
|
||||
self.ipu_iu: Optional[dict[str, str]] = hsrv.ipu_iu
|
||||
self.ipu_nm: Optional[NetMap] = hsrv.ipu_nm
|
||||
self.ipa_nm: Optional[NetMap] = hsrv.ipa_nm
|
||||
self.xff_nm: Optional[NetMap] = hsrv.xff_nm
|
||||
self.xff_lan: NetMap = hsrv.xff_lan # type: ignore
|
||||
|
|
|
@ -69,6 +69,7 @@ from .util import (
|
|||
build_netmap,
|
||||
has_resource,
|
||||
ipnorm,
|
||||
load_ipu,
|
||||
load_resource,
|
||||
min_ex,
|
||||
shut_socket,
|
||||
|
@ -175,6 +176,11 @@ class HttpSrv(object):
|
|||
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
||||
self.prism = has_resource(self.E, "web/deps/prism.js.gz")
|
||||
|
||||
if self.args.ipu:
|
||||
self.ipu_iu, self.ipu_nm = load_ipu(self.log, self.args.ipu)
|
||||
else:
|
||||
self.ipu_iu = self.ipu_nm = None
|
||||
|
||||
self.ipa_nm = build_netmap(self.args.ipa)
|
||||
self.xff_nm = build_netmap(self.args.xff_src)
|
||||
self.xff_lan = build_netmap("lan")
|
||||
|
|
|
@ -60,6 +60,7 @@ from .util import (
|
|||
alltrace,
|
||||
ansi_re,
|
||||
build_netmap,
|
||||
load_ipu,
|
||||
min_ex,
|
||||
mp,
|
||||
odfusion,
|
||||
|
@ -221,6 +222,11 @@ class SvcHub(object):
|
|||
noch.update([x for x in zsl if x])
|
||||
args.chpw_no = noch
|
||||
|
||||
if args.ipu:
|
||||
iu, nm = load_ipu(self.log, args.ipu)
|
||||
setattr(args, "ipu_iu", iu)
|
||||
setattr(args, "ipu_nm", nm)
|
||||
|
||||
if not self.args.no_ses:
|
||||
self.setup_session_db()
|
||||
|
||||
|
|
|
@ -665,11 +665,15 @@ class HLog(logging.Handler):
|
|||
|
||||
|
||||
class NetMap(object):
|
||||
def __init__(self, ips: list[str], cidrs: list[str], keep_lo=False) -> None:
|
||||
def __init__(
|
||||
self, ips: list[str], cidrs: list[str], keep_lo=False, strict_cidr=False
|
||||
) -> None:
|
||||
"""
|
||||
ips: list of plain ipv4/ipv6 IPs, not cidr
|
||||
cidrs: list of cidr-notation IPs (ip/prefix)
|
||||
"""
|
||||
self.mutex = threading.Lock()
|
||||
|
||||
if "::" in ips:
|
||||
ips = [x for x in ips if x != "::"] + list(
|
||||
[x.split("/")[0] for x in cidrs if ":" in x]
|
||||
|
@ -696,7 +700,7 @@ class NetMap(object):
|
|||
bip = socket.inet_pton(fam, ip.split("/")[0])
|
||||
self.bip.append(bip)
|
||||
self.b2sip[bip] = ip.split("/")[0]
|
||||
self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, False)
|
||||
self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, strict_cidr)
|
||||
|
||||
self.bip.sort(reverse=True)
|
||||
|
||||
|
@ -707,8 +711,10 @@ class NetMap(object):
|
|||
try:
|
||||
return self.cache[ip]
|
||||
except:
|
||||
pass
|
||||
with self.mutex:
|
||||
return self._map(ip)
|
||||
|
||||
def _map(self, ip: str) -> str:
|
||||
v6 = ":" in ip
|
||||
ci = IPv6Address(ip) if v6 else IPv4Address(ip)
|
||||
bip = next((x for x in self.bip if ci in self.b2net[x]), None)
|
||||
|
@ -2678,6 +2684,31 @@ def build_netmap(csv: str):
|
|||
return NetMap(ips, cidrs, True)
|
||||
|
||||
|
||||
def load_ipu(log: "RootLogger", ipus: list[str]) -> tuple[dict[str, str], NetMap]:
|
||||
ip_u = {"": "*"}
|
||||
cidr_u = {}
|
||||
for ipu in ipus:
|
||||
try:
|
||||
cidr, uname = ipu.split("=")
|
||||
cip, csz = cidr.split("/")
|
||||
except:
|
||||
t = "\n invalid value %r for argument --ipu; must be CIDR=UNAME (192.168.0.0/16=amelia)"
|
||||
raise Exception(t % (ipu,))
|
||||
uname2 = cidr_u.get(cidr)
|
||||
if uname2 is not None:
|
||||
t = "\n invalid value %r for argument --ipu; cidr %s already mapped to %r"
|
||||
raise Exception(t % (ipu, cidr, uname2))
|
||||
cidr_u[cidr] = uname
|
||||
ip_u[cip] = uname
|
||||
try:
|
||||
nm = NetMap(["::"], list(cidr_u.keys()), True, True)
|
||||
except Exception as ex:
|
||||
t = "failed to translate --ipu into netmap, probably due to invalid config: %r"
|
||||
log("root", t % (ex,), 1)
|
||||
raise
|
||||
return ip_u, nm
|
||||
|
||||
|
||||
def yieldfile(fn: str, bufsz: int) -> Generator[bytes, None, None]:
|
||||
readsz = min(bufsz, 128 * 1024)
|
||||
with open(fsenc(fn), "rb", bufsz) as f:
|
||||
|
|
|
@ -128,7 +128,7 @@ class Cfg(Namespace):
|
|||
ex = "dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash plain_ip"
|
||||
ka.update(**{k: True for k in ex.split()})
|
||||
|
||||
ex = "ah_cli ah_gen css_browser hist js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua"
|
||||
ex = "ah_cli ah_gen css_browser hist ipu js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua"
|
||||
ka.update(**{k: None for k in ex.split()})
|
||||
|
||||
ex = "hash_mt safe_dedup srch_time u2abort u2j u2sz"
|
||||
|
|
Loading…
Reference in a new issue