kvm/web_tls.go

128 lines
3.0 KiB
Go

package kvm
import (
"crypto/tls"
"encoding/pem"
"fmt"
"net/http"
"github.com/jetkvm/kvm/internal/websecure"
)
const (
tlsStorePath = "/userdata/jetkvm/tls"
webSecureListen = ":443"
webSecureSelfSignedDefaultDomain = "jetkvm.local"
webSecureSelfSignedCAName = "JetKVM Self-Signed CA"
webSecureSelfSignedOrganization = "JetKVM"
webSecureSelfSignedOU = "JetKVM Self-Signed"
webSecureCustomCertificateName = "user-defined"
)
var (
certStore *websecure.CertStore
certSigner *websecure.SelfSigner
)
type TLSState struct {
Mode string `json:"mode"`
Certificate string `json:"certificate"`
PrivateKey string `json:"privateKey"`
}
func initCertStore() {
certStore = websecure.NewCertStore(tlsStorePath)
certStore.LoadCertificates()
certSigner = websecure.NewSelfSigner(
certStore,
logger,
webSecureSelfSignedDefaultDomain,
webSecureSelfSignedOrganization,
webSecureSelfSignedOU,
webSecureSelfSignedCAName,
)
}
func getCertificate(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
if config.TLSMode == "self-signed" {
if isTimeSyncNeeded() || !timeSyncSuccess {
return nil, fmt.Errorf("time is not synced")
}
return certSigner.GetCertificate(info)
} else if config.TLSMode == "custom" {
return certStore.GetCertificate(webSecureCustomCertificateName), nil
}
logger.Infof("TLS mode is disabled but WebSecure is running, returning nil")
return nil, nil
}
func getTLSState() TLSState {
s := TLSState{}
switch config.TLSMode {
case "disabled":
s.Mode = "disabled"
case "custom":
s.Mode = "custom"
cert := certStore.GetCertificate(webSecureCustomCertificateName)
if cert != nil {
var certPEM []byte
// convert to pem format
for _, c := range cert.Certificate {
block := pem.Block{
Type: "CERTIFICATE",
Bytes: c,
}
certPEM = append(certPEM, pem.EncodeToMemory(&block)...)
}
s.Certificate = string(certPEM)
}
case "self-signed":
s.Mode = "self-signed"
}
return s
}
func setTLSState(s TLSState) error {
switch s.Mode {
case "disabled":
config.TLSMode = ""
case "custom":
// parse pem to cert and key
err, _ := certStore.ValidateAndSaveCertificate(webSecureCustomCertificateName, s.Certificate, s.PrivateKey, true)
// warn doesn't matter as ... we don't know the hostname yet
if err != nil {
return fmt.Errorf("Failed to save certificate: %w", err)
}
config.TLSMode = "custom"
case "self-signed":
config.TLSMode = "self-signed"
default:
return fmt.Errorf("invalid TLS mode: %s", s.Mode)
}
return nil
}
// RunWebSecureServer runs a web server with TLS.
func RunWebSecureServer() {
r := setupRouter()
server := &http.Server{
Addr: webSecureListen,
Handler: r,
TLSConfig: &tls.Config{
MaxVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{},
GetCertificate: getCertificate,
},
}
logger.Infof("Starting websecure server on %s", webSecureListen)
err := server.ListenAndServeTLS("", "")
if err != nil {
panic(err)
}
}