@@ -3,9 +3,13 @@ package httpd_test
33import (
44 "crypto/tls"
55 "fmt"
6+ "math/big"
7+ "net"
68 "net/http"
9+ "os"
710 "testing"
811
12+ th "github.com/influxdata/influxdb/pkg/testing/helper"
913 "github.com/influxdata/influxdb/pkg/testing/selfsigned"
1014 "github.com/influxdata/influxdb/pkg/tlsconfig"
1115 "github.com/influxdata/influxdb/services/httpd"
@@ -46,9 +50,7 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
4650
4751 // Open service to initialize certLoader
4852 require .NoError (t , s .Open ())
49- defer func () {
50- require .NoError (t , s .Close ())
51- }()
53+ defer th .CheckedClose (t , s )()
5254
5355 // Try to verify reload with HTTPS disabled
5456 newConfig := httpd.Config {
@@ -98,9 +100,7 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
98100
99101 // Open service to initialize certLoader
100102 require .NoError (t , s .Open ())
101- defer func () {
102- require .NoError (t , s .Close ())
103- }()
103+ defer th .CheckedClose (t , s )()
104104
105105 // Get the initial certificate serial number for comparison
106106 initialCert , err := tlsconfig .LoadCertificate (ss1 .CertPath , ss1 .KeyPath )
@@ -173,9 +173,7 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
173173
174174 // Open service to initialize certLoader
175175 require .NoError (t , s .Open ())
176- defer func () {
177- require .NoError (t , s .Close ())
178- }()
176+ defer th .CheckedClose (t , s )()
179177
180178 // Try to verify reload with non-existent certificate (one example of VerifyLoad failure)
181179 newConfig := httpd.Config {
@@ -188,7 +186,7 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
188186 require .Nil (t , applyFunc )
189187 })
190188
191- t .Run ("HTTPS enabled, no certificate change" , func (t * testing.T ) {
189+ t .Run ("HTTPS enabled, no certificate change (same paths) " , func (t * testing.T ) {
192190 // Create initial certificates
193191 ss := selfsigned .NewSelfSignedCert (t )
194192
@@ -204,9 +202,7 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
204202
205203 // Open service to initialize certLoader
206204 require .NoError (t , s .Open ())
207- defer func () {
208- require .NoError (t , s .Close ())
209- }()
205+ defer th .CheckedClose (t , s )()
210206
211207 // Get the certificate serial number
212208 cert , err := tlsconfig .LoadCertificate (ss .CertPath , ss .KeyPath )
@@ -255,3 +251,168 @@ func TestService_VerifyReloadedConfig(t *testing.T) {
255251 }
256252 })
257253}
254+
255+ // openService creates, opens, and registers cleanup for an httpd.Service.
256+ func openService (t * testing.T , config httpd.Config ) * httpd.Service {
257+ t .Helper ()
258+ s := httpd .NewService (config )
259+ s .WithLogger (zap .NewNop ())
260+ require .NoError (t , s .Open ())
261+ t .Cleanup (th .CheckedClose (t , s ))
262+ return s
263+ }
264+
265+ // insecureHTTPSClient returns an *http.Client that skips TLS certificate verification.
266+ func insecureHTTPSClient () * http.Client {
267+ return & http.Client {Transport : & http.Transport {
268+ TLSClientConfig : & tls.Config {InsecureSkipVerify : true },
269+ }}
270+ }
271+
272+ // pingHTTPS performs a GET /ping over HTTPS using the given client and returns the response.
273+ func pingHTTPS (t * testing.T , addr net.Addr , client * http.Client ) * http.Response {
274+ t .Helper ()
275+ resp , err := client .Get (fmt .Sprintf ("https://%s/ping" , addr ))
276+ require .NoError (t , err )
277+ t .Cleanup (func () { resp .Body .Close () })
278+ return resp
279+ }
280+
281+ // requireServingCertSerial verifies the service is serving TLS with the expected certificate serial.
282+ func requireServingCertSerial (t * testing.T , s * httpd.Service , expectedSerial * big.Int ) {
283+ t .Helper ()
284+ resp := pingHTTPS (t , s .Addr (), insecureHTTPSClient ())
285+ require .NotNil (t , resp .TLS )
286+ require .NotEmpty (t , resp .TLS .PeerCertificates )
287+ require .Equal (t , expectedSerial , resp .TLS .PeerCertificates [0 ].SerialNumber )
288+ }
289+
290+ // loadCertSerial loads a certificate and returns its serial number.
291+ func loadCertSerial (t * testing.T , certPath , keyPath string ) * big.Int {
292+ t .Helper ()
293+ cert , err := tlsconfig .LoadCertificate (certPath , keyPath )
294+ require .NoError (t , err )
295+ return cert .Leaf .SerialNumber
296+ }
297+
298+ // TestService_Open_TLSConfigManager verifies that the TLSConfigManager created
299+ // in Service.Open is configured properly based on the Service's configuration.
300+ func TestService_Open_TLSConfigManager (t * testing.T ) {
301+ t .Run ("HTTPS disabled creates non-TLS listener" , func (t * testing.T ) {
302+ s := openService (t , httpd.Config {
303+ BindAddress : "localhost:" ,
304+ HTTPSEnabled : false ,
305+ })
306+
307+ resp , err := http .Get (fmt .Sprintf ("http://%s/ping" , s .Addr ()))
308+ require .NoError (t , err )
309+ resp .Body .Close ()
310+ require .Equal (t , http .StatusNoContent , resp .StatusCode )
311+ require .Nil (t , resp .TLS )
312+ })
313+
314+ t .Run ("HTTPS enabled creates TLS listener with correct certificate" , func (t * testing.T ) {
315+ ss := selfsigned .NewSelfSignedCert (t )
316+ s := openService (t , httpd.Config {
317+ BindAddress : "localhost:" ,
318+ HTTPSEnabled : true ,
319+ HTTPSCertificate : ss .CertPath ,
320+ HTTPSPrivateKey : ss .KeyPath ,
321+ })
322+
323+ requireServingCertSerial (t , s , loadCertSerial (t , ss .CertPath , ss .KeyPath ))
324+ })
325+
326+ t .Run ("HTTPS enabled with missing certificate fails Open" , func (t * testing.T ) {
327+ s := httpd .NewService (httpd.Config {
328+ BindAddress : "localhost:" ,
329+ HTTPSEnabled : true ,
330+ HTTPSCertificate : "/nonexistent/cert.pem" ,
331+ HTTPSPrivateKey : "/nonexistent/key.pem" ,
332+ })
333+ s .WithLogger (zap .NewNop ())
334+
335+ err := s .Open ()
336+ require .Error (t , err )
337+ require .ErrorContains (t , err , "error creating TLS manager" )
338+ })
339+
340+ t .Run ("HTTPS enabled applies base TLS config MinVersion" , func (t * testing.T ) {
341+ ss := selfsigned .NewSelfSignedCert (t )
342+ s := openService (t , httpd.Config {
343+ BindAddress : "localhost:" ,
344+ HTTPSEnabled : true ,
345+ HTTPSCertificate : ss .CertPath ,
346+ HTTPSPrivateKey : ss .KeyPath ,
347+ TLS : & tls.Config {MinVersion : tls .VersionTLS13 },
348+ })
349+
350+ pingURI := fmt .Sprintf ("https://%s/ping" , s .Addr ())
351+
352+ // A client restricted to TLS 1.2 should fail the handshake.
353+ tls12Client := & http.Client {Transport : & http.Transport {
354+ TLSClientConfig : & tls.Config {
355+ InsecureSkipVerify : true ,
356+ MinVersion : tls .VersionTLS12 ,
357+ MaxVersion : tls .VersionTLS12 ,
358+ },
359+ }}
360+ _ , err := tls12Client .Get (pingURI )
361+ require .Error (t , err , "TLS 1.2 client should be rejected when server requires TLS 1.3" )
362+
363+ // A client using TLS 1.3 should succeed.
364+ resp := pingHTTPS (t , s .Addr (), insecureHTTPSClient ())
365+ require .Equal (t , http .StatusNoContent , resp .StatusCode )
366+ require .Equal (t , uint16 (tls .VersionTLS13 ), resp .TLS .Version )
367+ })
368+
369+ t .Run ("insecureCert=false rejects permissive key file permissions" , func (t * testing.T ) {
370+ ss := selfsigned .NewSelfSignedCert (t )
371+ require .NoError (t , os .Chmod (ss .KeyPath , 0644 ))
372+
373+ s := httpd .NewService (httpd.Config {
374+ BindAddress : "localhost:" ,
375+ HTTPSEnabled : true ,
376+ HTTPSCertificate : ss .CertPath ,
377+ HTTPSPrivateKey : ss .KeyPath ,
378+ HTTPSInsecureCertificate : false ,
379+ })
380+ s .WithLogger (zap .NewNop ())
381+
382+ err := s .Open ()
383+ require .Error (t , err , "Open should fail when key file permissions are too permissive" )
384+ require .ErrorContains (t , err , "error creating TLS manager" )
385+ })
386+
387+ t .Run ("insecureCert=true allows permissive key file permissions" , func (t * testing.T ) {
388+ ss := selfsigned .NewSelfSignedCert (t )
389+ require .NoError (t , os .Chmod (ss .KeyPath , 0644 ))
390+
391+ s := openService (t , httpd.Config {
392+ BindAddress : "localhost:" ,
393+ HTTPSEnabled : true ,
394+ HTTPSCertificate : ss .CertPath ,
395+ HTTPSPrivateKey : ss .KeyPath ,
396+ HTTPSInsecureCertificate : true ,
397+ })
398+
399+ resp := pingHTTPS (t , s .Addr (), insecureHTTPSClient ())
400+ require .Equal (t , http .StatusNoContent , resp .StatusCode )
401+ require .NotNil (t , resp .TLS )
402+ })
403+
404+ t .Run ("HTTPS enabled with combined cert and key file" , func (t * testing.T ) {
405+ ss := selfsigned .NewSelfSignedCert (t , selfsigned .WithCombinedFile ())
406+ s := openService (t , httpd.Config {
407+ BindAddress : "localhost:" ,
408+ HTTPSEnabled : true ,
409+ HTTPSCertificate : ss .CertPath ,
410+ HTTPSPrivateKey : "" ,
411+ })
412+
413+ resp := pingHTTPS (t , s .Addr (), insecureHTTPSClient ())
414+ require .Equal (t , http .StatusNoContent , resp .StatusCode )
415+ require .NotNil (t , resp .TLS )
416+ require .NotEmpty (t , resp .TLS .PeerCertificates )
417+ })
418+ }
0 commit comments