Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HAPROXY invented - PROXY V1 protocol support, to pass original IP address when behind HAPROXY/NGINX #227

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions snitun/server/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,39 @@ async def stop(self) -> None:
self._server.close()
await self._server.wait_closed()

def parse_proxy(self, data: bytes):
# PROXY protocol supported by HAPROXY and NGINX
# https://www.haproxy.org/download/2.8/doc/proxy-protocol.txt
# http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_protocol
if data.startswith(b'PROXY'):
maxlen = 108
proxy_end = data.index(b'\r\n')
if not proxy_end:
_LOGGER.warning('Bad PROXY header, no cr/lf')
return data, None
if proxy_end > maxlen:
_LOGGER.warning(f'Bad PROXY data, length > {maxlen}')
return data, None
proxy_hello = data[:proxy_end]
_split_params = proxy_hello.split(b' ')
if len(_split_params) != 6:
_LOGGER.warning(f'Bad PROXY data content: %s', _split_params)
return data, None
proxy_params = {
'header_len': len(proxy_hello),
'hello': _split_params[0].decode(),
'ip_version': _split_params[1].decode(),
'src_ip': _split_params[2].decode(),
'dst_ip': _split_params[3].decode(),
'src_port': int(_split_params[4]),
'dst_port': int(_split_params[5])
}
_LOGGER.debug('PROXY v1 protocol detected: %s', proxy_params)
return data[proxy_end+2:], proxy_params
else:
_LOGGER.debug('No PROXY header')
return data, None

async def _handler(
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None:
Expand All @@ -127,6 +160,14 @@ async def _handler(
writer.close()
return

# Check if HAPROXY/NGINX is used
if data.startswith(b'PROXY'):
data, proxy_params = self.parse_proxy(data)
if not proxy_params:
_LOGGER.warning("PROXY header detected, but PROXY data have BAD content.")
writer.close()
return

# Select the correct handler for process data
if data[0] == 0x16:
self._loop.create_task(
Expand Down