Skip to content

Commit 8a73bc7

Browse files
pascal-duboisFrankkkkk
authored andcommitted
Implement Haproxy PROXY protocol v1 and v2
Signed-off-by: Frank Villaro-Dixon <frank.villaro@infomaniak.com>
1 parent 5793ee5 commit 8a73bc7

File tree

4 files changed

+554
-2
lines changed

4 files changed

+554
-2
lines changed

pyftpdlib/handlers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,7 @@ def __init__(self, conn, server, ioloop=None):
12341234
self.remote_ip = ""
12351235
self.remote_port = ""
12361236
self.started = time.time()
1237+
self.proxy_proto_obj = self.server.proxy_proto_obj
12371238

12381239
# private session attributes
12391240
self._last_response = ""
@@ -1294,6 +1295,15 @@ def __init__(self, conn, server, ioloop=None):
12941295
self.handle_error()
12951296
return
12961297
else:
1298+
if self.server.proxy_proto_enabled and self.proxy_proto_obj and \
1299+
(self.proxy_proto_obj.trusted or self.server.proxy_proto_allow_untrusted):
1300+
# Override the socket values if PROXY protocol data is
1301+
# available and comes from a trusted proxy
1302+
if self.proxy_proto_obj.remote_ip:
1303+
self.remote_ip = self.proxy_proto_obj.remote_ip
1304+
if self.proxy_proto_obj.remote_port:
1305+
self.remote_port = self.proxy_proto_obj.remote_port
1306+
12971307
self.log("FTP session opened (connect)")
12981308

12991309
# try to handle urgent data inline

pyftpdlib/ioloop.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def handle_accepted(self, sock, addr):
7676
from .log import debug
7777
from .log import is_logging_configured
7878
from .log import logger
79+
from .proxy_proto import ProxyProtocol
7980

8081

8182
timer = getattr(time, 'monotonic', time.time)
@@ -427,9 +428,9 @@ def unregister(self, fd):
427428
del self.socket_map[fd]
428429
except KeyError:
429430
debug("call: unregister(); fd was no longer in socket_map", self)
430-
for l in (self._r, self._w):
431+
for loop in (self._r, self._w):
431432
try:
432-
l.remove(fd)
433+
loop.remove(fd)
433434
except ValueError:
434435
pass
435436

@@ -977,8 +978,29 @@ def add_channel(self, map=None, events=None):
977978
class Acceptor(AsyncChat):
978979
"""Same as base AsyncChat and supposed to be used to
979980
accept new connections.
981+
982+
All relevant PROXY protocol information is stored in class attributes
983+
described below.
984+
985+
- (bool) proxy_proto_enabled:
986+
enable the use of PROXY protocol (defaults to False).
987+
988+
- (list) proxy_proto_trusted_nets:
989+
the IP networks of the proxies you want to trust. Use a /32 (or /128) network
990+
mask if you want to declare a single IP (defaults to []).
991+
992+
- (bool) proxy_proto_allow_untrusted:
993+
whether or not to parse untrusted proxies headers (defaults to True).
994+
995+
- (instance) proxy_proto_obj:
996+
the ProxyProtocol instance populated with header's information
980997
"""
981998

999+
proxy_proto_enabled = False
1000+
proxy_proto_trusted_nets = []
1001+
proxy_proto_allow_untrusted = True
1002+
proxy_proto_obj = None
1003+
9821004
def add_channel(self, map=None, events=None):
9831005
AsyncChat.add_channel(self, map=map, events=self.ioloop.READ)
9841006

@@ -1045,6 +1067,21 @@ def handle_accept(self):
10451067
debug("call: handle_accept(); accept() returned ECONNABORTED",
10461068
self)
10471069
else:
1070+
if self.proxy_proto_enabled:
1071+
# Retrieve a populated PROXY protocol object. If no exception
1072+
# is raised the returned object is considered valid
1073+
try:
1074+
ProxyProtocol.trusted_networks = self.proxy_proto_trusted_nets
1075+
ProxyProtocol.allow_untrusted = self.proxy_proto_allow_untrusted
1076+
self.proxy_proto_obj = ProxyProtocol.create(sock)
1077+
except Exception as e:
1078+
logger.error("proxy: {}".format(e))
1079+
return
1080+
1081+
if self.proxy_proto_obj.trusted or self.proxy_proto_allow_untrusted:
1082+
# Could result in a (None, None) tuple
1083+
addr = (self.proxy_proto_obj.remote_ip, self.proxy_proto_obj.remote_port)
1084+
10481085
# sometimes addr == None instead of (ip, port) (see issue 104)
10491086
if addr is not None:
10501087
self.handle_accepted(sock, addr)

0 commit comments

Comments
 (0)