From 215029534eb5816b97cc27fbf36dc9920fa15d98 Mon Sep 17 00:00:00 2001 From: Rob Best Date: Wed, 27 Mar 2019 18:10:35 +0000 Subject: [PATCH] Improve tests and remove reliance on external websites --- ssl_exporter_test.go | 569 ++++++++++++++++++++++++++++----- test/badssl.com-client-key.pem | 30 -- test/badssl.com-client.pem | 27 -- 3 files changed, 486 insertions(+), 140 deletions(-) delete mode 100644 test/badssl.com-client-key.pem delete mode 100644 test/badssl.com-client.pem diff --git a/ssl_exporter_test.go b/ssl_exporter_test.go index 76f0a2c..5d46844 100644 --- a/ssl_exporter_test.go +++ b/ssl_exporter_test.go @@ -3,115 +3,518 @@ package main import ( "crypto/tls" "crypto/x509" - "encoding/pem" "fmt" - "io/ioutil" + "log" "net/http" "net/http/httptest" - "regexp" "strings" "testing" ) -func TestProbeHandler(t *testing.T) { - certContent, err := ioutil.ReadFile("test/badssl.com-client.pem") - if err != nil { - t.Fatalf("Can't read test client certificate from disk") - } +var clientCert = `-----BEGIN CERTIFICATE----- +MIIC6jCCApCgAwIBAgIQPbn1oJJ0lvHOxk3BbnhGMTAKBggqhkjOPQQDAjCBhTEL +MAkGA1UEBhMCR0IxEDAOBgNVBAgTB0VuZ2xhbmQxDzANBgNVBAcTBkxvbmRvbjEU +MBIGA1UECRMLMTIzIEZha2UgU3QxEDAOBgNVBBETB1NXMThYWFgxEzARBgNVBAoT +CnJpYmJ5YmliYnkxFjAUBgNVBAMTDXJpYmJ5YmliYnkubWUwHhcNMTkwMzI5MDc1 +MjI5WhcNMjAwMzI4MDc1MjI5WjAdMRswGQYDVQQDExJjZXJ0LnJpYmJ5YmliYnku +bWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASlHGGsAAEMpyBVkgSZazMcYmHH +4K8+m9VI9nSnD4t1b01jYuNAsJjvnRI2iGLOxQ1i8KgzgeZz6ud1mJLIudTzo4IB +RzCCAUMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjAMBgNVHRMBAf8EAjAAMGgGA1UdDgRhBF9mNzphMzo4NDo0ZDo0NjowOTpl +Nzo5ZDpiNzo3MjphMTo5ZTpkOTpjMDoxYTpmYzpjMzplODplZDozOTozMTo5Mzox +MjpmMDplZTowODo2YTo2Mzo3NzphNjplMDoyMjBqBgNVHSMEYzBhgF8xNTpkZDo0 +MTo4ODoxODo0YjoxOTo2NToyYjo2ZTo0Njo1NTozZTo3MTo0MzpjYjphMjo3Nzpk +YzpiNTpjZToxMTpiZTo2NDo3ODo3Zjo1OTo2NzpiYTpmMDo0YTowNTAuBgNVHREE +JzAlghJjZXJ0LnJpYmJ5YmliYnkubWWCCWxvY2FsaG9zdIcEfwAAATAKBggqhkjO +PQQDAgNIADBFAiEAq5AUjiAQxMy0g0f2KyFshTu5QPXXSPo+VTBSQcYuEzICIAWr +JxpZXB4hH2+sEZ4z+bH6l47wbYqOT02d/VNbk3vw +-----END CERTIFICATE-----` - keyContent, err := ioutil.ReadFile("test/badssl.com-client-key.pem") - if err != nil { - t.Fatalf("Can't read test client certificate key from disk") - } +var clientKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPfP8yJatMwUfCyNdIQiQANO2vd3QQIoHJ6g+o8kb7PJoAoGCCqGSM49 +AwEHoUQDQgAEpRxhrAABDKcgVZIEmWszHGJhx+CvPpvVSPZ0pw+LdW9NY2LjQLCY +750SNohizsUNYvCoM4Hmc+rndZiSyLnU8w== +-----END EC PRIVATE KEY-----` - keyBlock, _ := pem.Decode(keyContent) +var clientCertWrong = `-----BEGIN CERTIFICATE----- +MIIC7zCCApWgAwIBAgIRAPx4XNhgs5QfvE6FHnYa3uQwCgYIKoZIzj0EAwIwgYUx +CzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24x +FDASBgNVBAkTCzEyMyBGYWtlIFN0MRAwDgYDVQQREwdTVzE4WFhYMRMwEQYDVQQK +EwpyaWJieWJpYmJ5MRYwFAYDVQQDEw1yaWJieWJpYmJ5Lm1lMB4XDTE5MDMyNzE2 +MTgzOVoXDTIwMDMyNjE2MTgzOVowHzEdMBsGA1UEAxMUY2xpZW50LnJpYmJ5Ymli +YnkubWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQtlqtCTzZNCdDiMHKD/p1F +97/I1MnkRK+QdUxEDnRhHAuMOhypxJ6NruZz+wXLnJEmUYmTsHkz1a4tKz2YJCUp +o4IBSTCCAUUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr +BgEFBQcDATAMBgNVHRMBAf8EAjAAMGgGA1UdDgRhBF9kYzowNDozMjo0ZTpkOTo4 +YjphNTplMDpmNjo5MjpkYzpiYzoxOTo1NTo0ZDo0YjpiNTo5YTo5OTpjYjo4Zjoz +ZjplMTpkNzo3MDoyMTo2MzpmZDo4YTo4MDpjMzpiNzBqBgNVHSMEYzBhgF82YTo0 +MDozNTowZjpmZTowMjpkNzo0Zjo5ODozZTo3ODoyMTpjMDo0YTo5YzpjZTo2Nzoz +NDpiZDo4MjowYTo3MjpkMzpjOTo3Njo5MDo3Nzo5ODpmMDo2NTpmYzpkMDAwBgNV +HREEKTAnghRjbGllbnQucmliYnliaWJieS5tZYIJbG9jYWxob3N0hwR/AAABMAoG +CCqGSM49BAMCA0gAMEUCIQCa7ru0f0/HVoGa7aBJqACMBfiXWCI159WGt2B7Mxvf +VAIgX9O8fOl6qmsJyfMkfdmv6lo9oAWIecDLpVtqEj5i2Qc= +-----END CERTIFICATE-----` - keyBlockDecrypted, err := x509.DecryptPEMBlock(keyBlock, []byte("badssl.com")) - if err != nil { - t.Fatalf("Issue decrypting test client key") - } +var clientKeyWrong = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILnEJttULi+2cupO4ta6IB9bEeul6rMGFSpPMB7kPuSwoAoGCCqGSM49 +AwEHoUQDQgAELZarQk82TQnQ4jByg/6dRfe/yNTJ5ESvkHVMRA50YRwLjDocqcSe +ja7mc/sFy5yRJlGJk7B5M9WuLSs9mCQlKQ== +-----END EC PRIVATE KEY-----` - keyContent = pem.EncodeToMemory(&pem.Block{Type: keyBlock.Type, Bytes: keyBlockDecrypted}) +var serverCert = `-----BEGIN CERTIFICATE----- +MIIC6jCCApGgAwIBAgIRAO+sgyd/vcnDgfmafkgALKwwCgYIKoZIzj0EAwIwgYUx +CzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24x +FDASBgNVBAkTCzEyMyBGYWtlIFN0MRAwDgYDVQQREwdTVzE4WFhYMRMwEQYDVQQK +EwpyaWJieWJpYmJ5MRYwFAYDVQQDEw1yaWJieWJpYmJ5Lm1lMB4XDTE5MDMyOTA3 +NTIyN1oXDTIwMDMyODA3NTIyN1owHTEbMBkGA1UEAxMSY2VydC5yaWJieWJpYmJ5 +Lm1lMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY5nQFSmpZnFvjbAicuElYlT2 +xQvO+LgYt+5bcGfemT5HRq63tljiGlsyNXAysAmMwT9+blu8sLqkyh6PMFesJ6OC +AUcwggFDMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfZmI6NDM6NWY6M2Y6NTE6NGI6 +NjA6YTI6YzQ6NzI6ZjE6MGQ6OTM6ZDA6YjQ6ODA6N2Y6Mjc6NjM6Yjk6NWI6NTQ6 +ZGQ6NzI6NzU6N2Q6MDU6N2U6ZTc6Y2U6OTM6YTMwagYDVR0jBGMwYYBfMTU6ZGQ6 +NDE6ODg6MTg6NGI6MTk6NjU6MmI6NmU6NDY6NTU6M2U6NzE6NDM6Y2I6YTI6Nzc6 +ZGM6YjU6Y2U6MTE6YmU6NjQ6Nzg6N2Y6NTk6Njc6YmE6ZjA6NGE6MDUwLgYDVR0R +BCcwJYISY2VydC5yaWJieWJpYmJ5Lm1lgglsb2NhbGhvc3SHBH8AAAEwCgYIKoZI +zj0EAwIDRwAwRAIgI6w7Px0UnI3AAP4n9ApO1gNIhY+ECEb0EZvKopmNUn0CIHN4 +MEaXLzEfNdNi7E521qIR+bhV/mu8nubZIsG4K383 +-----END CERTIFICATE-----` - emptyRootCAs := x509.NewCertPool() +var serverKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAeLgH2jonGdCgdG1MpEy9wAgxvCSC4N7sK3hC0GZM7MoAoGCCqGSM49 +AwEHoUQDQgAEY5nQFSmpZnFvjbAicuElYlT2xQvO+LgYt+5bcGfemT5HRq63tlji +GlsyNXAysAmMwT9+blu8sLqkyh6PMFesJw== +-----END EC PRIVATE KEY-----` - certificate, err := tls.X509KeyPair(certContent, keyContent) +var expiredCert = `-----BEGIN CERTIFICATE----- +MIIC2DCCAn6gAwIBAgIQeP4wyiBMCZ5TLpM40Ho6UzAKBggqhkjOPQQDAjCBhTEL +MAkGA1UEBhMCR0IxEDAOBgNVBAgTB0VuZ2xhbmQxDzANBgNVBAcTBkxvbmRvbjEU +MBIGA1UECRMLMTIzIEZha2UgU3QxEDAOBgNVBBETB1NXMThYWFgxEzARBgNVBAoT +CnJpYmJ5YmliYnkxFjAUBgNVBAMTDXJpYmJ5YmliYnkubWUwHhcNMTkwMzI5MDgw +MTM4WhcNMTkwMzI4MDgwMTM4WjAdMRswGQYDVQQDExJjZXJ0LnJpYmJ5YmliYnku +bWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjDs0ehi0miAKmDnuCmRyWaKOY ++h0MugoFngChyygYCY+mOb/+HV5AYUEf1NFJLz4DtYnNKyWNHnX7vUPEh+Ico4IB +NTCCATEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjAMBgNVHRMBAf8EAjAAMGgGA1UdDgRhBF9mNTo1NDpmYzphNTo1ZjplMzo5 +YTo3MzplNzo1YTo0ZDowNzo0MTo4YjoyOTo2ZDpiNzpiNTpjMDpiZjowMzpkZTo5 +Zjo5NTozNzphMjphNDo4MDo2YTo3MDozNDpmNjBqBgNVHSMEYzBhgF9iOTpjMDo2 +NzoyYjo2YTpiNzowMToyMjo2Zjo1NTplMjpiMDphNDoyNDo1YTo5NzplMzpjYzpi +MTo3Yjo4ZjoyNDpiNTo1NToxYzpiMDo3NTozMDplNToxZDo3OTpmZDAcBgNVHREE +FTATggCCCWxvY2FsaG9zdIcEfwAAATAKBggqhkjOPQQDAgNIADBFAiB+ZGtScM5Y +QHra5d+lqFRJOd7WXkoU03QHWOP3pSqbCAIhAJreqVQ3dUME4j9LYbQWmD96agdL +2uxG31qfCa/T5TCq +-----END CERTIFICATE-----` + +var expiredKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIFDlw65IF8NLdgIWU1ipkMffcE6MgZ5DHTGzf0WN09EJoAoGCCqGSM49 +AwEHoUQDQgAEow7NHoYtJogCpg57gpkclmijmPodDLoKBZ4AocsoGAmPpjm//h1e +QGFBH9TRSS8+A7WJzSsljR51+71DxIfiHA== +-----END EC PRIVATE KEY-----` + +var caCert = `-----BEGIN CERTIFICATE----- +MIIDBjCCAqygAwIBAgIRAJxzFmvhp8ef68W7SQrt5KwwCgYIKoZIzj0EAwIwgYUx +CzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24x +FDASBgNVBAkTCzEyMyBGYWtlIFN0MRAwDgYDVQQREwdTVzE4WFhYMRMwEQYDVQQK +EwpyaWJieWJpYmJ5MRYwFAYDVQQDEw1yaWJieWJpYmJ5Lm1lMB4XDTE5MDMyOTA3 +NTIyMloXDTI0MDMyNzA3NTIyMlowgYUxCzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdF +bmdsYW5kMQ8wDQYDVQQHEwZMb25kb24xFDASBgNVBAkTCzEyMyBGYWtlIFN0MRAw +DgYDVQQREwdTVzE4WFhYMRMwEQYDVQQKEwpyaWJieWJpYmJ5MRYwFAYDVQQDEw1y +aWJieWJpYmJ5Lm1lMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE94APL4adMA7A +tSSfxcHzzxdVBCwJju6jVCf5qRqG4Qz0neXlde6jIXocZvoboZJiA2e7BadnjoPN +2sTB8mgg4KOB+jCB9zAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zBo +BgNVHQ4EYQRfMTU6ZGQ6NDE6ODg6MTg6NGI6MTk6NjU6MmI6NmU6NDY6NTU6M2U6 +NzE6NDM6Y2I6YTI6Nzc6ZGM6YjU6Y2U6MTE6YmU6NjQ6Nzg6N2Y6NTk6Njc6YmE6 +ZjA6NGE6MDUwagYDVR0jBGMwYYBfMTU6ZGQ6NDE6ODg6MTg6NGI6MTk6NjU6MmI6 +NmU6NDY6NTU6M2U6NzE6NDM6Y2I6YTI6Nzc6ZGM6YjU6Y2U6MTE6YmU6NjQ6Nzg6 +N2Y6NTk6Njc6YmE6ZjA6NGE6MDUwCgYIKoZIzj0EAwIDSAAwRQIhANycTcKTH1DU +eu3Xuz8CdtgT67yqUTxDy0O5kS8fFPUVAiAV0u1M7dQYV+buY8oOLYnZxondrb7/ +BNltD7A8Y0S0hw== +-----END CERTIFICATE-----` + +// Test the basic case: a typical HTTPS server +func TestProbeHandlerConnectSuccess(t *testing.T) { + server, err := server() if err != nil { t.Fatalf(err.Error()) } - // Test the behaviour of various target URIs - // 'ok' denotes whether we expect a succesful tls connection - cases := []struct { - uri string - ok bool - tlsConfig *tls.Config - }{ - // Test against an assumed valid, reachable and functioning HTTPS address - {uri: "google.com:443", ok: true, tlsConfig: &tls.Config{}}, - // Test against a HTTP address - {uri: "google.com:80", ok: false, tlsConfig: &tls.Config{}}, - // Test against an expired certificate when we're rejecting invalid certs - {uri: "expired.badssl.com:443", ok: false, tlsConfig: &tls.Config{}}, - // Test against an expired certificate when we're accepting invalid certs - {uri: "expired.badssl.com:443", ok: true, tlsConfig: &tls.Config{InsecureSkipVerify: true}}, - // Test against a target with no port - {uri: "google.com", ok: true, tlsConfig: &tls.Config{}}, - // Test against a string with spaces - {uri: "with spaces", ok: false, tlsConfig: &tls.Config{}}, - // Test against nothing - {uri: "", ok: false, tlsConfig: &tls.Config{}}, - // Test with client authentication - {uri: "client.badssl.com:443", ok: true, tlsConfig: &tls.Config{Certificates: []tls.Certificate{certificate}}}, - // Test with an empty root CA bundle - {uri: "google.com:443", ok: false, tlsConfig: &tls.Config{RootCAs: emptyRootCAs}}, - // 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{}}, - } - - 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.") - - successMetricRegexp, err := regexp.Compile("(ssl_tls_connect_success [0-1])") + rr, err := probe(server.URL) if err != nil { - t.Fatalf("Error compiling success metric: " + err.Error()) + t.Fatalf(err.Error()) } - for _, test := range cases { + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 1`") + } - uri := "/probe?target=" + test.uri - req, err := http.NewRequest("GET", uri, nil) - if err != nil { - t.Fatal(err) - } + server.Close() +} - rr := httptest.NewRecorder() - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - probeHandler(w, r, test.tlsConfig) - }) +// Test against a non-existent server +func TestProbeHandlerConnectSuccessFalse(t *testing.T) { + rr, err := probe("localhost:6666") + if err != nil { + t.Fatalf(err.Error()) + } - handler.ServeHTTP(rr, req) + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") + } - // 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) - } +} - // Make sure we're getting the ssl_tls_connect_success metric back - if !successMetricRegexp.MatchString(rr.Body.String()) { - t.Errorf("can't find ssl_tls_connect_success metric in response body w/ %q", uri) - } +// Test with an empty target +func TestProbeHandlerEmptyTarget(t *testing.T) { + rr, err := probe("") + if err != nil { + t.Fatalf(err.Error()) + } - // 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") - if test.ok && !ok { - t.Errorf("expected tls connection to succeed but it failed w/ %q", uri) - } - if !test.ok && ok { - t.Errorf("expected tls connection to fail but it succeeded w/ %q", uri) - } + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") + } +} + +// Test with spaces in the target +func TestProbeHandlerSpaces(t *testing.T) { + rr, err := probe("with spaces") + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") } } + +// Test against a HTTP server +func TestProbeHandlerHTTP(t *testing.T) { + server, err := serverHTTP() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probe(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") + } + + server.Close() +} + +// Test that the exporter returns the correct list of IPs +func TestProbeHandlerIPs(t *testing.T) { + server, err := server() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probe(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_cert_subject_alternative_ips{ips=\",127.0.0.1,\"") + if !ok { + t.Errorf("expected `ssl_cert_subject_alternative_ips{ips=\",127.0.0.1,\"`") + } + + server.Close() +} + +// Test that the exporter returns the correct CN +func TestProbeHandlerCommonName(t *testing.T) { + server, err := server() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probe(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + log.Println(rr.Body.String()) + ok := strings.Contains(rr.Body.String(), "ssl_cert_subject_common_name{issuer_cn=\"ribbybibby.me\",serial_no=\"318581226177353336430613662595136105644\",subject_cn=\"cert.ribbybibby.me\"} 1") + if !ok { + t.Errorf("expected `ssl_cert_subject_common_name{issuer_cn=\"ribbybibby.me\",serial_no=\"318581226177353336430613662595136105644\",subject_cn=\"cert.ribbybibby.me\"} 1`") + } + + server.Close() +} + +// Test that the exporter returns the correct list of DNS names +func TestProbeHandlerDNSNames(t *testing.T) { + server, err := server() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probe(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_cert_subject_alternative_dnsnames{dnsnames=\",cert.ribbybibby.me,localhost,\"") + if !ok { + t.Errorf("expected `ssl_cert_subject_alternative_dnsnames{dnsnames=\",cert.ribbybibby.me,localhost,\"`") + } + + server.Close() +} + +// Test client authentication +func TestProbeHandlerClientAuth(t *testing.T) { + server, err := serverClientAuth() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probeClientAuth(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 1`") + } + + server.Close() +} + +// Test client authentication with a bad client certificate +func TestProbeHandlerClientAuthWrongClientCert(t *testing.T) { + server, err := serverClientAuth() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probeClientAuthBad(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") + } + + server.Close() +} + +// Test against a server with an expired certificate +func TestProbeHandlerExpired(t *testing.T) { + server, err := serverExpired() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probe(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 0") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 0`") + } + + server.Close() +} + +// Test against a server with an expired certificate with an insecure probe +func TestProbeHandlerExpiredInsecure(t *testing.T) { + server, err := serverExpired() + if err != nil { + t.Fatalf(err.Error()) + } + + rr, err := probeInsecure(server.URL) + if err != nil { + t.Fatalf(err.Error()) + } + + ok := strings.Contains(rr.Body.String(), "ssl_tls_connect_success 1") + if !ok { + t.Errorf("expected `ssl_tls_connect_success 1`") + } + + server.Close() +} + +func probe(url string) (*httptest.ResponseRecorder, error) { + uri := "/probe?target=" + url + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + probeHandler(w, r, &tls.Config{ + RootCAs: certPool(), + }) + }) + + handler.ServeHTTP(rr, req) + + return rr, nil +} + +func probeInsecure(url string) (*httptest.ResponseRecorder, error) { + uri := "/probe?target=" + url + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + probeHandler(w, r, &tls.Config{ + RootCAs: certPool(), + InsecureSkipVerify: true, + }) + }) + + handler.ServeHTTP(rr, req) + + return rr, nil +} + +func probeClientAuth(url string) (*httptest.ResponseRecorder, error) { + clientCertificate, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) + if err != nil { + return nil, err + } + + uri := "/probe?target=" + url + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + probeHandler(w, r, &tls.Config{ + Certificates: []tls.Certificate{clientCertificate}, + RootCAs: certPool(), + }) + }) + + handler.ServeHTTP(rr, req) + + return rr, nil +} + +func probeClientAuthBad(url string) (*httptest.ResponseRecorder, error) { + clientCertificate, err := tls.X509KeyPair([]byte(clientCertWrong), []byte(clientKeyWrong)) + if err != nil { + return nil, err + } + + uri := "/probe?target=" + url + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + probeHandler(w, r, &tls.Config{ + Certificates: []tls.Certificate{clientCertificate}, + RootCAs: certPool(), + }) + }) + + handler.ServeHTTP(rr, req) + + return rr, nil +} + +func server() (*httptest.Server, error) { + serverCertificate, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey)) + if err != nil { + return nil, err + } + + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello world") + })) + + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{serverCertificate}, + } + + server.StartTLS() + return server, nil +} + +func serverClientAuth() (*httptest.Server, error) { + certPool := certPool() + + serverCertificate, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey)) + if err != nil { + return nil, err + } + + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello world") + })) + + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{serverCertificate}, + ClientAuth: tls.RequireAndVerifyClientCert, + RootCAs: certPool, + ClientCAs: certPool, + } + + server.StartTLS() + return server, nil +} + +func serverExpired() (*httptest.Server, error) { + certPool := certPool() + + serverCertificate, err := tls.X509KeyPair([]byte(expiredCert), []byte(expiredKey)) + if err != nil { + return nil, err + } + + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello world") + })) + + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{serverCertificate}, + RootCAs: certPool, + ClientCAs: certPool, + } + + server.StartTLS() + return server, nil +} + +func serverHTTP() (*httptest.Server, error) { + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello world") + })) + + server.Start() + return server, nil +} + +func certPool() *x509.CertPool { + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM([]byte(caCert)) + return certPool +} diff --git a/test/badssl.com-client-key.pem b/test/badssl.com-client-key.pem deleted file mode 100644 index 39c929d..0000000 --- a/test/badssl.com-client-key.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,8906E5B87ECB8682 - -cY/EOwDILOiCPzyqZanlrhrb5h+fCnS/mHhsNme0Zyrk9udaBZKwxkPtUC6rJZ9/ -rX4HQch9tuVy992CDe1bl4l+3EfqshUhxtivG8GHiFj7TNo5Ia9LaE+KwugB2rdX -J8odwWmwE8Y9TX09WyPKHikjg2nAOgN3Gf+GeJW6fL9xcEVmIgDyX6kBusQYWIQ3 -PMYqyaBhL8UCZVRpiUINTrJXCnNAI4t6K0i4J+hjyekRnA5PQ+A/0+C9J+kQW+wj -uDWkQtyyBNsscGhXrL9ita7/UtJa+/aS99FLTnxD+fqyU4svzNIQWmZBcIxb9Lxq -LHcxNaQCtFycD1iU8Tsq9HY1apKrdPBjaaYne6qCg8nIPWpo3gWoffu+R3EfVcdZ -lNgKL3Cd1oCJy/Vcz+u0iayCPT0zfP1dLlGpL2U7L7+8pJ88FbXt/F1th9QEUp5z -dISg/6ukHyIkfXwoxYXkin9fW2clo99+fuXkJaVPEOnRmr0+kf+1S0Uw8fkete4v -f0IUxUwydxCXFN2ZFZ6o/LFLMLRfNAqGSGjqGeY1L0ILkJPo3KBqlmUhl2FIROOW -4rSrfpujjsOqlPncJ9apW04RnqhY7t0YOtp0rMK+gIDteKD+utCyh+UAetiiqTiB -ZDup/kzPNDClSDU6cgRbZUf/Nt8RiBcVeX4TfDb1eEhqV0BTHrLOBo8yp6ETDlJM -CKO5i9kb5fUaFPFD5VTc5rnY4qV/hUj/uyAVK757A+m9nn7fYOMeaRAgOEvChYSf -Qam0VPgpJxfjsaw0BFOMyfsHNJqvTbPXA+hIKD3fhP6jNcgpKTLHJp89J4XUOeIR -tvh/u7vljhVygP7ZQOpCoX1xSMdJMO7Qhrh3O/2fq/EJertJD2PQ0Zgqb8wosHn/ -Aw9VWT6849jL55Xd5l3zXmI9vU0Le4HP3NstV9jjpcp91dU9yfQpcuIo8U26u+4r -LR1VmhZJjo7FBOpyJZ1Jb3vyp+nPI+tH214DhM9LuXvMbf3ORhXTMOlqSABUmo/+ -t+QjVfcEuhFR9CTVWZUIXflJk/euvzqTQdm8iz7JuFzQOhoXjiPIq0GqtCk10SeL -zqHz1s0TZNcrZyzkmiHuWjGVwHN/XZA3dW67uj522hD7EzucKE9CoCdJ3f4JEWmS -CQwEbba6SKAR6iBlouIYLVdkOBgimGiF13rCwvdN234hQeJT8Wc87iG+uDw73PJL -+amDglATH4wIpBk4xmjh/GTRDK0yH9jp7Dv9iwShbjk0yOuoz5yDn8VPqRIREn8d -9sAaiFUUQ/9XrdlA5F+49OznClDWLKHK8sSAAFyrzvoCcqseSKbvLyrlHGT0fiot -obgDu/W+K2xEOjQeaIyVI5J1qOi6k78fyv1vutjEs6mcTRtDAIxi+V5y5lXUEj0v -OWYsbp9yb8Yq602vV8UYSROd+1xdE+7Td3ENLYE7MnVqju7a5NRfnZYgAU03NgIf -nHGFZC6/tMz/PXS+D0dqzXxwEjH5JQzGBjvSQHK09gHtCfcyshMQQWtXZGQViZX8 -QdYXiaq67nJex0DjWTt56a4EgsdYC1J28bJ3GAkrWNkDFRmlx49zvA== ------END RSA PRIVATE KEY----- diff --git a/test/badssl.com-client.pem b/test/badssl.com-client.pem deleted file mode 100644 index 90fb671..0000000 --- a/test/badssl.com-client.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEnTCCAoWgAwIBAgIJAPC7KMFjfslXMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV -BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp -c2NvMQ8wDQYDVQQKDAZCYWRTU0wxMTAvBgNVBAMMKEJhZFNTTCBDbGllbnQgUm9v -dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTcxMTE2MDUzNjMzWhcNMTkxMTE2 -MDUzNjMzWjBvMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG -A1UEBwwNU2FuIEZyYW5jaXNjbzEPMA0GA1UECgwGQmFkU1NMMSIwIAYDVQQDDBlC -YWRTU0wgQ2xpZW50IENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAxzdfEeseTs/rukjly6MSLHM+Rh0enA3Ai4Mj2sdl31x3SbPoen08 -utVhjPmlxIUdkiMG4+ffe7N+JtDLG75CaxZp9CxytX7kywooRBJsRnQhmQPca8MR -WAJBIz+w/L+3AFkTIqWBfyT+1VO8TVKPkEpGdLDovZOmzZAASi9/sj+j6gM7AaCi -DeZTf2ES66abA5pOp60Q6OEdwg/vCUJfarhKDpi9tj3P6qToy9Y4DiBUhOct4MG8 -w5XwmKAC+Vfm8tb7tMiUoU0yvKKOcL6YXBXxB2kPcOYxYNobXavfVBEdwSrjQ7i/ -s3o6hkGQlm9F7JPEuVgbl/Jdwa64OYIqjQIDAQABoy0wKzAJBgNVHRMEAjAAMBEG -CWCGSAGG+EIBAQQEAwIHgDALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggIB -AKpzk1ZTunWuof3DIer2Abq7IV3STGeFaoH4TuHdSbmXwC0KuPkv7wVPgPekyRaH -b9CBnsreRF7eleD1M63kakhdnA1XIbdJw8sfSDlKdI4emmb4fzdaaPxbrkQ5IxOB -QDw5rTUFVPPqFWw1bGP2zrKD1/i1pxUtGM0xem1jR7UZYpsSPs0JCOHKZOmk8OEW -Uy+Jp4gRzbMLZ0TrvajGEZXRepjOkXObR81xZGtvTNP2wl1zm13ffwIYdqJUrf1H -H4miU9lVX+3/Z+2mVHBWhzBgbTmo06s3uwUE6JsxUGm2/w4NNblRit0uQcGw7ba8 -kl2d5rZQscFsqNFz2vRjj1G0dO8S3owmuF0izZO9Fqvq0jB6oaUkxcAcTKFSjs2z -wy1oy+cu8iO3GRbfAW7U0xzGp9MnkdPS5dHzvhod3/DK0YVskfxZF7M8GhkjT7Qm -2EUBQNNMNXC3g/GXTdXOgqqjW5GXahI8Z6Q4OYN6xZwuEhizwKkgojwaww2YgYT9 -MJXciJZWr3QXvFdBH7m0zwpKgQ1wm6j3yeyuRphq2lEtU3OQl55A3tXtvqyMXsxk -xMCCNQdmKQt0WYmMS3Xj/AfAY2sjCWziDflvW5mGCUjSYdZ+r3JIIF4m/FNCIO1d -Ioacp9qb0qL9duFlVHtFiPgoKrEdJaNVUL7NG9ppF8pR ------END CERTIFICATE-----