mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 16:46:17 +00:00
Two per-device checks run against each speaker's CA bundle via a
single SSH probe round-trip:
(1) Every PEM block from ca-bundle.crt.original (the factory backup
written by TrustCACertFromBytes on first CA injection) must be
present in the live ca-bundle.crt. A missing block means the
original trust store was truncated, which would break external
HTTPS (Spotify, Amazon, firmware updates).
(2) The AfterTouch CA sentinel (# AfterTouch) must be present in
the live bundle. Without it the speaker rejects AfterTouch's
TLS cert and migration is effectively inactive.
Both findings carry a QuickFix:
- FixIDRestoreAndInjectCA: cp .original → live bundle over SSH,
then TrustCACert to re-inject the AfterTouch CA.
- FixIDInjectCACert: TrustCACert only (original certs intact).
Graceful degradation:
- SSH unavailable → SeverityInfo, no fix offered.
- .original absent (device never had install-ca run) → SeverityWarning,
suggest install-ca; check (2) still runs.
Infrastructure changes:
- ssh_probe.go: add ca-bundle.crt.original to probeFilePaths (free
in the existing single-round-trip batch).
- setup.go: export ProbeCABundles and RestoreCABundleFromOriginal so
the handlers package can use them without exposing speakerProbe.
- Fix executors live in handlers (need setup.Manager) per the
established boundary used by completeSpeakerPairingFix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
2.0 KiB
Go
64 lines
2.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/service/health"
|
|
)
|
|
|
|
// restoreAndInjectCAFix is the FixFunc registered for
|
|
// (CheckIDSpeakerCABundle, FixIDRestoreAndInjectCA). It handles the case
|
|
// where original factory CA certificates have gone missing from the live
|
|
// bundle by:
|
|
//
|
|
// 1. Copying .original back over the live ca-bundle.crt via SSH.
|
|
// 2. Re-injecting the AfterTouch CA via TrustCACert so the speaker
|
|
// continues to trust AfterTouch's TLS certificate.
|
|
func (s *Server) restoreAndInjectCAFix(target health.Target) (string, error) {
|
|
if target.Device == "" {
|
|
return "", fmt.Errorf("device is required")
|
|
}
|
|
|
|
deviceIP, err := s.resolveDeviceIDToIP(target.Device)
|
|
if err != nil {
|
|
return "", fmt.Errorf("locate device %s: %w", target.Device, err)
|
|
}
|
|
|
|
if err := s.sm.RestoreCABundleFromOriginal(deviceIP); err != nil {
|
|
return "", fmt.Errorf("restore CA bundle on device %s: %w", target.Device, err)
|
|
}
|
|
|
|
if _, err := s.sm.TrustCACert(deviceIP); err != nil {
|
|
return "", fmt.Errorf("re-inject AfterTouch CA on device %s after restore: %w", target.Device, err)
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"Device %s: original CA bundle restored from factory backup and AfterTouch CA re-injected.",
|
|
target.Device,
|
|
), nil
|
|
}
|
|
|
|
// injectCACertFix is the FixFunc registered for
|
|
// (CheckIDSpeakerCABundle, FixIDInjectCACert). It handles the case where
|
|
// the AfterTouch CA is absent from the live bundle (e.g. removed manually
|
|
// or not yet injected).
|
|
func (s *Server) injectCACertFix(target health.Target) (string, error) {
|
|
if target.Device == "" {
|
|
return "", fmt.Errorf("device is required")
|
|
}
|
|
|
|
deviceIP, err := s.resolveDeviceIDToIP(target.Device)
|
|
if err != nil {
|
|
return "", fmt.Errorf("locate device %s: %w", target.Device, err)
|
|
}
|
|
|
|
if _, err := s.sm.TrustCACert(deviceIP); err != nil {
|
|
return "", fmt.Errorf("inject AfterTouch CA on device %s: %w", target.Device, err)
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"Device %s: AfterTouch CA certificate installed in speaker's bundle.",
|
|
target.Device,
|
|
), nil
|
|
}
|