mirror of https://github.com/jetkvm/kvm.git
no longer need to reboot to start / stop tls service
This commit is contained in:
parent
720c6f8951
commit
ffb43da96e
|
@ -109,6 +109,8 @@ func SaveConfig() error {
|
||||||
configLock.Lock()
|
configLock.Lock()
|
||||||
defer configLock.Unlock()
|
defer configLock.Unlock()
|
||||||
|
|
||||||
|
logger.Tracef("Saving config to %s", configPath)
|
||||||
|
|
||||||
file, err := os.Create(configPath)
|
file, err := os.Create(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create config file: %w", err)
|
return fmt.Errorf("failed to create config file: %w", err)
|
||||||
|
|
|
@ -405,8 +405,8 @@ func rpcGetTLSState() TLSState {
|
||||||
return getTLSState()
|
return getTLSState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetTLSState(tlsState TLSState) error {
|
func rpcSetTLSState(state TLSState) error {
|
||||||
err := setTLSState(tlsState)
|
err := setTLSState(state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set TLS state: %w", err)
|
return fmt.Errorf("failed to set TLS state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
6
main.go
6
main.go
|
@ -70,10 +70,12 @@ func Main() {
|
||||||
//go RunFuseServer()
|
//go RunFuseServer()
|
||||||
go RunWebServer()
|
go RunWebServer()
|
||||||
|
|
||||||
|
go RunWebSecureServer()
|
||||||
|
// Web secure server is started only if TLS mode is enabled
|
||||||
if config.TLSMode != "" {
|
if config.TLSMode != "" {
|
||||||
initCertStore()
|
startWebSecureServer()
|
||||||
go RunWebSecureServer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// As websocket client already checks if the cloud token is set, we can start it here.
|
// As websocket client already checks if the cloud token is set, we can start it here.
|
||||||
go RunWebsocketClient()
|
go RunWebsocketClient()
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,12 @@ export default function SettingsAccessIndexRoute() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTlsUpdate = useCallback(() => {
|
const handleTlsUpdate = useCallback(() => {
|
||||||
send("setTLSState", { state: { mode: tlsMode, certificate: tlsCert, privateKey: tlsKey } as TLSState }, resp => {
|
const state = { mode: tlsMode } as TLSState;
|
||||||
|
if (tlsMode !== "disabled") {
|
||||||
|
state.certificate = tlsCert;
|
||||||
|
state.privateKey = tlsKey;
|
||||||
|
}
|
||||||
|
send("setTLSState", { state }, resp => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(`Failed to update TLS settings: ${resp.error.data || "Unknown error"}`);
|
notifications.error(`Failed to update TLS settings: ${resp.error.data || "Unknown error"}`);
|
||||||
return;
|
return;
|
||||||
|
@ -171,17 +176,6 @@ export default function SettingsAccessIndexRoute() {
|
||||||
});
|
});
|
||||||
}, [send, tlsMode, tlsCert, tlsKey]);
|
}, [send, tlsMode, tlsCert, tlsKey]);
|
||||||
|
|
||||||
const handleReboot = useCallback(() => {
|
|
||||||
send("reboot", { force: false }, resp => {
|
|
||||||
if ("error" in resp) {
|
|
||||||
notifications.error(`Failed to reboot: ${resp.error.data || "Unknown error"}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifications.success("Device will restart shortly, it might take a few seconds to boot up again.");
|
|
||||||
});
|
|
||||||
}, [send]);
|
|
||||||
|
|
||||||
// Fetch device ID and cloud state on component mount
|
// Fetch device ID and cloud state on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCloudState();
|
getCloudState();
|
||||||
|
@ -211,8 +205,8 @@ export default function SettingsAccessIndexRoute() {
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="HTTPS/TLS Mode"
|
title="HTTPS/TLS Mode"
|
||||||
description={<>
|
description={<>
|
||||||
Select the TLS mode for your device (beta)<br />
|
Select the TLS mode for your device <sup>experimental</sup><br />
|
||||||
<small>changing this setting might restart the device</small>
|
<small>The feature might not work as expected, please report any issues if you encounter any.</small>
|
||||||
</>}
|
</>}
|
||||||
>
|
>
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
|
@ -227,27 +221,6 @@ export default function SettingsAccessIndexRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
|
||||||
If TLS wasn't enabled before, you'll need to <strong>reboot</strong> the device to apply the changes.<br />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-x-2">
|
|
||||||
<Button
|
|
||||||
size="SM"
|
|
||||||
theme="primary"
|
|
||||||
text="Update TLS Settings"
|
|
||||||
onClick={handleTlsUpdate}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="SM"
|
|
||||||
theme="danger"
|
|
||||||
text="Reboot"
|
|
||||||
onClick={handleReboot}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{tlsMode === "custom" && (
|
{tlsMode === "custom" && (
|
||||||
<div className="mt-4 space-y-4">
|
<div className="mt-4 space-y-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
@ -282,6 +255,19 @@ export default function SettingsAccessIndexRoute() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-x-2">
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="primary"
|
||||||
|
text="Update TLS Settings"
|
||||||
|
onClick={handleTlsUpdate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title="Authentication Mode"
|
title="Authentication Mode"
|
||||||
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}
|
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}
|
||||||
|
|
85
web_tls.go
85
web_tls.go
|
@ -1,10 +1,13 @@
|
||||||
package kvm
|
package kvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/jetkvm/kvm/internal/websecure"
|
"github.com/jetkvm/kvm/internal/websecure"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +34,10 @@ type TLSState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCertStore() {
|
func initCertStore() {
|
||||||
|
if certStore != nil {
|
||||||
|
logger.Warnf("TLS store already initialized, it should not be initialized again")
|
||||||
|
return
|
||||||
|
}
|
||||||
certStore = websecure.NewCertStore(tlsStorePath)
|
certStore = websecure.NewCertStore(tlsStorePath)
|
||||||
certStore.LoadCertificates()
|
certStore.LoadCertificates()
|
||||||
|
|
||||||
|
@ -87,10 +94,18 @@ func getTLSState() TLSState {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTLSState(s TLSState) error {
|
func setTLSState(s TLSState) error {
|
||||||
|
var isChanged = false
|
||||||
|
|
||||||
switch s.Mode {
|
switch s.Mode {
|
||||||
case "disabled":
|
case "disabled":
|
||||||
|
if config.TLSMode != "" {
|
||||||
|
isChanged = true
|
||||||
|
}
|
||||||
config.TLSMode = ""
|
config.TLSMode = ""
|
||||||
case "custom":
|
case "custom":
|
||||||
|
if config.TLSMode == "" {
|
||||||
|
isChanged = true
|
||||||
|
}
|
||||||
// parse pem to cert and key
|
// parse pem to cert and key
|
||||||
err, _ := certStore.ValidateAndSaveCertificate(webSecureCustomCertificateName, s.Certificate, s.PrivateKey, true)
|
err, _ := certStore.ValidateAndSaveCertificate(webSecureCustomCertificateName, s.Certificate, s.PrivateKey, true)
|
||||||
// warn doesn't matter as ... we don't know the hostname yet
|
// warn doesn't matter as ... we don't know the hostname yet
|
||||||
|
@ -99,15 +114,47 @@ func setTLSState(s TLSState) error {
|
||||||
}
|
}
|
||||||
config.TLSMode = "custom"
|
config.TLSMode = "custom"
|
||||||
case "self-signed":
|
case "self-signed":
|
||||||
|
if config.TLSMode == "" {
|
||||||
|
isChanged = true
|
||||||
|
}
|
||||||
config.TLSMode = "self-signed"
|
config.TLSMode = "self-signed"
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid TLS mode: %s", s.Mode)
|
return fmt.Errorf("invalid TLS mode: %s", s.Mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isChanged {
|
||||||
|
logger.Tracef("TLS enabled state is not changed, not starting/stopping websecure server")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.TLSMode == "" {
|
||||||
|
logger.Tracef("Stopping websecure server, as TLS mode is disabled")
|
||||||
|
stopWebSecureServer()
|
||||||
|
} else {
|
||||||
|
logger.Tracef("Starting websecure server, as TLS mode is enabled")
|
||||||
|
startWebSecureServer()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
startTLS = make(chan struct{})
|
||||||
|
stopTLS = make(chan struct{})
|
||||||
|
tlsServiceLock = sync.Mutex{}
|
||||||
|
tlsStarted = false
|
||||||
|
)
|
||||||
|
|
||||||
// RunWebSecureServer runs a web server with TLS.
|
// RunWebSecureServer runs a web server with TLS.
|
||||||
func RunWebSecureServer() {
|
func runWebSecureServer() {
|
||||||
|
tlsServiceLock.Lock()
|
||||||
|
defer tlsServiceLock.Unlock()
|
||||||
|
|
||||||
|
tlsStarted = true
|
||||||
|
defer func() {
|
||||||
|
tlsStarted = false
|
||||||
|
}()
|
||||||
|
|
||||||
r := setupRouter()
|
r := setupRouter()
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
|
@ -120,8 +167,42 @@ func RunWebSecureServer() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
logger.Infof("Starting websecure server on %s", webSecureListen)
|
logger.Infof("Starting websecure server on %s", webSecureListen)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _ = range stopTLS {
|
||||||
|
logger.Infof("Shutting down websecure server")
|
||||||
|
server.Shutdown(context.Background())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
err := server.ListenAndServeTLS("", "")
|
err := server.ListenAndServeTLS("", "")
|
||||||
if err != nil {
|
if !errors.Is(err, http.ErrServerClosed) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stopWebSecureServer() {
|
||||||
|
if !tlsStarted {
|
||||||
|
logger.Warnf("Websecure server is not running, not stopping it")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stopTLS <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startWebSecureServer() {
|
||||||
|
if tlsStarted {
|
||||||
|
logger.Warnf("Websecure server is already running, not starting it again")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
startTLS <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunWebSecureServer() {
|
||||||
|
for _ = range startTLS {
|
||||||
|
logger.Tracef("Starting websecure server, as we have received a start signal")
|
||||||
|
if certStore == nil {
|
||||||
|
initCertStore()
|
||||||
|
}
|
||||||
|
go runWebSecureServer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue