diff --git a/config.go b/config.go index a06febd56..d423489d3 100644 --- a/config.go +++ b/config.go @@ -107,6 +107,7 @@ type Config struct { DisplayDimAfterSec int `json:"display_dim_after_sec"` DisplayOffAfterSec int `json:"display_off_after_sec"` TLSMode string `json:"tls_mode"` // options: "self-signed", "user-defined", "" + TLSEnforce bool `json:"tls_enforce"` UsbConfig *usbgadget.Config `json:"usb_config"` UsbDevices *usbgadget.Devices `json:"usb_devices"` NetworkConfig *types.NetworkConfig `json:"network_config"` @@ -188,6 +189,7 @@ func getDefaultConfig() Config { // This is the "Standard" jiggler option in the UI JigglerConfig: func() *JigglerConfig { c := defaultJigglerConfig; return &c }(), TLSMode: "", + TLSEnforce: false, UsbConfig: func() *usbgadget.Config { c := defaultUsbConfig; return &c }(), UsbDevices: func() *usbgadget.Devices { c := defaultUsbDevices; return &c }(), NetworkConfig: func() *types.NetworkConfig { diff --git a/go.mod b/go.mod index aac587993..18d8dbf7a 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/contrib v0.0.0-20250521004450-2b1292699c15 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect diff --git a/go.sum b/go.sum index 8d33b159a..47ba798c0 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,8 @@ github.com/gin-contrib/logger v1.2.6 h1:EPolruKUTzNXMVBD9LuAFQmRjTs7AH7yKGuXgYqr github.com/gin-contrib/logger v1.2.6/go.mod h1:7niPrd7F0Nscw/zvgz8RiGJxSdbKM2yfQNy8xCHcm64= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/contrib v0.0.0-20250521004450-2b1292699c15 h1:AoSudS8CW8Mc9rRf5sO1vBtNxr2Ok6TaAICjgg5oKUY= +github.com/gin-gonic/contrib v0.0.0-20250521004450-2b1292699c15/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-co-op/gocron/v2 v2.17.0 h1:e/oj6fcAM8vOOKZxv2Cgfmjo+s8AXC46po5ZPtaSea4= diff --git a/ui/localization/messages/da.json b/ui/localization/messages/da.json index d2064532f..b930b0d25 100644 --- a/ui/localization/messages/da.json +++ b/ui/localization/messages/da.json @@ -19,6 +19,13 @@ "access_description": "Administrer adgangskontrollen for enheden", "access_disable_protection": "Deaktiver beskyttelse", "access_enable_password": "Aktivér adgangskode", + "access_enforce_tls_label": "Håndhæve HTTPS (TLS)", + "access_enforce_tls_description": "Omdiriger alle HTTP-anmodninger til HTTPS", + "access_enforce_tls_warning_before": "Før du aktiverer denne funktion, skal du sikre dig, at du har:", + "access_enforce_tls_warning_confirm": "Jeg forstår, aktiver alligevel.", + "access_enforce_tls_warning_description": "ADVARSEL: Dette vil begrænse adgangen til webgrænsefladen til kun HTTPS.", + "access_enforce_tls_warning_https": "HTTPS-tilstand er aktiveret og fungerer.", + "access_enforce_tls_warning_title": "Vil du aktivere håndhæv HTTPS (TLS)?", "access_failed_deregister": "Kunne ikke afregistrere enhed: {error}", "access_failed_update_cloud_url": "Kunne ikke opdatere cloud-URL: {error}", "access_failed_update_tls": "Kunne ikke opdatere TLS-indstillinger: {error}", diff --git a/ui/localization/messages/de.json b/ui/localization/messages/de.json index 326baa165..54fe5c0ad 100644 --- a/ui/localization/messages/de.json +++ b/ui/localization/messages/de.json @@ -19,6 +19,13 @@ "access_description": "Verwalten Sie die Zugriffssteuerung Ihres Geräts", "access_disable_protection": "Schutz deaktivieren", "access_enable_password": "Kennwort aktivieren", + "access_enforce_tls_label": "HTTPS (TLS) erzwingen", + "access_enforce_tls_description": "Leiten Sie alle HTTP-Anfragen auf HTTPS um", + "access_enforce_tls_warning_before": "Bevor Sie diese Funktion aktivieren, stellen Sie sicher, dass Sie über Folgendes verfügen:", + "access_enforce_tls_warning_confirm": "Ich verstehe, aktivieren Sie es trotzdem.", + "access_enforce_tls_warning_description": "WARNUNG: Dadurch wird der Zugriff auf die Webschnittstelle nur auf HTTPS beschränkt.", + "access_enforce_tls_warning_https": "Der HTTPS-Modus ist aktiviert und funktioniert.", + "access_enforce_tls_warning_title": "HTTPS (TLS) erzwingen aktivieren?", "access_failed_deregister": "Abmeldung des Geräts fehlgeschlagen: {error}", "access_failed_update_cloud_url": "Fehler beim Aktualisieren der Cloud-URL: {error}", "access_failed_update_tls": "TLS-Einstellungen konnten nicht aktualisiert werden: {error}", diff --git a/ui/localization/messages/en.json b/ui/localization/messages/en.json index a36874d0c..7ec2d7e82 100644 --- a/ui/localization/messages/en.json +++ b/ui/localization/messages/en.json @@ -19,6 +19,13 @@ "access_description": "Manage the Access Control of the device", "access_disable_protection": "Disable Protection", "access_enable_password": "Enable Password", + "access_enforce_tls_label": "Enforce HTTPS (TLS)", + "access_enforce_tls_description": "Redirect all HTTP requests to HTTPS", + "access_enforce_tls_warning_before": "Before enabling this feature, make sure you have:", + "access_enforce_tls_warning_confirm": "I Understand, Enable Anyway.", + "access_enforce_tls_warning_description": "WARNING: This will restrict web interface access to HTTPS only.", + "access_enforce_tls_warning_https": "HTTPS mode is enabled and working.", + "access_enforce_tls_warning_title": "Enable Enforce HTTPS (TLS)?", "access_failed_deregister": "Failed to de-register device: {error}", "access_failed_update_cloud_url": "Failed to update cloud URL: {error}", "access_failed_update_tls": "Failed to update TLS settings: {error}", diff --git a/ui/localization/messages/es.json b/ui/localization/messages/es.json index cec167b1f..e6b9d98cc 100644 --- a/ui/localization/messages/es.json +++ b/ui/localization/messages/es.json @@ -19,6 +19,13 @@ "access_description": "Administre el control de acceso del dispositivo", "access_disable_protection": "Desactivar la protección", "access_enable_password": "Activar contraseña", + "access_enforce_tls_label": "Aplicar HTTPS (TLS)", + "access_enforce_tls_description": "Redirigir todas las solicitudes HTTP a HTTPS", + "access_enforce_tls_warning_before": "Antes de habilitar esta función, asegúrese de tener:", + "access_enforce_tls_warning_confirm": "Entiendo, habilítelo de todos modos.", + "access_enforce_tls_warning_description": "ADVERTENCIA: Esto restringirá el acceso a la interfaz web solo a HTTPS.", + "access_enforce_tls_warning_https": "El modo HTTPS está habilitado y funcionando.", + "access_enforce_tls_warning_title": "¿Habilitar aplicar HTTPS (TLS)?", "access_failed_deregister": "No se pudo cancelar el registro del dispositivo: {error}", "access_failed_update_cloud_url": "No se pudo actualizar la URL de la nube: {error}", "access_failed_update_tls": "No se pudo actualizar la configuración de TLS: {error}", diff --git a/ui/localization/messages/fr.json b/ui/localization/messages/fr.json index b86bc160b..7c011e697 100644 --- a/ui/localization/messages/fr.json +++ b/ui/localization/messages/fr.json @@ -19,6 +19,13 @@ "access_description": "Gérer le contrôle d'accès de l'appareil", "access_disable_protection": "Désactiver la protection", "access_enable_password": "Activer le mot de passe", + "access_enforce_tls_label": "Appliquer HTTPS (TLS)", + "access_enforce_tls_description": "Rediriger toutes les requêtes HTTP vers HTTPS", + "access_enforce_tls_warning_before": "Avant d'activer cette fonctionnalité, assurez-vous d'avoir :", + "access_enforce_tls_warning_confirm": "Je comprends, activez quand même.", + "access_enforce_tls_warning_description": "AVERTISSEMENT : cela limitera l'accès à l'interface Web au HTTPS uniquement.", + "access_enforce_tls_warning_https": "Le mode HTTPS est activé et fonctionne.", + "access_enforce_tls_warning_title": "Activer Appliquer HTTPS (TLS) ?", "access_failed_deregister": "Échec de la désinscription du périphérique : {error}", "access_failed_update_cloud_url": "Échec de la mise à jour de l'URL du cloud : {error}", "access_failed_update_tls": "Échec de la mise à jour des paramètres TLS : {error}", diff --git a/ui/localization/messages/it.json b/ui/localization/messages/it.json index 3fe77fedf..49a021147 100644 --- a/ui/localization/messages/it.json +++ b/ui/localization/messages/it.json @@ -19,6 +19,13 @@ "access_description": "Gestisci il controllo degli accessi del dispositivo", "access_disable_protection": "Disattiva la protezione", "access_enable_password": "Attiva password", + "access_enforce_tls_label": "Applica HTTPS (TLS)", + "access_enforce_tls_description": "Reindirizzare tutte le richieste HTTP su HTTPS", + "access_enforce_tls_warning_before": "Prima di abilitare questa funzione, assicurati di avere:", + "access_enforce_tls_warning_confirm": "Ho capito, attiva comunque.", + "access_enforce_tls_warning_description": "ATTENZIONE: ciò limiterà l'accesso all'interfaccia web solo a HTTPS.", + "access_enforce_tls_warning_https": "La modalità HTTPS è abilitata e funziona.", + "access_enforce_tls_warning_title": "Abilitare l'applicazione HTTPS (TLS)?", "access_failed_deregister": "Impossibile annullare la registrazione del dispositivo: {error}", "access_failed_update_cloud_url": "Impossibile aggiornare l'URL del cloud: {error}", "access_failed_update_tls": "Impossibile aggiornare le impostazioni TLS: {error}", diff --git a/ui/localization/messages/nb.json b/ui/localization/messages/nb.json index 59c8ea64c..c8e66faae 100644 --- a/ui/localization/messages/nb.json +++ b/ui/localization/messages/nb.json @@ -19,6 +19,13 @@ "access_description": "Administrer tilgang til enheten", "access_disable_protection": "Deaktiver beskyttelse", "access_enable_password": "Aktiver passord", + "access_enforce_tls_label": "Håndheve HTTPS (TLS)", + "access_enforce_tls_description": "Omdiriger alle HTTP-forespørsler til HTTPS", + "access_enforce_tls_warning_before": "Før du aktiverer denne funksjonen, sørg for at du har:", + "access_enforce_tls_warning_confirm": "Jeg forstår, aktiver uansett.", + "access_enforce_tls_warning_description": "ADVARSEL: Dette vil begrense tilgangen til nettgrensesnittet kun til HTTPS.", + "access_enforce_tls_warning_https": "HTTPS-modus er aktivert og fungerer.", + "access_enforce_tls_warning_title": "Vil du aktivere håndheve HTTPS (TLS)?", "access_failed_deregister": "Kunne ikke avregistrere enheten: {error}", "access_failed_update_cloud_url": "Kunne ikke oppdatere nettadressen til skyen: {error}", "access_failed_update_tls": "Kunne ikke oppdatere TLS-innstillingene: {error}", diff --git a/ui/localization/messages/sv.json b/ui/localization/messages/sv.json index 17c794f30..81268da0a 100644 --- a/ui/localization/messages/sv.json +++ b/ui/localization/messages/sv.json @@ -19,6 +19,13 @@ "access_description": "Hantera enhetens åtkomstkontroll", "access_disable_protection": "Inaktivera skydd", "access_enable_password": "Aktivera lösenord", + "access_enforce_tls_label": "Framtvinga HTTPS (TLS)", + "access_enforce_tls_description": "Omdirigera alla HTTP-förfrågningar till HTTPS", + "access_enforce_tls_warning_before": "Innan du aktiverar den här funktionen, se till att du har:", + "access_enforce_tls_warning_confirm": "Jag förstår, aktivera ändå.", + "access_enforce_tls_warning_description": "VARNING: Detta begränsar webbgränssnittsåtkomsten till endast HTTPS.", + "access_enforce_tls_warning_https": "HTTPS-läget är aktiverat och fungerar.", + "access_enforce_tls_warning_title": "Aktivera Enforce HTTPS (TLS)?", "access_failed_deregister": "Misslyckades med att avregistrera enheten: {error}", "access_failed_update_cloud_url": "Misslyckades med att uppdatera moln-URL: {error}", "access_failed_update_tls": "Misslyckades med att uppdatera TLS-inställningarna: {error}", diff --git a/ui/localization/messages/zh.json b/ui/localization/messages/zh.json index 9510b518a..e5efe7a29 100644 --- a/ui/localization/messages/zh.json +++ b/ui/localization/messages/zh.json @@ -19,6 +19,13 @@ "access_description": "管理设备的访问控制。", "access_disable_protection": "禁用保护", "access_enable_password": "启用密码", + "access_enforce_tls_label": "强制执行 HTTPS (TLS)", + "access_enforce_tls_description": "将所有 HTTP 请求重定向到 HTTPS", + "access_enforce_tls_warning_before": "在启用此功能之前,请确保您拥有:", + "access_enforce_tls_warning_confirm": "我明白了,无论如何启用。", + "access_enforce_tls_warning_description": "警告:这将限制 Web 界面仅访问 HTTPS。", + "access_enforce_tls_warning_https": "HTTPS 模式已启用并正在运行。", + "access_enforce_tls_warning_title": "启用强制 HTTPS (TLS)?", "access_failed_deregister": "注销设备失败:{error}", "access_failed_update_cloud_url": "更新云地址失败:{error}", "access_failed_update_tls": "更新 TLS 设置失败:{error}", diff --git a/ui/src/routes/devices.$id.settings.access._index.tsx b/ui/src/routes/devices.$id.settings.access._index.tsx index adce74c89..c2b46c712 100644 --- a/ui/src/routes/devices.$id.settings.access._index.tsx +++ b/ui/src/routes/devices.$id.settings.access._index.tsx @@ -4,6 +4,8 @@ import { ShieldCheckIcon } from "@heroicons/react/24/outline"; import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; +import { CheckboxWithLabel } from "@components/Checkbox"; +import { ConfirmDialog } from "@components/ConfirmDialog"; import { GridCard } from "@components/Card"; import { Button, LinkButton } from "@components/Button"; import { InputFieldWithLabel } from "@components/InputField"; @@ -24,6 +26,7 @@ import { CloudState } from "./adopt"; export interface TLSState { mode: "self-signed" | "custom" | "disabled"; + enforce: boolean; certificate?: string; privateKey?: string; } @@ -54,8 +57,10 @@ export default function SettingsAccessIndexRoute() { // Use a simple string identifier for the selected provider const [selectedProvider, setSelectedProvider] = useState("jetkvm"); const [tlsMode, setTlsMode] = useState("unknown"); + const [tlsEnforce, setTlsEnforce] = useState(false); const [tlsCert, setTlsCert] = useState(""); const [tlsKey, setTlsKey] = useState(""); + const [showTlsEnforceWarning, setShowTlsEnforceWarning] = useState(false); const getCloudState = useCallback(() => { send("getCloudState", {}, (resp: JsonRpcResponse) => { @@ -84,6 +89,7 @@ export default function SettingsAccessIndexRoute() { const tlsState = resp.result as TLSState; setTlsMode(tlsState.mode); + setTlsEnforce(tlsState.enforce); if (tlsState.certificate) setTlsCert(tlsState.certificate); if (tlsState.privateKey) setTlsKey(tlsState.privateKey); }); @@ -148,12 +154,13 @@ export default function SettingsAccessIndexRoute() { // Function to update TLS state - accepts a mode parameter const updateTlsState = useCallback( - (mode: string, cert?: string, key?: string) => { + (mode: string, enforce: boolean, cert?: string, key?: string) => { const state = { mode } as TLSState; if (cert && key) { state.certificate = cert; state.privateKey = key; } + state.enforce = mode === "disabled" ? false : enforce; send("setTLSState", { state }, (resp: JsonRpcResponse) => { if ("error" in resp) { @@ -172,10 +179,11 @@ export default function SettingsAccessIndexRoute() { // Handle TLS mode change const handleTlsModeChange = (value: string) => { setTlsMode(value); + setTlsEnforce(value === "disabled" ? false : tlsEnforce); // For "disabled" and "self-signed" modes, immediately apply the settings if (value !== "custom") { - updateTlsState(value); + updateTlsState(value, tlsEnforce); } }; @@ -187,9 +195,28 @@ export default function SettingsAccessIndexRoute() { setTlsKey(value); }; + const handleTlsEnforceChange = (value: boolean) => { + // If trying to enable Enforce TLS, show warning first + if (value) { + setShowTlsEnforceWarning(true); + } + else { + setTlsEnforce(value); + // If HTTPS Mode is off, instantly disable Enforce TLS + if (tlsMode === "disabled") { + updateTlsState(tlsMode, value); + } + } + }; + + const confirmTlsEnforceEnable = () => { + setTlsEnforce(true); + setShowTlsEnforceWarning(false); + }; + // Update the custom TLS settings button click handler const handleCustomTlsUpdate = () => { - updateTlsState(tlsMode, tlsCert, tlsKey); + updateTlsState(tlsMode, tlsEnforce, tlsCert, tlsKey); }; // Fetch device ID and cloud state on component mount @@ -233,36 +260,69 @@ export default function SettingsAccessIndexRoute() { /> - {tlsMode === "custom" && ( - - - handleTlsCertChange(e.target.value)} - /> - handleTlsKeyChange(e.target.value)} + {(tlsMode === "custom" || tlsMode == "self-signed") && ( + <> + +
+ handleTlsEnforceChange(e.target.checked)} + /> +
+ {tlsMode === "custom" && ( + <> + + handleTlsCertChange(e.target.value)} + /> + handleTlsKeyChange(e.target.value)} + /> + + )} +
+
+
+ { + setShowTlsEnforceWarning(false); + }} + title={m.access_enforce_tls_warning_title()} + description={ + <> +

{m.access_enforce_tls_warning_description()}

+

{m.access_enforce_tls_warning_before()}

+
    +
  • {m.access_enforce_tls_warning_https()}
  • +
+ + } + variant="warning" + confirmText={m.access_enforce_tls_warning_confirm()} + onConfirm={confirmTlsEnforceEnable} /> -
-
-
+ )}