Add support for socks connection to Git backend

This commit is contained in:
Arthur Hanson 2024-09-27 16:05:26 -07:00
parent 85396866bc
commit 4198f77855

View File

@ -4,6 +4,8 @@ import re
import tempfile import tempfile
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from urllib3 import PoolManager, HTTPConnectionPool, HTTPSConnectionPool
from urllib3.connection import HTTPConnection, HTTPSConnection
from urllib.parse import urlparse from urllib.parse import urlparse
from django import forms from django import forms
@ -23,6 +25,81 @@ __all__ = (
logger = logging.getLogger('netbox.data_backends') logger = logging.getLogger('netbox.data_backends')
# These Proxy Methods are for handling socks connection proxy
class ProxyHTTPConnection(HTTPConnection):
use_rdns = False
def __init__(self, *args, **kwargs):
socks_options = kwargs.pop('_socks_options')
self._proxy_url = socks_options['proxy_url']
super().__init__(*args, **kwargs)
def _new_conn(self):
from python_socks.sync import Proxy
proxy = Proxy.from_url(self._proxy_url, rdns=self.use_rdns)
return proxy.connect(
dest_host=self.host,
dest_port=self.port,
timeout=self.timeout
)
class ProxyHTTPSConnection(ProxyHTTPConnection, HTTPSConnection):
pass
class rdnsProxyHTTPConnection(ProxyHTTPConnection):
use_rdns = True
class rdnsProxyHTTPSConnection(ProxyHTTPSConnection):
use_rdns = True
class ProxyHTTPConnectionPool(HTTPConnectionPool):
ConnectionCls = ProxyHTTPConnection
class ProxyHTTPSConnectionPool(HTTPSConnectionPool):
ConnectionCls = ProxyHTTPSConnection
class rdnsProxyHTTPConnectionPool(HTTPConnectionPool):
ConnectionCls = rdnsProxyHTTPConnection
class rdnsProxyHTTPSConnectionPool(HTTPSConnectionPool):
ConnectionCls = rdnsProxyHTTPSConnection
class ProxyPoolManager(PoolManager):
def __init__(self, proxy_url, timeout=5, num_pools=10, headers=None,
**connection_pool_kw):
# python_socks uses rdns param to denote remote DNS parsing and
# doesn't accept the 'h' or 'a' in the proxy URL
cleaned_proxy_url = proxy_url
if use_rdns := cleaned_proxy_url.startswith(('socks5h:', 'socks5a:')):
cleaned_proxy_url = cleaned_proxy_url.replace('socks5h:', 'socks5:')
cleaned_proxy_url = cleaned_proxy_url.replace('socks5a:', 'socks5:')
connection_pool_kw['_socks_options'] = {'proxy_url': cleaned_proxy_url}
connection_pool_kw['timeout'] = timeout
super().__init__(num_pools, headers, **connection_pool_kw)
if use_rdns:
self.pool_classes_by_scheme = {
'http': rdnsProxyHTTPConnectionPool,
'https': rdnsProxyHTTPSConnectionPool,
}
else:
self.pool_classes_by_scheme = {
'http': ProxyHTTPConnectionPool,
'https': ProxyHTTPSConnectionPool,
}
@register_data_backend() @register_data_backend()
class LocalBackend(DataBackend): class LocalBackend(DataBackend):
name = 'local' name = 'local'
@ -67,11 +144,14 @@ class GitBackend(DataBackend):
# Initialize backend config # Initialize backend config
config = ConfigDict() config = ConfigDict()
self.use_socks = False
# Apply HTTP proxy (if configured) # Apply HTTP proxy (if configured)
if settings.HTTP_PROXIES and self.url_scheme in ('http', 'https'): if settings.HTTP_PROXIES and self.url_scheme in ('http', 'https'):
if proxy := settings.HTTP_PROXIES.get(self.url_scheme): if proxy := settings.HTTP_PROXIES.get(self.url_scheme):
config.set("http", "proxy", proxy) config.set("http", "proxy", proxy)
if proxy.startswith('socks'):
self.use_socks = True
return config return config
@ -87,6 +167,10 @@ class GitBackend(DataBackend):
"errstream": porcelain.NoneStream(), "errstream": porcelain.NoneStream(),
} }
# check if using socks for proxy - if so need to use custom pool_manager
if self.use_socks:
clone_args['pool_manager'] = ProxyPoolManager(settings.HTTP_PROXIES.get(self.url_scheme))
if self.url_scheme in ('http', 'https'): if self.url_scheme in ('http', 'https'):
if self.params.get('username'): if self.params.get('username'):
clone_args.update( clone_args.update(