Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8199c46
Add option to enforce TLS by redirecting HTTP requests to HTTPS (#1092)
DigitalDJ Dec 23, 2025
08e74f9
Ensure TLS Enforce can never be on when HTTPS mode is disabled (#1092)
DigitalDJ Dec 24, 2025
ae8bddf
Fix nesting spacing for custom TLS certificates (#1092)
DigitalDJ Dec 24, 2025
cbd4490
Change flow of Enforce TLS enablement. Require pop-up acknolwedgement…
DigitalDJ Dec 24, 2025
7fbd475
Fix web secure server not fully shutting down due to waiting on stopT…
DigitalDJ Dec 24, 2025
f72a4de
Be more clear about setupRouter argument; don't pass config down, use…
DigitalDJ Dec 24, 2025
ce20043
Disable use of HSTS (#1092)
DigitalDJ Dec 24, 2025
897dde8
Delay handler swap when turning on TLS enforce to allow any pending W…
DigitalDJ Dec 24, 2025
779fcaa
Close update router channel on web server close (#1092)
DigitalDJ Dec 24, 2025
2cef0ea
Fix getTLSState check TLSMode string "disabled" - config uses empty s…
DigitalDJ Dec 24, 2025
19e29bd
Be more defensive about enforcing TLS. Ensure TLS is successfully ena…
DigitalDJ Dec 24, 2025
96276d4
Update translations to be more consistent for Enforce TLS. Change Enf…
DigitalDJ Dec 31, 2025
f882abc
Allow UI to be IFRAME'd when using HTTPS
DigitalDJ Dec 31, 2025
93e5697
Clarify use of isChanged and re-route HTTP server after we signal the…
DigitalDJ Dec 31, 2025
a700a0b
Merge branch 'dev' into feat/enforce-tls
DigitalDJ Dec 31, 2025
4919cd9
Fix hidden Update TLS Settings button when mode is self-signed
DigitalDJ Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "TLS afdwingen",
"access_enforce_tls_description": "Alle HTTP-verzoeken doorsturen naar HTTPS",
"access_enforce_tls_warning_before": "Før du aktiverer denne funktion, skal du sørge for at du har:",
"access_enforce_tls_warning_confirm": "Jeg forstår, aktivér alligevel",
"access_enforce_tls_warning_description": "ADVARSEL: Dette vil begrænse webgrænsefladeadgang til kun HTTPS.",
"access_enforce_tls_warning_https": "HTTPS-tilstand er aktiveret og fungerer.",
"access_enforce_tls_warning_title": "Aktivér håndhævelse af 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "TLS erzwingen",
"access_enforce_tls_description": "Alle HTTP-Anfragen auf HTTPS umleiten",
"access_enforce_tls_warning_before": "Bevor Sie diese Funktion aktivieren, stellen Sie sicher, dass Sie Folgendes haben:",
"access_enforce_tls_warning_confirm": "Ich verstehe, trotzdem aktivieren",
"access_enforce_tls_warning_description": "WARNUNG: Dadurch wird der Zugriff auf die Weboberfläche auf HTTPS beschränkt.",
"access_enforce_tls_warning_https": "Der HTTPS-Modus ist aktiviert und funktioniert.",
"access_enforce_tls_warning_title": "TLS erzwingen?",
"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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 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, habilitar de todas formas",
"access_enforce_tls_warning_description": "ADVERTENCIA: Esto restringirá el acceso a la interfaz web a HTTPS únicamente.",
"access_enforce_tls_warning_https": "El modo HTTPS está habilitado y funcionando.",
"access_enforce_tls_warning_title": "¿Habilitar Aplicar 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 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, j'active quand même",
"access_enforce_tls_warning_description": "AVERTISSEMENT : Ceci limitera l’accès à l’interface Web au protocole HTTPS uniquement.",
"access_enforce_tls_warning_https": "Le mode HTTPS est activé et fonctionnel.",
"access_enforce_tls_warning_title": "Activer l'application du protocole 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 TLS",
"access_enforce_tls_description": "Reindirizza tutte le richieste HTTP a HTTPS",
"access_enforce_tls_warning_before": "Prima di abilitare questa funzione, assicurati di avere:",
"access_enforce_tls_warning_confirm": "Capisco, abilita comunque",
"access_enforce_tls_warning_description": "ATTENZIONE: questo limiterà l'accesso all'interfaccia web solo a HTTPS.",
"access_enforce_tls_warning_https": "La modalità HTTPS è abilitata e funzionante.",
"access_enforce_tls_warning_title": "Abilitare l'opzione Applica 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -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åndhev TLS",
"access_enforce_tls_description": "Omdiriger alle HTTP-forespørsler til HTTPS",
"access_enforce_tls_warning_before": "Før du aktiverer denne funksjonen, må du sørge 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 webgrensesnittet til kun HTTPS.",
"access_enforce_tls_warning_https": "HTTPS-modus er aktivert og fungerer.",
"access_enforce_tls_warning_title": "Aktiver håndhevelse av 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
"access_description": "Hantera enhetens åtkomstkontroll",
"access_disable_protection": "Inaktivera skydd",
"access_enable_password": "Aktivera lösenord",
"access_enforce_tls_label": "Tillämpa 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 åtkomsten till webbgränssnittet till endast HTTPS.",
"access_enforce_tls_warning_https": "HTTPS-läget är aktiverat och fungerar.",
"access_enforce_tls_warning_title": "Aktivera 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}",
Expand Down
7 changes: 7 additions & 0 deletions ui/localization/messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
"access_description": "管理设备的访问控制。",
"access_disable_protection": "禁用保护",
"access_enable_password": "启用密码",
"access_enforce_tls_label": "强制执行 TLS",
"access_enforce_tls_description": "将所有 HTTP 请求重定向到 HTTPS",
"access_enforce_tls_warning_before": "启用此功能前,请确保您已具备以下条件:",
"access_enforce_tls_warning_confirm": "我已了解,确认启用",
"access_enforce_tls_warning_description": "警告:这将限制网页界面仅支持 HTTPS 访问。",
"access_enforce_tls_warning_https": "HTTPS模式已启用并正常工作。",
"access_enforce_tls_warning_title": "启用强制 TLS?",
"access_failed_deregister": "注销设备失败:{error}",
"access_failed_update_cloud_url": "更新云地址失败:{error}",
"access_failed_update_tls": "更新 TLS 设置失败:{error}",
Expand Down
124 changes: 92 additions & 32 deletions ui/src/routes/devices.$id.settings.access._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -24,6 +26,7 @@ import { CloudState } from "./adopt";

export interface TLSState {
mode: "self-signed" | "custom" | "disabled";
enforce: boolean;
certificate?: string;
privateKey?: string;
}
Expand Down Expand Up @@ -54,8 +57,10 @@ export default function SettingsAccessIndexRoute() {
// Use a simple string identifier for the selected provider
const [selectedProvider, setSelectedProvider] = useState<string>("jetkvm");
const [tlsMode, setTlsMode] = useState<string>("unknown");
const [tlsEnforce, setTlsEnforce] = useState<boolean>(false);
const [tlsCert, setTlsCert] = useState<string>("");
const [tlsKey, setTlsKey] = useState<string>("");
const [showTlsEnforceWarning, setShowTlsEnforceWarning] = useState(false);

const getCloudState = useCallback(() => {
send("getCloudState", {}, (resp: JsonRpcResponse) => {
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
};

Expand All @@ -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
Expand Down Expand Up @@ -233,36 +260,69 @@ export default function SettingsAccessIndexRoute() {
/>
</SettingsItem>

{tlsMode === "custom" && (
<NestedSettingsGroup className="mt-4">
<SettingsItem
title={m.access_tls_certificate_title()}
description={m.access_tls_certificate_description()}
/>
<TextAreaWithLabel
label={m.access_certificate_label()}
rows={3}
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
value={tlsCert}
onChange={e => handleTlsCertChange(e.target.value)}
/>
<TextAreaWithLabel
label={m.access_private_key_label()}
description={m.access_private_key_description()}
rows={3}
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
value={tlsKey}
onChange={e => handleTlsKeyChange(e.target.value)}
{(tlsMode === "custom" || tlsMode == "self-signed") && (
<>
<NestedSettingsGroup className="mt-4">
<div>
<CheckboxWithLabel
label={m.access_enforce_tls_label()}
description={m.access_enforce_tls_description()}
checked={tlsEnforce}
onChange={e => handleTlsEnforceChange(e.target.checked)}
/>
</div>
{tlsMode === "custom" && (
<>
<SettingsItem
title={m.access_tls_certificate_title()}
description={m.access_tls_certificate_description()}
/>
<TextAreaWithLabel
label={m.access_certificate_label()}
rows={3}
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
value={tlsCert}
onChange={e => handleTlsCertChange(e.target.value)}
/>
<TextAreaWithLabel
label={m.access_private_key_label()}
description={m.access_private_key_description()}
rows={3}
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
value={tlsKey}
onChange={e => handleTlsKeyChange(e.target.value)}
/>
<div className="flex items-center gap-x-2">
<Button
size="SM"
theme="primary"
text={m.access_update_tls_settings()}
onClick={handleCustomTlsUpdate}
/>
</div>
</>
)}
</NestedSettingsGroup>
<ConfirmDialog
open={showTlsEnforceWarning}
onClose={() => {
setShowTlsEnforceWarning(false);
}}
title={m.access_enforce_tls_warning_title()}
description={
<>
<p>{m.access_enforce_tls_warning_description()}</p>
<p>{m.access_enforce_tls_warning_before()}</p>
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
<li>{m.access_enforce_tls_warning_https()}</li>
</ul>
</>
}
variant="warning"
confirmText={m.access_enforce_tls_warning_confirm()}
onConfirm={confirmTlsEnforceEnable}
/>
<div className="flex items-center gap-x-2">
<Button
size="SM"
theme="primary"
text={m.access_update_tls_settings()}
onClick={handleCustomTlsUpdate}
/>
</div>
</NestedSettingsGroup>
</>
)}

<SettingsItem
Expand Down
Loading