func generateServingCertFiles(
	caCert *x509.Certificate,
	caPrivateKey *ecdsa.PrivateKey,
	certFilepath string,
	keyFilepath string,
) (*x509.Certificate, *ecdsa.PrivateKey, error) {
	certTemplate := &x509.Certificate{
		SerialNumber: big.NewInt(456),
		Subject: pkix.Name{
			Organization: []string{"Serving Certificate"},
		},
		NotBefore:   time.Now(),
		NotAfter:    time.Now().Add(5 * time.Minute),
		KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
	}
	cert, privateKey, err := generateCertFiles(
		certTemplate,
		caCert,
		caPrivateKey,
		certFilepath,
		keyFilepath,
	)
	if err != nil {
		return nil, nil, err
	}
	return cert, privateKey, nil
}
generateServingCertFiles creates a new certificate that a client will check against its
certificate authority to verify the server. The certificate is signed by a certificate
authority.
package cortex
import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"net"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strings"
	"testing"
	"time"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)
func TestAuthentication(t *testing.T) {
	tests := []struct {
		testName                      string
		basicAuth                     map[string]string
		basicAuthPasswordFileContents []byte
		bearerToken                   string
		bearerTokenFile               string
		bearerTokenFileContents       []byte
		expectedAuthHeaderValue       string
		expectedError                 error
	}{
		{
			testName: "Basic Auth with password",
			basicAuth: map[string]string{
				"username": "TestUser",
				"password": "TestPassword",
			},
			expectedAuthHeaderValue: "Basic " + base64.StdEncoding.EncodeToString(
				[]byte("TestUser:TestPassword"),
			),
			expectedError: nil,
		},
		{
			testName: "Basic Auth with password file",
			basicAuth: map[string]string{
				"username":      "TestUser",
				"password_file": "passwordFile",
			},
			basicAuthPasswordFileContents: []byte("TestPassword"),
			expectedAuthHeaderValue: "Basic " + base64.StdEncoding.EncodeToString(
				[]byte("TestUser:TestPassword"),
			),
			expectedError: nil,
		},
		{
			testName: "Basic Auth with bad password file",
			basicAuth: map[string]string{
				"username":      "TestUser",
				"password_file": "missingPasswordFile",
			},
			expectedAuthHeaderValue: "",
			expectedError:           ErrFailedToReadFile,
		},
		{
			testName:                "Bearer Token",
			bearerToken:             "testToken",
			expectedAuthHeaderValue: "Bearer testToken",
			expectedError:           nil,
		},
		{
			testName:                "Bearer Token with bad bearer token file",
			bearerTokenFile:         "missingBearerTokenFile",
			expectedAuthHeaderValue: "",
			expectedError:           ErrFailedToReadFile,
		},
		{
			testName:                "Bearer Token with bearer token file",
			bearerTokenFile:         "bearerTokenFile",
			expectedAuthHeaderValue: "Bearer testToken",
			bearerTokenFileContents: []byte("testToken"),
			expectedError:           nil,
		},
	}
	for _, test := range tests {
		t.Run(test.testName, func(t *testing.T) {
			handler := func(rw http.ResponseWriter, req *http.Request) {
				authHeaderValue := req.Header.Get("Authorization")
				_, err := rw.Write([]byte(authHeaderValue))
				require.NoError(t, err)
			}
			server := httptest.NewServer(http.HandlerFunc(handler))
			defer server.Close()
			if test.basicAuth != nil {
				passwordFile := test.basicAuth["password_file"]
				if passwordFile != "" && test.basicAuthPasswordFileContents != nil {
					filepath := "./" + test.basicAuth["password_file"]
					err := createFile(test.basicAuthPasswordFileContents, filepath)
					require.NoError(t, err)
					defer os.Remove(filepath)
				}
			}
			if test.bearerTokenFile != "" && test.bearerTokenFileContents != nil {
				filepath := "./" + test.bearerTokenFile
				err := createFile(test.bearerTokenFileContents, filepath)
				require.NoError(t, err)
				defer os.Remove(filepath)
			}
			exporter := Exporter{
				Config{
					BasicAuth:       test.basicAuth,
					BearerToken:     test.bearerToken,
					BearerTokenFile: test.bearerTokenFile,
				},
			}
			req, err := http.NewRequest(http.MethodPost, server.URL, nil)
			require.NoError(t, err)
			err = exporter.addHeaders(req)
			if err != nil {
				require.Equal(t, err.Error(), test.expectedError.Error())
			} else {
				require.NoError(t, test.expectedError)
				authHeaderValue := req.Header.Get("Authorization")
				require.Equal(t, authHeaderValue, test.expectedAuthHeaderValue)
			}
		})
	}
}
func createFile(bytes []byte, filepath string) error {
	err := ioutil.WriteFile(filepath, bytes, 0644)
	if err != nil {
		return err
	}
	return nil
}
func TestBuildClient(t *testing.T) {
	testProxyURL, err := url.Parse("123.4.5.6")
	require.NoError(t, err)
	tests := []struct {
		testName               string
		config                 Config
		expectedRemoteTimeout  time.Duration
		expectedErrorSubstring string
	}{
		{
			testName: "Remote Timeout with Proxy URL",
			config: Config{
				ProxyURL:      testProxyURL,
				RemoteTimeout: 123 * time.Second,
				TLSConfig: map[string]string{
					"ca_file":              "./ca_cert.pem",
					"insecure_skip_verify": "0",
				},
			},
			expectedRemoteTimeout:  123 * time.Second,
			expectedErrorSubstring: "proxyconnect tcp",
		},
		{
			testName: "No Timeout or Proxy URL, InsecureSkipVerify is false",
			config: Config{
				TLSConfig: map[string]string{
					"ca_file":              "./ca_cert.pem",
					"insecure_skip_verify": "0",
				},
			},
			expectedErrorSubstring: "",
		},
		{
			testName: "No Timeout or Proxy URL, InsecureSkipVerify is true",
			config: Config{
				TLSConfig: map[string]string{
					"ca_file":              "./ca_cert.pem",
					"insecure_skip_verify": "1",
				},
			},
			expectedErrorSubstring: "",
		},
	}
	for _, test := range tests {
		t.Run(test.testName, func(t *testing.T) {
			handler := func(rw http.ResponseWriter, req *http.Request) {
				fmt.Fprint(rw, "Successfully received HTTP request!")
			}
			server := httptest.NewTLSServer(http.HandlerFunc(handler))
			defer server.Close()
			encodedCACert := server.TLS.Certificates[0].Certificate[0]
			caCertPEM := pem.EncodeToMemory(&pem.Block{
				Type:  "CERTIFICATE",
				Bytes: encodedCACert,
			})
			err := createFile(caCertPEM, "./ca_cert.pem")
			require.NoError(t, err)
			defer os.Remove("ca_cert.pem")
			exporter := Exporter{
				config: test.config,
			}
			client, err := exporter.buildClient()
			require.NoError(t, err)
			assert.Equal(t, client.Timeout, test.expectedRemoteTimeout)
			_, err = client.Get(server.URL)
			if test.expectedErrorSubstring != "" {
				if assert.Error(t, err) {
					hasErrorSubstring := strings.Contains(err.Error(), test.expectedErrorSubstring)
					assert.True(t, hasErrorSubstring, "missing error message")
				}
			} else {
				require.NoError(t, err)
			}
		})
	}
}
func TestMutualTLS(t *testing.T) {
	tests := []struct {
		testName      string
		generateCerts bool
		caCert        string
		caKey         string
		servingCert   string
		servingKey    string
		clientCert    string
		clientKey     string
	}{
		{
			testName:      "Generated ECDSA Certs",
			generateCerts: true,
			caCert:        "ca.crt",
			caKey:         "ca.key",
			servingCert:   "server.crt",
			servingKey:    "server.key",
			clientCert:    "client.crt",
			clientKey:     "client.key",
		},
	}
	for _, test := range tests {
		t.Run(test.testName, func(t *testing.T) {
			if test.generateCerts {
				caCert, caPrivateKey, err := generateCACertFiles(test.caCert, test.caKey)
				require.NoError(t, err)
				defer os.Remove(test.caCert)
				defer os.Remove(test.caKey)
				_, _, err = generateServingCertFiles(
					caCert,
					caPrivateKey,
					test.servingCert,
					test.servingKey,
				)
				require.NoError(t, err)
				defer os.Remove(test.servingCert)
				defer os.Remove(test.servingKey)
				_, _, err = generateClientCertFiles(
					caCert,
					caPrivateKey,
					test.clientCert,
					test.clientKey,
				)
				require.NoError(t, err)
				defer os.Remove(test.clientCert)
				defer os.Remove(test.clientKey)
			}
			serverTLSConfig, err := generateServerTLSConfig(
				test.caCert,
				test.servingCert,
				test.servingKey,
			)
			require.NoError(t, err)
			handler := func(rw http.ResponseWriter, req *http.Request) {
				fmt.Fprint(rw, "Successfully verified client and received request!")
			}
			server := httptest.NewUnstartedServer(http.HandlerFunc(handler))
			server.TLS = serverTLSConfig
			server.StartTLS()
			defer server.Close()
			exporter := Exporter{
				Config{
					TLSConfig: map[string]string{
						"ca_file":              test.caCert,
						"cert_file":            test.clientCert,
						"key_file":             test.clientKey,
						"insecure_skip_verify": "0",
					},
				},
			}
			client, err := exporter.buildClient()
			require.NoError(t, err)
			res, err := client.Get(server.URL)
			require.NoError(t, err)
			defer res.Body.Close()
		})
	}
}
func generateCertFiles(
	template *x509.Certificate,
	signer *x509.Certificate,
	signerKey *ecdsa.PrivateKey,
	certFilepath string,
	keyFilepath string,
) (*x509.Certificate, *ecdsa.PrivateKey, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	if signerKey == nil {
		signerKey = privateKey
	}
	encodedCert, err := x509.CreateCertificate(
		rand.Reader, template, signer, privateKey.Public(), signerKey,
	)
	if err != nil {
		return nil, nil, err
	}
	certFile, err := os.Create(certFilepath)
	if err != nil {
		return nil, nil, err
	}
	err = pem.Encode(certFile, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: encodedCert,
	})
	if err != nil {
		return nil, nil, err
	}
	encodedPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
	if err != nil {
		return nil, nil, err
	}
	keyFile, err := os.Create(keyFilepath)
	if err != nil {
		return nil, nil, err
	}
	err = pem.Encode(keyFile, &pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: encodedPrivateKey,
	})
	if err != nil {
		return nil, nil, err
	}
	cert, err := x509.ParseCertificate(encodedCert)
	if err != nil {
		return nil, nil, err
	}
	return cert, privateKey, nil
}
func generateCACertFiles(certFilepath string, keyFilepath string) (*x509.Certificate, *ecdsa.PrivateKey, error) {
	certTemplate := &x509.Certificate{
		SerialNumber: big.NewInt(123),
		Subject: pkix.Name{
			Organization: []string{"CA Certificate"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(5 * time.Minute),
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
		IsCA:                  true,
		BasicConstraintsValid: true,
	}
	cert, privateKey, err := generateCertFiles(
		certTemplate,
		certTemplate,
		nil,
		certFilepath,
		keyFilepath,
	)
	if err != nil {
		return nil, nil, err
	}
	return cert, privateKey, nil
}