Все любят 443-й порт. А некоторые еще и 80-й. Короче таких любителей много, а порт один и повесить все сервисы на один порт невозможно. Ну вообще возможно все. Если захотеть. И использовать правильные инструменты.
Есть у меня Raspberry Pi на котором крутились разные сервисы и до поры не мешали друг-другу. 80-й и 443-й порты занимал zwift-offline. Я понимаю по какой причине они захардкодили эти порты, но не понимаю почему они не оставили возможности их изменения для работы через прокси. Но мы это починим и довольно скоро. Так вот, жил у меня весь этот зоопарк пока не появилась необходимость поднять еще один подобный сервис, который тоже хочет 443-й порт, да еще и со своим сертификатом. Ну в общем требования такие же как у zwift-offline
. Осталось придумать как все это засунуть в бедную Raspberry Pi и заставить работать.
Чиним Zwift
Нужно добавить отсутствующую возможность запуска сервиса на любых портах отличных от 80 и 443. Для этого нужно применить такой патч к существующему коду:
Subject: [PATCH] Add port customization feature
---
Index: standalone.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/standalone.py b/standalone.py
--- a/standalone.py (revision 184d07534a7e9740873b8d35c9455419ba0ef456)
+++ b/standalone.py (date 1732480060765)
@@ -781,7 +781,7 @@
bot.start()
socketserver.ThreadingTCPServer.allow_reuse_address = True
-httpd = socketserver.ThreadingTCPServer(('', 80), CDNHandler)
+httpd = socketserver.ThreadingTCPServer(('', os.environ.get('ZWIFT_HTTP_PORT', 80)), CDNHandler)
zoffline_thread = threading.Thread(target=httpd.serve_forever)
zoffline_thread.daemon = True
zoffline_thread.start()
Index: zwift_offline.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/zwift_offline.py b/zwift_offline.py
--- a/zwift_offline.py (revision 184d07534a7e9740873b8d35c9455419ba0ef456)
+++ b/zwift_offline.py (date 1732480069047)
@@ -4067,7 +4067,7 @@
send_message_thread = threading.Thread(target=send_server_back_online_message)
send_message_thread.start()
logger.info("Server version %s is running." % ZWIFT_VER_CUR)
- server = WSGIServer(('0.0.0.0', 443), app, certfile='%s/cert-zwift-com.pem' % SSL_DIR, keyfile='%s/key-zwift-com.pem' % SSL_DIR, log=logger)
+ server = WSGIServer(('0.0.0.0', os.environ.get('ZWIFT_HTTPS_PORT', 443)), app, certfile='%s/cert-zwift-com.pem' % SSL_DIR, keyfile='%s/key-zwift-com.pem' % SSL_DIR, log=logger)
server.serve_forever()
# app.run(ssl_context=('%s/cert-zwift-com.pem' % SSL_DIR, '%s/key-zwift-com.pem' % SSL_DIR), port=443, threaded=True, host='0.0.0.0') # debug=True, use_reload=False)
Проверено на версии кода 184d075
Настройка проксирования
В качестве прокси я взял HAProxy, поэтому для начала установим его на RPi:
sudo apt install haproxy
Теперь нужно поднять проксю, которая будет разводить трафик по разным сервисам.
Главным требованием к проксику у меня было наличие SSL passthrough
чтобы не жонглировать сертификатами. Да и в целом терминирование TLS мне не требовалось, потому что сервисы и так ожидают TLS. В результате некоторого количества проб и ошибок получился вот такой конфиг:
global
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
defaults
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http
mode http
bind *:80
use_backend zoffline_http
frontend https
mode tcp
bind *:443
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl sni_ur_or_rly101_zwift_com req_ssl_sni -i us-or-rly101.zwift.com
acl sni_secure_zwift_com req_ssl_sni -i secure.zwift.com
acl sni_launcher_zwift_com req_ssl_sni -i launcher.zwift.com
acl sni_another_service req.ssl_sni -i another.site.com
use_backend zoffline_https if sni_ur_or_rly101_zwift_com
use_backend zoffline_https if sni_secure_zwift_com
use_backend zoffline_https if sni_launcher_zwift_com
use_backend another_backend if sni_another_service
backend zoffline_http
mode http
server internal_zoffline_http 127.0.0.1:65100 check
backend zoffline_https
mode tcp
server internal_zoffline_https 127.0.0.1:65101 check
backend another_backend
mode tcp
server internal_another_backend 127.0.0.1:31337 check
Конечно же использование такого конфига требует правок в /etc/hosts
на клиенте чтобы завернуть трафик на локальную машину при запросе доменов того же zwift или чего угодно другого.
В итоге zwift-offline был поднят на портах 65100
вместо 80
и 65101
вместо 443
, а другой секретный сервис на порту 31337
вместо 443
. И все это работает благодаря механизмам проксирования с поддержкой SSL passthrough
. Если хочется кастомизаций, то добро пожаловать в официальную документацию по настройке.