feat(tls): HTTPS + wss:// для iot.kube5s.ru
- emqx-ws-ingress.yaml: TLS секция + cert-manager letsencrypt-prod, ssl-redirect=true - router.go: CORS Allow-Origin: http → https://iot.kube5s.ru - iot-console.html: дефолт MQTT брокера ws:// → wss:// - operator.yaml: v0.1.53 → v0.1.54 - crypto.subtle теперь работает (HTTPS страница)
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
# Изменено: 2026-04-04 (iot-console: добавлен path /console → sless-operator:9090)
|
# Изменено: 2026-04-04 (tls: добавлен TLS + cert-manager, wss://, https://)
|
||||||
# ВРЕМЕННЫЙ WORKAROUND: MQTT over WebSocket через порт 443.
|
# WORKAROUND: MQTT over WebSocket через порт 443.
|
||||||
# Причина: порт 1883 заблокирован NSX-T Edge firewall на уровне облака.
|
# Причина: порт 1883 заблокирован NSX-T Edge firewall на уровне облака.
|
||||||
# Решение: EMQX WebSocket listener (8083) проксируется через nginx-ingress.
|
# Решение: EMQX WebSocket listener (8083) проксируется через nginx-ingress с TLS termination.
|
||||||
#
|
#
|
||||||
# IoT устройство подключается: ws://iot.kube5s.ru/mqtt
|
# IoT устройство подключается: wss://iot.kube5s.ru/mqtt
|
||||||
# IoT Консоль (UI): http://iot.kube5s.ru/console
|
# IoT Консоль (UI): https://iot.kube5s.ru/console
|
||||||
#
|
#
|
||||||
# DNS A-запись: iot.kube5s.ru → 185.247.187.147 (создана через Nubes API, zoneUid=498096ee)
|
# DNS A-запись: iot.kube5s.ru → 185.247.187.147 (создана через Nubes API, zoneUid=498096ee)
|
||||||
|
# TLS: cert-manager + letsencrypt-prod, secret=iot-kube5s-ru-tls
|
||||||
#
|
#
|
||||||
# Когда DevOps откроет порт 1883 — этот файл можно удалить.
|
# Когда DevOps откроет порт 1883 — этот файл можно удалить.
|
||||||
---
|
---
|
||||||
@@ -32,14 +33,19 @@ metadata:
|
|||||||
namespace: sless
|
namespace: sless
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
# ssl-redirect=false: IoT устройства не умеют следовать 301 редиректам при WebSocket upgrade
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
# ssl-redirect=true: принудительно HTTPS для всего трафика на iot.kube5s.ru
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
# WebSocket: nginx-ingress автоматически добавляет Upgrade/Connection при proxy-http-version=1.1
|
# WebSocket: nginx-ingress автоматически добавляет Upgrade/Connection при proxy-http-version=1.1
|
||||||
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
||||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- iot.kube5s.ru
|
||||||
|
secretName: iot-kube5s-ru-tls
|
||||||
rules:
|
rules:
|
||||||
- host: iot.kube5s.ru
|
- host: iot.kube5s.ru
|
||||||
http:
|
http:
|
||||||
@@ -53,8 +59,8 @@ spec:
|
|||||||
port:
|
port:
|
||||||
number: 8083
|
number: 8083
|
||||||
# IoT Консоль (UI) — HTML SPA встроенный в бинарник sless-operator
|
# IoT Консоль (UI) — HTML SPA встроенный в бинарник sless-operator
|
||||||
# URL: http://iot.kube5s.ru/console
|
# URL: https://iot.kube5s.ru/console
|
||||||
# Почему здесь: UI работает по HTTP чтобы ws:// (без TLS) не вызывал mixed content
|
# TLS termination на Ingress → wss:// MQTT и https:// API работают без mixed content
|
||||||
- path: /console
|
- path: /console
|
||||||
pathType: Exact
|
pathType: Exact
|
||||||
backend:
|
backend:
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ spec:
|
|||||||
- name: operator
|
- name: operator
|
||||||
# При обновлении версии оператора — менять тег здесь (не latest!)
|
# При обновлении версии оператора — менять тег здесь (не latest!)
|
||||||
# v0.1.50 — добавлены IoT controller, IoT REST API, MQTT auth endpoint
|
# v0.1.50 — добавлены IoT controller, IoT REST API, MQTT auth endpoint
|
||||||
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.53
|
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.54
|
||||||
# Always — чтобы всегда тянуть по точному тегу (не кешировать старый)
|
# Always — чтобы всегда тянуть по точному тегу (не кешировать старый)
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Изменено: 2026-04-04 (iot-console: добавлен CORS middleware + /console маршрут)
|
// Изменено: 2026-04-04 (tls: CORS origin обновлён с http → https для iot.kube5s.ru)
|
||||||
// router.go — регистрация всех REST-маршрутов через gorilla/mux.
|
// router.go — регистрация всех REST-маршрутов через gorilla/mux.
|
||||||
// Все маршруты /v1/ защищены Bearer-токеном (middleware.Auth).
|
// Все маршруты /v1/ защищены Bearer-токеном (middleware.Auth).
|
||||||
// /console — публичный маршрут (статический HTML без auth).
|
// /console — публичный маршрут (статический HTML без auth).
|
||||||
// CORS включён для http://iot.kube5s.ru — там хостится IoT Консоль (UI).
|
// CORS включён для https://iot.kube5s.ru — там хостится IoT Консоль (UI).
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
||||||
@@ -16,12 +16,12 @@ import (
|
|||||||
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api/middleware"
|
"gitea-naeel.giteak8s.services.ngcloud.ru/naeel/sless/internal/api/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// corsMiddleware добавляет CORS-заголовки для IoT Консоли на http://iot.kube5s.ru.
|
// corsMiddleware добавляет CORS-заголовки для IoT Консоли на https://iot.kube5s.ru.
|
||||||
// Нужен потому что: консоль на http://iot.kube5s.ru, API на https://sless.kube5s.ru — разные origin.
|
// Нужен потому что: консоль на https://iot.kube5s.ru, API на https://sless.kube5s.ru — разные origin.
|
||||||
// Обрабатывает preflight OPTIONS запросы от браузера.
|
// Обрабатывает preflight OPTIONS запросы от браузера.
|
||||||
func corsMiddleware(next http.Handler) http.Handler {
|
func corsMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "http://iot.kube5s.ru")
|
w.Header().Set("Access-Control-Allow-Origin", "https://iot.kube5s.ru")
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
|
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
|
||||||
w.Header().Set("Access-Control-Max-Age", "600")
|
w.Header().Set("Access-Control-Max-Age", "600")
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ const S = {
|
|||||||
// ns вычисляется из токена автоматически (SHA256(sub) → "sless-{hex}"), не вводится вручную
|
// ns вычисляется из токена автоматически (SHA256(sub) → "sless-{hex}"), не вводится вручную
|
||||||
ns: localStorage.getItem('iot_ns') || '',
|
ns: localStorage.getItem('iot_ns') || '',
|
||||||
apiBase: localStorage.getItem('iot_api_base') || 'https://sless.kube5s.ru',
|
apiBase: localStorage.getItem('iot_api_base') || 'https://sless.kube5s.ru',
|
||||||
mqttBase: localStorage.getItem('iot_mqtt_base')|| 'ws://iot.kube5s.ru/mqtt',
|
mqttBase: localStorage.getItem('iot_mqtt_base')|| 'wss://iot.kube5s.ru/mqtt',
|
||||||
|
|
||||||
// Рабочие данные
|
// Рабочие данные
|
||||||
devices: [],
|
devices: [],
|
||||||
@@ -479,7 +479,7 @@ function renderLogin() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>MQTT Broker WebSocket</label>
|
<label>MQTT Broker WebSocket</label>
|
||||||
<input id="f-mqtt" value="${h(S.mqttBase)}" placeholder="ws://iot.kube5s.ru/mqtt">
|
<input id="f-mqtt" value="${h(S.mqttBase)}" placeholder="wss://iot.kube5s.ru/mqtt">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Bearer токен</label>
|
<label>Bearer токен</label>
|
||||||
@@ -522,7 +522,7 @@ async function doLogin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
S.apiBase = api || 'https://sless.kube5s.ru';
|
S.apiBase = api || 'https://sless.kube5s.ru';
|
||||||
S.mqttBase = mqtt || 'ws://iot.kube5s.ru/mqtt';
|
S.mqttBase = mqtt || 'wss://iot.kube5s.ru/mqtt';
|
||||||
S.ns = ns;
|
S.ns = ns;
|
||||||
S.token = tok;
|
S.token = tok;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user