add x-forwarded-proto fallback (closes #1110);

some reverseproxies do not include a compatible alternative to
x-forwarded-proto by default, while also lacking the option to
set custom headers

add --xf-proto-fb to set a fixed protocol to assume
This commit is contained in:
ed
2025-12-16 21:15:44 +00:00
parent 5e1d9a58d8
commit 9c64788d43
2 changed files with 10 additions and 5 deletions

View File

@@ -1278,6 +1278,7 @@ def add_network(ap):
ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from")
ap2.add_argument("--xf-host", metavar="NAME", type=u, default="x-forwarded-host", help="if reverse-proxied, which http header to read the correct Host value from; this header must contain the server's external domain name")
ap2.add_argument("--xf-proto", metavar="NAME", type=u, default="x-forwarded-proto", help="if reverse-proxied, which http header to read the correct protocol value from; this header must contain either 'http' or 'https'")
ap2.add_argument("--xf-proto-fb", metavar="T", type=u, default="", help="protocol to assume if the X-Forwarded-Proto header (\033[33m--xf-proto\033[0m) is not provided by the reverseproxy; either 'http' or 'https'")
ap2.add_argument("--xff-src", metavar="CIDR", type=u, default="127.0.0.0/8, ::1/128", help="list of trusted reverse-proxy CIDRs (comma-separated); only accept the real-ip header (\033[33m--xff-hdr\033[0m) and IdP headers if the incoming connection is from an IP within either of these subnets. Specify [\033[32mlan\033[0m] to allow all LAN / private / non-internet IPs. Can be disabled with [\033[32many\033[0m] if you are behind cloudflare (or similar) and are using \033[32m--xff-hdr=cf-connecting-ip\033[0m (or similar)")
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]\n └─for performance and security, this only looks at the TCP/Network-level IP, and will NOT work behind a reverseproxy")
ap2.add_argument("--ipar", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated).\n └─this is reverseproxy-compatible; reads client-IP from 'X-Forwarded-For' if possible, with TCP/Network IP as fallback")

View File

@@ -152,7 +152,8 @@ USED4SEC = {"usedforsecurity": False} if sys.version_info > (3, 9) else {}
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)"
BADXFF2 = ". Some copyparty features are now disabled as a safety measure."
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 by setting global-option --xf-proto-fb to either http or https'
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
H_CONN_CLOSE = "Connection: Close"
@@ -449,10 +450,13 @@ class HttpCli(object):
try:
self.is_https = len(self.headers[self.args.xf_proto]) == 5
except:
self.bad_xff = True
self.host = "example.com"
t = 'got proxied request without header "%s" (global-option "xf-proto"). This header must contain either "http" or "https". Either fix your reverse-proxy config to include this header, or change the copyparty global-option "xf-proto" to another header-name to read this value from'
self.log(t % (self.args.xf_proto,) + BADXFF2, 3)
if self.args.xf_proto_fb:
self.is_https = len(self.args.xf_proto_fb) == 5
else:
self.bad_xff = True
self.host = "example.com"
t = 'got proxied request without header "%s" (global-option "xf-proto"). This header must contain either "http" or "https". Either fix your reverse-proxy config to include this header%s%s'
self.log(t % (self.args.xf_proto, BADXFP, BADXFF2), 3)
# the semantics of trusted_xff and bad_xff are different;
# trusted_xff is whether the connection came from a trusted reverseproxy,