Skip to content

Instantly share code, notes, and snippets.

@lucianosmori
Last active May 26, 2026 17:54
Show Gist options
  • Select an option

  • Save lucianosmori/2053d82dc94cdf4df3958386cae93d97 to your computer and use it in GitHub Desktop.

Select an option

Save lucianosmori/2053d82dc94cdf4df3958386cae93d97 to your computer and use it in GitHub Desktop.
TLS 1.1 reverse proxy for accessing legacy devices. just Python 3

Usage

python3 tls11_proxy.py <device-ip>:443 8443

Then open http://localhost:8443 in any browser.

Examples

# EP1501 on default HTTPS port
python3 tls11_proxy.py 192.168.1.100:443 8443

# Custom port
python3 tls11_proxy.py 10.0.1.50:8443 9000

How it works

macOS Python links against OpenSSL 3.6+ which disables TLS 1.1 by default. The script overrides this with set_ciphers('DEFAULT:@SECLEVEL=0') to force the connection through.

Your browser talks plain HTTP to localhost, the proxy handles the legacy TLS negotiation.

Alternative: Quick CLI check (no proxy needed)

/usr/bin/curl -k --tlsv1.1 --tls-max 1.1 https://<device-ip>/

Must use /usr/bin/curl (LibreSSL) homebrew curl won't work.

Homebrew OpenSSL/curl without @SECLEVEL=0 override wont work.

#!/usr/bin/env python3
"""Reverse proxy: plain HTTP locally -> TLS 1.1 to target device.
Usage: python3 tls11_proxy.py <target-host:port> [local-port]
"""
import http.server, ssl, urllib.request, sys, warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
TARGET = sys.argv[1] if len(sys.argv) > 1 else "localhost:19443"
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8080
def make_ctx():
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.minimum_version = ssl.TLSVersion.TLSv1_1
ctx.maximum_version = ssl.TLSVersion.TLSv1_1
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ctx.set_ciphers('DEFAULT:@SECLEVEL=0')
return ctx
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self): self._p("GET")
def do_POST(self): self._p("POST")
def do_PUT(self): self._p("PUT")
def do_HEAD(self): self._p("HEAD")
def do_DELETE(self): self._p("DELETE")
def _p(self, m):
url = f"https://{TARGET}{self.path}"
body = None
if m in ("POST","PUT"):
body = self.rfile.read(int(self.headers.get("Content-Length",0))) or None
req = urllib.request.Request(url, data=body, method=m)
for k,v in self.headers.items():
if k.lower() not in ("host","connection"): req.add_header(k,v)
try:
r = urllib.request.urlopen(req, context=make_ctx(), timeout=10)
self.send_response(r.status)
for k,v in r.headers.items():
if k.lower() != "transfer-encoding": self.send_header(k,v)
self.end_headers()
self.wfile.write(r.read())
except Exception as e:
self.send_response(502)
self.end_headers()
self.wfile.write(f"Proxy error: {e}".encode())
def log_message(self, fmt, *a): print(f"[proxy] {a[0]}")
print(f"TLS 1.1 Proxy: http://localhost:{PORT} -> https://{TARGET}")
http.server.HTTPServer(("127.0.0.1", PORT), H).serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment