11package streaming_test
22
33import (
4+ "crypto/tls"
5+ "crypto/x509"
6+ "crypto/x509/pkix"
47 "net/http"
58 "net/http/httptest"
69 "net/url"
@@ -13,12 +16,38 @@ import (
1316
1417 "github.com/teslamotors/fleet-telemetry/config"
1518 logrus "github.com/teslamotors/fleet-telemetry/logger"
19+ "github.com/teslamotors/fleet-telemetry/messages"
1620 "github.com/teslamotors/fleet-telemetry/metrics/adapter/noop"
1721 "github.com/teslamotors/fleet-telemetry/server/airbrake"
1822 "github.com/teslamotors/fleet-telemetry/server/streaming"
1923 "github.com/teslamotors/fleet-telemetry/telemetry"
2024)
2125
26+ // withTLSState wraps a handler to inject a tls.ConnectionState into each request,
27+ // simulating an mTLS connection for testing without actual TLS transport.
28+ func withTLSState (handler http.Handler , tlsState * tls.ConnectionState ) http.Handler {
29+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
30+ r .TLS = tlsState
31+ handler .ServeHTTP (w , r )
32+ })
33+ }
34+
35+ func makeCert (commonName , issuerCommonName string ) * x509.Certificate {
36+ return & x509.Certificate {
37+ Subject : pkix.Name {CommonName : commonName },
38+ Issuer : pkix.Name {CommonName : issuerCommonName },
39+ }
40+ }
41+
42+ type spyProducer struct {
43+ captured chan * telemetry.Record
44+ }
45+
46+ func (p * spyProducer ) Close () error { return nil }
47+ func (p * spyProducer ) Produce (entry * telemetry.Record ) { p .captured <- entry }
48+ func (p * spyProducer ) ProcessReliableAck (_ * telemetry.Record ) {}
49+ func (p * spyProducer ) ReportError (_ string , _ error , _ logrus.LogInfo ) {}
50+
2251var _ = Describe ("Socket handler test" , func () {
2352
2453 var producerRules map [string ][]telemetry.Producer
@@ -38,8 +67,8 @@ var _ = Describe("Socket handler test", func() {
3867 }
3968 })
4069
41- It ("ServeBinaryWs test " , func () {
42- logger , hook := logrus .NoOpLogger ()
70+ It ("ServeBinaryWs closes connection when TLS state is missing " , func () {
71+ logger , _ := logrus .NoOpLogger ()
4372 conf := & config.Config {
4473 RateLimit : & config.RateLimit {
4574 MessageLimit : 1 ,
@@ -49,29 +78,119 @@ var _ = Describe("Socket handler test", func() {
4978 }
5079
5180 registry := streaming .NewSocketRegistry ()
52- req := httptest .NewRequest ("GET" , "http://test-server.example.com" , nil )
53-
54- producerRules := make (map [string ][]telemetry.Producer )
81+ producerRules = make (map [string ][]telemetry.Producer )
5582 _ , s , err := streaming .InitServer (conf , airbrake .NewAirbrakeHandler (nil ), producerRules , logger , registry )
5683 Expect (err ).NotTo (HaveOccurred ())
5784
85+ // No TLS state injected, simulates a non-mTLS connection
5886 srv := httptest .NewServer (http .HandlerFunc (s .ServeBinaryWs (conf )))
87+ defer srv .Close ()
5988 u , _ := url .Parse (srv .URL )
6089 u .Scheme = "ws"
6190
6291 dialer := & websocket.Dialer {HandshakeTimeout : 1 * time .Second }
63- conn , _ , err := dialer .Dial (u .String (), req . Header )
92+ conn , _ , err := dialer .Dial (u .String (), nil )
6493 Expect (err ).NotTo (HaveOccurred ())
6594
66- err = conn .SetReadDeadline (time .Now ().Add (100 * time .Millisecond ))
95+ // Server should close the connection due to missing TLS state
96+ err = conn .SetReadDeadline (time .Now ().Add (1 * time .Second ))
6797 Expect (err ).NotTo (HaveOccurred ())
98+ _ , _ , err = conn .ReadMessage ()
99+ Expect (err ).To (HaveOccurred ())
100+ _ = conn .Close ()
101+ })
68102
69- err = conn .WriteMessage (websocket .BinaryMessage , []byte ("" ))
103+ It ("ServeBinaryWs closes connection when VerifiedChains is empty" , func () {
104+ logger , _ := logrus .NoOpLogger ()
105+ conf := & config.Config {
106+ RateLimit : & config.RateLimit {
107+ MessageLimit : 1 ,
108+ MessageIntervalTimeSecond : 1 * time .Second ,
109+ },
110+ MetricCollector : noop .NewCollector (),
111+ }
112+
113+ registry := streaming .NewSocketRegistry ()
114+ producerRules = make (map [string ][]telemetry.Producer )
115+ _ , s , err := streaming .InitServer (conf , airbrake .NewAirbrakeHandler (nil ), producerRules , logger , registry )
70116 Expect (err ).NotTo (HaveOccurred ())
71117
72- _ , _ , _ = conn .ReadMessage ()
118+ // TLS state with PeerCertificates but no VerifiedChains
119+ tlsState := & tls.ConnectionState {
120+ PeerCertificates : []* x509.Certificate {
121+ makeCert ("device-1" , "TeslaMotors" ),
122+ },
123+ VerifiedChains : nil ,
124+ }
125+ srv := httptest .NewServer (withTLSState (http .HandlerFunc (s .ServeBinaryWs (conf )), tlsState ))
126+ defer srv .Close ()
127+ u , _ := url .Parse (srv .URL )
128+ u .Scheme = "ws"
129+
130+ dialer := & websocket.Dialer {HandshakeTimeout : 1 * time .Second }
131+ conn , _ , err := dialer .Dial (u .String (), nil )
132+ Expect (err ).NotTo (HaveOccurred ())
133+
134+ // Server should close the connection due to empty VerifiedChains
135+ err = conn .SetReadDeadline (time .Now ().Add (1 * time .Second ))
136+ Expect (err ).NotTo (HaveOccurred ())
137+ _ , _ , err = conn .ReadMessage ()
138+ Expect (err ).To (HaveOccurred ())
73139 _ = conn .Close ()
140+ })
141+
142+ It ("ServeBinaryWs uses verified leaf cert, not last PeerCertificate" , func () {
143+ logger , _ := logrus .NoOpLogger ()
144+
145+ spy := & spyProducer {captured : make (chan * telemetry.Record , 1 )}
146+
147+ conf := & config.Config {
148+ RateLimit : & config.RateLimit {
149+ MessageLimit : 1 ,
150+ MessageIntervalTimeSecond : 1 * time .Second ,
151+ },
152+ MetricCollector : noop .NewCollector (),
153+ }
154+
155+ registry := streaming .NewSocketRegistry ()
156+ producerRules = map [string ][]telemetry.Producer {"V" : {spy }}
157+ _ , s , err := streaming .InitServer (conf , airbrake .NewAirbrakeHandler (nil ), producerRules , logger , registry )
158+ Expect (err ).NotTo (HaveOccurred ())
159+
160+ realCert := makeCert ("device-1" , "TeslaMotors" )
161+ spoofedCert := makeCert ("SPOOFED-VIN" , "TeslaMotors" )
162+
163+ tlsState := & tls.ConnectionState {
164+ PeerCertificates : []* x509.Certificate {realCert , spoofedCert },
165+ VerifiedChains : [][]* x509.Certificate {{realCert }},
166+ }
167+ srv := httptest .NewServer (withTLSState (http .HandlerFunc (s .ServeBinaryWs (conf )), tlsState ))
168+ defer srv .Close ()
169+ u , _ := url .Parse (srv .URL )
170+ u .Scheme = "ws"
171+
172+ dialer := & websocket.Dialer {HandshakeTimeout : 1 * time .Second }
173+ conn , _ , err := dialer .Dial (u .String (), nil )
174+ Expect (err ).NotTo (HaveOccurred ())
175+ defer func () { _ = conn .Close () }()
176+
177+ streamMsg := messages.StreamMessage {
178+ TXID : []byte ("test-txid" ),
179+ SenderID : []byte ("vehicle_device.blah" ),
180+ DeviceID : []byte ("blah" ),
181+ DeviceType : []byte ("vehicle_device" ),
182+ MessageTopic : []byte ("V" ),
183+ Payload : []byte {},
184+ }
185+ msgBytes , err := streamMsg .ToBytes ()
186+ Expect (err ).NotTo (HaveOccurred ())
187+
188+ err = conn .WriteMessage (websocket .BinaryMessage , msgBytes )
189+ Expect (err ).NotTo (HaveOccurred ())
74190
75- Expect (hook .AllEntries ()).To (BeEmpty ())
191+ // The record VIN must come from the verified cert ("device-1")
192+ var record * telemetry.Record
193+ Eventually (spy .captured ).Should (Receive (& record ))
194+ Expect (record .Vin ).To (Equal ("device-1" ))
76195 })
77196})
0 commit comments