2017-12-02 17:38:30 +02:00
package main
import (
2019-03-18 14:24:28 +02:00
"crypto/tls"
"crypto/x509"
"encoding/pem"
2017-12-02 17:38:30 +02:00
"fmt"
2019-03-18 14:24:28 +02:00
"io/ioutil"
2017-12-02 17:38:30 +02:00
"net/http"
"net/http/httptest"
"regexp"
"strings"
"testing"
)
func TestProbeHandler ( t * testing . T ) {
2019-03-18 14:24:28 +02:00
certContent , err := ioutil . ReadFile ( "test/badssl.com-client.pem" )
if err != nil {
t . Fatalf ( "Can't read test client certificate from disk" )
}
keyContent , err := ioutil . ReadFile ( "test/badssl.com-client-key.pem" )
if err != nil {
t . Fatalf ( "Can't read test client certificate key from disk" )
}
keyBlock , _ := pem . Decode ( keyContent )
keyBlockDecrypted , err := x509 . DecryptPEMBlock ( keyBlock , [ ] byte ( "badssl.com" ) )
if err != nil {
t . Fatalf ( "Issue decrypting test client key" )
}
emptyRootCAs := x509 . NewCertPool ( )
2019-03-24 21:17:24 +02:00
certificate , _ := tls . X509KeyPair ( certContent , keyBlockDecrypted )
if err != nil {
t . Fatalf ( err . Error ( ) )
}
2017-12-02 17:38:30 +02:00
// Test the behaviour of various target URIs
2019-03-24 20:47:16 +02:00
// 'ok' denotes whether we expect a succesful tls connection
2017-12-02 17:38:30 +02:00
cases := [ ] struct {
2019-03-18 14:24:28 +02:00
uri string
ok bool
tlsConfig * tls . Config
2017-12-02 17:38:30 +02:00
} {
// Test against an assumed valid, reachable and functioning HTTPS address
2019-03-20 16:04:24 +02:00
{ uri : "google.com:443" , ok : true , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
// Test against a HTTP address
2019-03-20 16:04:24 +02:00
{ uri : "google.com:80" , ok : false , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
// Test against an expired certificate when we're rejecting invalid certs
2019-03-20 16:04:24 +02:00
{ uri : "expired.badssl.com:443" , ok : false , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
// Test against an expired certificate when we're accepting invalid certs
2019-03-20 16:04:24 +02:00
{ uri : "expired.badssl.com:443" , ok : true , tlsConfig : & tls . Config { InsecureSkipVerify : true } } ,
// Test against a target with no port
2019-03-24 20:47:16 +02:00
{ uri : "google.com" , ok : true , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
// Test against a string with spaces
2019-03-18 14:24:28 +02:00
{ uri : "with spaces" , ok : false , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
// Test against nothing
2019-03-18 14:24:28 +02:00
{ uri : "" , ok : false , tlsConfig : & tls . Config { } } ,
// Test with client authentication
2019-03-20 16:04:24 +02:00
{ uri : "client.badssl.com:443" , ok : true , tlsConfig : & tls . Config { Certificates : [ ] tls . Certificate { certificate } } } ,
2019-03-18 14:24:28 +02:00
// Test with an empty root CA bundle
2019-03-20 16:04:24 +02:00
{ uri : "google.com:443" , ok : false , tlsConfig : & tls . Config { RootCAs : emptyRootCAs } } ,
2019-03-22 14:10:06 +02:00
// Test with a https scheme
{ uri : "https://google.com" , ok : true , tlsConfig : & tls . Config { } } ,
// Test with a https scheme and port
{ uri : "https://google.com:443" , ok : true , tlsConfig : & tls . Config { } } ,
2017-12-02 17:38:30 +02:00
}
fmt . Println ( "Note: The error logs in these tests are expected. One of the important tests is that we return the expected body, even in the face of errors." )
2019-03-24 21:17:24 +02:00
successMetricRegexp , err := regexp . Compile ( "(ssl_tls_connect_success [0-1])" )
if err != nil {
t . Fatalf ( "Error compiling success metric: " + err . Error ( ) )
}
2017-12-02 17:38:30 +02:00
for _ , test := range cases {
uri := "/probe?target=" + test . uri
req , err := http . NewRequest ( "GET" , uri , nil )
if err != nil {
t . Fatal ( err )
}
rr := httptest . NewRecorder ( )
handler := http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2019-03-18 14:24:28 +02:00
probeHandler ( w , r , test . tlsConfig )
2017-12-02 17:38:30 +02:00
} )
handler . ServeHTTP ( rr , req )
// We should always return a 200, no matter what
if status := rr . Code ; status != http . StatusOK {
t . Errorf ( "handler returned wrong status code: got %v want %v" ,
status , http . StatusOK )
}
2019-03-24 20:47:16 +02:00
// Make sure we're getting the ssl_tls_connect_success metric back
2019-03-24 21:17:24 +02:00
if ! successMetricRegexp . MatchString ( rr . Body . String ( ) ) {
2019-03-24 20:47:16 +02:00
t . Errorf ( "can't find ssl_tls_connect_success metric in response body w/ %q" , uri )
2017-12-02 17:38:30 +02:00
}
2019-03-24 20:47:16 +02:00
// Make sure we're getting the result we expect from ssl_tls_connect_success
ok := strings . Contains ( rr . Body . String ( ) , "ssl_tls_connect_success 1" )
2017-12-02 17:38:30 +02:00
if test . ok && ! ok {
2019-03-24 20:47:16 +02:00
t . Errorf ( "expected tls connection to succeed but it failed w/ %q" , uri )
2017-12-02 17:38:30 +02:00
}
if ! test . ok && ok {
2019-03-24 20:47:16 +02:00
t . Errorf ( "expected tls connection to fail but it succeeded w/ %q" , uri )
2017-12-02 17:38:30 +02:00
}
}
}