From f6e733de6b714ee2df94629e93aaa72c7e392dea Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sun, 3 May 2026 21:08:21 +0200 Subject: [PATCH] fix(certmanager): use hostname as server cert CN instead of a random Bose domain domains[0] was non-deterministic (Go map iteration) and could resolve to any domain in the list including Bose-owned domains. Adds CommonName field to CertificateManager, defaulting to "localhost", set to the device hostname at startup. All Bose domains remain in the SAN where clients actually look. Co-Authored-By: Claude Sonnet 4.6 --- cmd/soundtouch-service/main.go | 10 ++++++++-- pkg/service/certmanager/certmanager.go | 10 ++++++++-- pkg/service/certmanager/certmanager_test.go | 5 +++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/cmd/soundtouch-service/main.go b/cmd/soundtouch-service/main.go index c17df3c..362daab 100644 --- a/cmd/soundtouch-service/main.go +++ b/cmd/soundtouch-service/main.go @@ -387,7 +387,7 @@ func main() { config.domains = getDomains(config.serverURL, config.httpsServerURL, hostname) - cm := initCertificateManager(config.dataDir) + cm := initCertificateManager(config.dataDir, config.hostname) sm := setup.NewManager(config.serverURL, ds, cm) sm.MgmtUsername = config.mgmtUsername sm.MgmtPassword = config.mgmtPassword @@ -469,12 +469,14 @@ func main() { // TLS cert generation can be slow on constrained hardware; run it in the // background so the HTTP server is available immediately. log.Printf("HTTPS setup running in background; %s will be available shortly", config.httpsServerURL) + go func() { tlsConfig, err := cm.GetServerTLSConfig(config.domains) if err != nil { log.Printf("Warning: Failed to setup TLS: %v", err) return } + startHTTPSServer(config.httpsAddr, r, tlsConfig, config.httpsServerURL) }() @@ -510,6 +512,7 @@ type serviceConfig struct { bindAddr string addr string dataDir string + hostname string serverURL string httpsServerURL string httpsAddr string @@ -621,6 +624,7 @@ func loadConfig(c *cli.Context) serviceConfig { bindAddr: bindAddr, addr: addr, dataDir: dataDir, + hostname: hostname, serverURL: serverURL, httpsServerURL: httpsServerURL, httpsAddr: httpsAddr, @@ -812,8 +816,10 @@ func initDataStore(dataDir string) *datastore.DataStore { return ds } -func initCertificateManager(dataDir string) *certmanager.CertificateManager { +func initCertificateManager(dataDir, hostname string) *certmanager.CertificateManager { cm := certmanager.NewCertificateManager(filepath.Join(dataDir, "certs")) + + cm.CommonName = hostname if err := cm.EnsureCA(); err != nil { log.Printf("Warning: Failed to ensure CA: %v", err) } diff --git a/pkg/service/certmanager/certmanager.go b/pkg/service/certmanager/certmanager.go index 6ede602..43ea1b3 100644 --- a/pkg/service/certmanager/certmanager.go +++ b/pkg/service/certmanager/certmanager.go @@ -17,7 +17,8 @@ import ( // CertificateManager handles CA and certificate generation. type CertificateManager struct { - CertsDir string + CertsDir string + CommonName string // CN for generated server certs; defaults to "localhost" if empty } // NewCertificateManager creates a new CertificateManager. @@ -255,11 +256,16 @@ func (cm *CertificateManager) GenerateCertificate(domains []string) ([]byte, []b } } + cn := cm.CommonName + if cn == "" { + cn = "localhost" + } + template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"AfterTouch"}, - CommonName: domains[0], + CommonName: cn, }, NotBefore: notBefore, NotAfter: notAfter, diff --git a/pkg/service/certmanager/certmanager_test.go b/pkg/service/certmanager/certmanager_test.go index b2652fc..7c282b0 100644 --- a/pkg/service/certmanager/certmanager_test.go +++ b/pkg/service/certmanager/certmanager_test.go @@ -16,6 +16,7 @@ func TestCertificateManager(t *testing.T) { defer os.RemoveAll(tempDir) cm := NewCertificateManager(filepath.Join(tempDir, "certs")) + cm.CommonName = "test.local" // Test CA generation if err := cm.EnsureCA(); err != nil { @@ -67,8 +68,8 @@ func TestCertificateManager(t *testing.T) { t.Fatalf("Failed to parse certificate: %v", err) } - if cert.Subject.CommonName != domains[0] { - t.Errorf("Expected CommonName %s, got %s", domains[0], cert.Subject.CommonName) + if cert.Subject.CommonName != cm.CommonName { + t.Errorf("Expected CommonName %s, got %s", cm.CommonName, cert.Subject.CommonName) } // Check DNS names