feat(sense): add loopback NVR management console
Harness governance / validate (pull_request) Has been cancelled

This commit is contained in:
QiuSW
2026-08-11 14:38:22 +08:00
parent a4427a6d2b
commit 7db707e596
15 changed files with 1370 additions and 13 deletions
+29
View File
@@ -27,6 +27,7 @@ const (
defaultONVIFMode = "disabled"
defaultControlAuthMode = "static-sha256"
defaultAuditRelayPeriod = time.Second
defaultConsoleWebRTCURL = "http://127.0.0.1:8889"
)
var instanceIDPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$`)
@@ -54,6 +55,8 @@ type Config struct {
ControlAuthFile string
ControlCursorKeyFile string
ControlAllowInsecureHTTP bool
ConsoleEnabled bool
ConsoleWebRTCBaseURL string
AuditRelayEnabled bool
AuditRelayURL string
AuditRelayKeyFile string
@@ -98,6 +101,10 @@ func Load() (Config, error) {
if err != nil {
return Config{}, err
}
consoleEnabled, err := boolEnv("SENSE_CONSOLE_ENABLED", false)
if err != nil {
return Config{}, err
}
auditRelayEnabled, err := boolEnv("SENSE_AUDIT_RELAY_ENABLED", false)
if err != nil {
return Config{}, err
@@ -144,6 +151,8 @@ func Load() (Config, error) {
ControlAuthFile: stringEnv("SENSE_CONTROL_AUTH_FILE", ""),
ControlCursorKeyFile: stringEnv("SENSE_CONTROL_CURSOR_KEY_FILE", ""),
ControlAllowInsecureHTTP: controlAllowInsecure,
ConsoleEnabled: consoleEnabled,
ConsoleWebRTCBaseURL: stringEnv("SENSE_CONSOLE_WEBRTC_BASE_URL", defaultConsoleWebRTCURL),
AuditRelayEnabled: auditRelayEnabled,
AuditRelayURL: stringEnv("SENSE_AUDIT_RELAY_URL", ""),
AuditRelayKeyFile: stringEnv("SENSE_AUDIT_RELAY_KEY_FILE", ""),
@@ -247,6 +256,26 @@ func (c Config) Validate() error {
return fmt.Errorf("non-loopback Control API requires SENSE_CONTROL_ALLOW_INSECURE_HTTP=true")
}
}
if c.ConsoleEnabled {
if !c.ControlAPIEnabled {
return fmt.Errorf("Sense console requires SENSE_CONTROL_API_ENABLED=true")
}
if !isLoopback {
return fmt.Errorf("Sense console requires an explicit loopback SENSE_HTTP_ADDR")
}
previewURL, err := url.Parse(c.ConsoleWebRTCBaseURL)
if err != nil || previewURL.Host == "" ||
(previewURL.Scheme != "http" && previewURL.Scheme != "https") ||
previewURL.User != nil || previewURL.RawQuery != "" || previewURL.Fragment != "" ||
(previewURL.Path != "" && previewURL.Path != "/") {
return fmt.Errorf("invalid SENSE_CONSOLE_WEBRTC_BASE_URL")
}
previewHost := previewURL.Hostname()
previewIP := net.ParseIP(previewHost)
if previewHost != "localhost" && (previewIP == nil || !previewIP.IsLoopback()) {
return fmt.Errorf("SENSE_CONSOLE_WEBRTC_BASE_URL must use an explicit loopback host")
}
}
if c.AuditRelayEnabled {
if databaseDriver != postgresDatabaseDriver {
return fmt.Errorf("Sense audit relay requires SENSE_DB_DRIVER=postgres")
+36
View File
@@ -167,6 +167,42 @@ func TestValidateControlAPINonLoopbackNeedsSeparateRiskAcceptance(t *testing.T)
}
}
func TestValidateSenseConsoleSecurityBoundary(t *testing.T) {
base := Config{
HTTPAddress: "127.0.0.1:8080", DatabaseDriver: "postgres",
DatabaseDSN: "postgres://sense-runtime@127.0.0.1/yovision?sslmode=disable",
MediaMTXURL: "http://127.0.0.1:9997", ReconcileInterval: time.Second, ProbeInterval: time.Second,
ControlAPIEnabled: true, ControlAuthMode: "static-sha256",
ControlAuthFile: filepath.Join(t.TempDir(), "sense-auth.json"),
ControlCursorKeyFile: filepath.Join(t.TempDir(), "sense-cursor.key"),
ConsoleEnabled: true, ConsoleWebRTCBaseURL: "http://127.0.0.1:8889",
}
if err := base.Validate(); err != nil {
t.Fatalf("valid loopback console rejected: %v", err)
}
withoutControl := base
withoutControl.ControlAPIEnabled = false
if err := withoutControl.Validate(); err == nil {
t.Fatal("console without Control API was accepted")
}
remoteBind := base
remoteBind.HTTPAddress, remoteBind.AllowNonLoopback = "0.0.0.0:8080", true
remoteBind.ControlAllowInsecureHTTP = true
if err := remoteBind.Validate(); err == nil {
t.Fatal("console on non-loopback Sense listener was accepted")
}
for _, invalidURL := range []string{
"http://media.example:8889", "ftp://127.0.0.1:8889", "http://127.0.0.1:8889/path",
"http://127.0.0.1:8889?token=hidden", "http://user@127.0.0.1:8889",
} {
candidate := base
candidate.ConsoleWebRTCBaseURL = invalidURL
if err := candidate.Validate(); err == nil {
t.Fatalf("invalid console WebRTC URL was accepted: %s", invalidURL)
}
}
}
func TestValidateAuditRelaySecurityBoundary(t *testing.T) {
base := Config{
HTTPAddress: "127.0.0.1:8080", DatabaseDriver: "postgres",