mirror of
https://github.com/9001/copyparty.git
synced 2026-02-27 22:13:10 -07:00
Merge 86a50b1e43 into 7c60adc69c
This commit is contained in:
commit
5cd9de3225
|
|
@ -18,6 +18,15 @@
|
||||||
# (note: enable compression by adding .xz at the end)
|
# (note: enable compression by adding .xz at the end)
|
||||||
q, lo: $LOGS_DIRECTORY/%Y-%m%d.log
|
q, lo: $LOGS_DIRECTORY/%Y-%m%d.log
|
||||||
|
|
||||||
|
# url to check against for vulnerable versions (default disabled); setting this will enable automatic
|
||||||
|
# vulnerability checks. the notification, in case you are running a vulnerable version, is shown on the
|
||||||
|
# admin panel (/?h) and only for users with admin permissions. you can choose between the value given here,
|
||||||
|
# or alternatively use https://api.copyparty.eu/security-advisories, or your own custom endpoint
|
||||||
|
vc-url: https://api.github.com/repos/9001/copyparty/security-advisories
|
||||||
|
|
||||||
|
# how many seconds to wait between vulnerability checks; default is 86400 (= 1 day).
|
||||||
|
vc-interval: 86400
|
||||||
|
|
||||||
# p: 80,443,3923 # listen on 80/443 as well (requires CAP_NET_BIND_SERVICE)
|
# p: 80,443,3923 # listen on 80/443 as well (requires CAP_NET_BIND_SERVICE)
|
||||||
# i: 127.0.0.1 # only allow connections from localhost (reverse-proxies)
|
# i: 127.0.0.1 # only allow connections from localhost (reverse-proxies)
|
||||||
# ftp: 3921 # enable ftp server on port 3921
|
# ftp: 3921 # enable ftp server on port 3921
|
||||||
|
|
|
||||||
|
|
@ -1204,6 +1204,8 @@ def add_general(ap, nc, srvname):
|
||||||
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
||||||
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
||||||
ap2.add_argument("--versionb", action="store_true", help="show version and exit")
|
ap2.add_argument("--versionb", action="store_true", help="show version and exit")
|
||||||
|
ap2.add_argument("--vc-url", metavar="URL", type=u, default="", help="URL to check for vulnerable versions (default: empty/disabled)")
|
||||||
|
ap2.add_argument("--vc-interval", metavar="SEC", type=int, default=86400, help="how many seconds to wait between vulnerability checks (default: 86400)")
|
||||||
|
|
||||||
|
|
||||||
def add_qr(ap, tty):
|
def add_qr(ap, tty):
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,10 @@ class BrokerMp(object):
|
||||||
elif dest == "cb_httpsrv_up":
|
elif dest == "cb_httpsrv_up":
|
||||||
self.hub.cb_httpsrv_up()
|
self.hub.cb_httpsrv_up()
|
||||||
|
|
||||||
|
elif dest == "httpsrv.set_bad_ver":
|
||||||
|
for p in self.procs:
|
||||||
|
p.q_pend.put((0, dest, list(args)))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("what is " + str(dest))
|
raise Exception("what is " + str(dest))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ class BrokerThr(BrokerCli):
|
||||||
self.httpsrv.set_netdevs(args[0])
|
self.httpsrv.set_netdevs(args[0])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if dest == "httpsrv.set_bad_ver":
|
||||||
|
self.httpsrv.set_bad_ver(args[0])
|
||||||
|
return
|
||||||
|
|
||||||
# new ipc invoking managed service in hub
|
# new ipc invoking managed service in hub
|
||||||
obj = self.hub
|
obj = self.hub
|
||||||
for node in dest.split("."):
|
for node in dest.split("."):
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,8 @@ ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
|
||||||
BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
|
BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
|
||||||
BADXFF2 = ". Some copyparty features are now disabled as a safety measure.\n\n\n"
|
BADXFF2 = ". Some copyparty features are now disabled as a safety measure.\n\n\n"
|
||||||
BADXFP = ', or change the copyparty global-option "xf-proto" to another header-name to read this value from. Alternatively, if your reverseproxy is not able to provide a header similar to "X-Forwarded-Proto", then you must tell copyparty which protocol to assume; either "--xf-proto-fb=http" or "--xf-proto-fb=https"'
|
BADXFP = ', or change the copyparty global-option "xf-proto" to another header-name to read this value from. Alternatively, if your reverseproxy is not able to provide a header similar to "X-Forwarded-Proto", then you must tell copyparty which protocol to assume; either "--xf-proto-fb=http" or "--xf-proto-fb=https"'
|
||||||
BADXFFB = "<b>NOTE: serverlog has a message regarding your reverse-proxy config</b>"
|
BADXFFB = "<div class='box-warning'>NOTE: serverlog has a message regarding your reverse-proxy config</div>"
|
||||||
|
BADVER = "<div class='box-warning'>The version of copyparty currently active has a known vulnerability <a class='unbox' href='https://github.com/9001/copyparty/security'>(more info)</a> that has been fixed; please update to the latest version. This message is only visible to users with the admin (a or A) permission.</div>"
|
||||||
|
|
||||||
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
|
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
|
||||||
H_CONN_CLOSE = "Connection: Close"
|
H_CONN_CLOSE = "Connection: Close"
|
||||||
|
|
@ -5624,7 +5625,8 @@ class HttpCli(object):
|
||||||
no304=self.no304(),
|
no304=self.no304(),
|
||||||
k304vis=self.args.k304 > 0,
|
k304vis=self.args.k304 > 0,
|
||||||
no304vis=self.args.no304 > 0,
|
no304vis=self.args.no304 > 0,
|
||||||
msg=BADXFFB if hasattr(self, "bad_xff") else "",
|
msg=(BADXFFB if not hasattr(self, "bad_xff") else "")
|
||||||
|
+ (BADVER if self.conn.hsrv.bad_ver and self.can_admin else ""),
|
||||||
ver=S_VERSION if show_ver else "",
|
ver=S_VERSION if show_ver else "",
|
||||||
chpw=self.args.chpw and self.uname != "*",
|
chpw=self.args.chpw and self.uname != "*",
|
||||||
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ class HttpSrv(object):
|
||||||
socket.setdefaulttimeout(120)
|
socket.setdefaulttimeout(120)
|
||||||
|
|
||||||
self.t0 = time.time()
|
self.t0 = time.time()
|
||||||
|
self.bad_ver = False
|
||||||
nsuf = "-n{}-i{:x}".format(nid, os.getpid()) if nid else ""
|
nsuf = "-n{}-i{:x}".format(nid, os.getpid()) if nid else ""
|
||||||
self.magician = Magician()
|
self.magician = Magician()
|
||||||
self.nm = NetMap([], [])
|
self.nm = NetMap([], [])
|
||||||
|
|
@ -232,6 +233,9 @@ class HttpSrv(object):
|
||||||
self.th_cfg: dict[str, set[str]] = {}
|
self.th_cfg: dict[str, set[str]] = {}
|
||||||
Daemon(self.post_init, "hsrv-init2")
|
Daemon(self.post_init, "hsrv-init2")
|
||||||
|
|
||||||
|
def set_bad_ver(self, val: bool) -> None:
|
||||||
|
self.bad_ver = val
|
||||||
|
|
||||||
def post_init(self) -> None:
|
def post_init(self) -> None:
|
||||||
try:
|
try:
|
||||||
x = self.broker.ask("thumbsrv.getcfg")
|
x = self.broker.ask("thumbsrv.getcfg")
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals
|
||||||
import argparse
|
import argparse
|
||||||
import atexit
|
import atexit
|
||||||
import errno
|
import errno
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -27,6 +28,7 @@ if True: # pylint: disable=using-constant-test
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unicode
|
from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unicode
|
||||||
|
from .__version__ import S_VERSION
|
||||||
from .authsrv import BAD_CFG, AuthSrv, derive_args, n_du_who, n_ver_who
|
from .authsrv import BAD_CFG, AuthSrv, derive_args, n_du_who, n_ver_who
|
||||||
from .bos import bos
|
from .bos import bos
|
||||||
from .cert import ensure_cert
|
from .cert import ensure_cert
|
||||||
|
|
@ -75,6 +77,7 @@ from .util import (
|
||||||
mp,
|
mp,
|
||||||
odfusion,
|
odfusion,
|
||||||
pybin,
|
pybin,
|
||||||
|
read_utf8,
|
||||||
start_log_thrs,
|
start_log_thrs,
|
||||||
start_stackmon,
|
start_stackmon,
|
||||||
termsize,
|
termsize,
|
||||||
|
|
@ -95,12 +98,16 @@ if TYPE_CHECKING:
|
||||||
if PY2:
|
if PY2:
|
||||||
range = xrange # type: ignore
|
range = xrange # type: ignore
|
||||||
|
|
||||||
|
if PY2:
|
||||||
|
from urllib2 import Request, urlopen
|
||||||
|
else:
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
|
||||||
VER_IDP_DB = 1
|
VER_IDP_DB = 1
|
||||||
VER_SESSION_DB = 1
|
VER_SESSION_DB = 1
|
||||||
VER_SHARES_DB = 2
|
VER_SHARES_DB = 2
|
||||||
|
|
||||||
|
|
||||||
class SvcHub(object):
|
class SvcHub(object):
|
||||||
"""
|
"""
|
||||||
Hosts all services which cannot be parallelized due to reliance on monolithic resources.
|
Hosts all services which cannot be parallelized due to reliance on monolithic resources.
|
||||||
|
|
@ -1382,6 +1389,7 @@ class SvcHub(object):
|
||||||
Daemon(self.tcpsrv.netmon, "netmon")
|
Daemon(self.tcpsrv.netmon, "netmon")
|
||||||
|
|
||||||
Daemon(self.thr_httpsrv_up, "sig-hsrv-up2")
|
Daemon(self.thr_httpsrv_up, "sig-hsrv-up2")
|
||||||
|
Daemon(self.check_ver, "ver-chk")
|
||||||
|
|
||||||
sigs = [signal.SIGINT, signal.SIGTERM]
|
sigs = [signal.SIGINT, signal.SIGTERM]
|
||||||
if not ANYWIN:
|
if not ANYWIN:
|
||||||
|
|
@ -1778,3 +1786,76 @@ class SvcHub(object):
|
||||||
zb = gzip.compress(zb)
|
zb = gzip.compress(zb)
|
||||||
zs = ub64enc(zb).decode("ascii")
|
zs = ub64enc(zb).decode("ascii")
|
||||||
self.log("stacks", zs)
|
self.log("stacks", zs)
|
||||||
|
|
||||||
|
def parse_version(self, ver: str) -> tuple:
|
||||||
|
if not ver or not isinstance(ver, str):
|
||||||
|
return (0, 0, 0)
|
||||||
|
match = re.search(r'[\d.]+', ver)
|
||||||
|
if not match:
|
||||||
|
return (0, 0, 0)
|
||||||
|
parts = [int(x) for x in match.group(0).split(".")]
|
||||||
|
while len(parts) < 3:
|
||||||
|
parts.append(0)
|
||||||
|
return tuple(parts[:3])
|
||||||
|
|
||||||
|
def get_vuln_cache_path(self) -> str:
|
||||||
|
return os.path.join(self.E.cfg, "vuln_advisory.json")
|
||||||
|
|
||||||
|
def check_ver(self) -> None:
|
||||||
|
if not self.args.vc_url:
|
||||||
|
return
|
||||||
|
|
||||||
|
ver_cpp = self.parse_version(S_VERSION)
|
||||||
|
|
||||||
|
while not self.stopping:
|
||||||
|
fpath = self.get_vuln_cache_path()
|
||||||
|
data = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
mtime = os.path.getmtime(fpath)
|
||||||
|
if time.time() - mtime < self.args.vc_interval:
|
||||||
|
data = read_utf8(None, fpath, True)
|
||||||
|
except Exception as e:
|
||||||
|
self.log("ver-chk", "no cached vulnerability advisory found, fetching; {}".format(e))
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
try:
|
||||||
|
req = Request(self.args.vc_url)
|
||||||
|
with urlopen(req, timeout=30) as f:
|
||||||
|
data = f.read().decode("utf-8")
|
||||||
|
|
||||||
|
with open(fpath, "wb") as f:
|
||||||
|
f.write(data.encode("utf-8"))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log("ver-chk", "failed to fetch vulnerability advisory; {}".format(e))
|
||||||
|
|
||||||
|
if data:
|
||||||
|
try:
|
||||||
|
advisories = json.loads(data)
|
||||||
|
has_vuln = False
|
||||||
|
|
||||||
|
for adv in advisories:
|
||||||
|
for vuln in adv.get("vulnerabilities", []):
|
||||||
|
if vuln.get("package", {}).get("name") != "copyparty":
|
||||||
|
continue
|
||||||
|
|
||||||
|
patched_str = vuln.get("patched_versions")
|
||||||
|
if patched_str:
|
||||||
|
patched_ver = self.parse_version(patched_str)
|
||||||
|
if ver_cpp < patched_ver:
|
||||||
|
has_vuln = True
|
||||||
|
break
|
||||||
|
if has_vuln:
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_vuln:
|
||||||
|
self.broker.say("httpsrv.set_bad_ver", True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log("ver-chk", "failed to process vulnerability advisory; {}".format(e))
|
||||||
|
|
||||||
|
for _ in range(self.args.vc_interval):
|
||||||
|
if self.stopping:
|
||||||
|
return
|
||||||
|
time.sleep(1)
|
||||||
|
|
|
||||||
|
|
@ -257,3 +257,19 @@ html.bz {
|
||||||
html.bz .vols img {
|
html.bz .vols img {
|
||||||
filter: sepia(0.8) hue-rotate(180deg);
|
filter: sepia(0.8) hue-rotate(180deg);
|
||||||
}
|
}
|
||||||
|
.box-warning {
|
||||||
|
background: #804;
|
||||||
|
border-bottom: 1px solid #c28;
|
||||||
|
padding: .75em;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: .2em;
|
||||||
|
margin-top: 0em;
|
||||||
|
}
|
||||||
|
.box-warning + .box-warning {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
.unbox {
|
||||||
|
border: unset !important;
|
||||||
|
padding: unset !important;
|
||||||
|
margin: unset !important;
|
||||||
|
}
|
||||||
|
|
@ -58,6 +58,15 @@
|
||||||
# show versions and exit
|
# show versions and exit
|
||||||
version
|
version
|
||||||
|
|
||||||
|
# url to check against for vulnerable versions (default disabled); setting this will enable automatic
|
||||||
|
# vulnerability checks. the notification, in case you are running a vulnerable version, is shown on the
|
||||||
|
# admin panel (/?h) and only for users with admin permissions. you can choose between the value given here,
|
||||||
|
# or alternatively use https://api.copyparty.eu/security-advisories, or your own custom endpoint
|
||||||
|
vc-url: https://api.github.com/repos/9001/copyparty/security-advisories
|
||||||
|
|
||||||
|
# how many seconds to wait between vulnerability checks; default is 86400 (= 1 day).
|
||||||
|
vc-interval: 86400
|
||||||
|
|
||||||
###000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\
|
###000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\
|
||||||
###// qr options \\000000000000000000000000000000000000000000000000000000000000000000000000000\
|
###// qr options \\000000000000000000000000000000000000000000000000000000000000000000000000000\
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue