diff --git a/pkg/cloudprovider/providers/vsphere/vclib/connection.go b/pkg/cloudprovider/providers/vsphere/vclib/connection.go index 91f0e8a963a..d8166377ffc 100644 --- a/pkg/cloudprovider/providers/vsphere/vclib/connection.go +++ b/pkg/cloudprovider/providers/vsphere/vclib/connection.go @@ -19,8 +19,12 @@ package vclib import ( "context" "crypto/tls" + "crypto/x509" "encoding/pem" + "errors" + "io/ioutil" "net" + "net/http" neturl "net/url" "sync" @@ -38,6 +42,7 @@ type VSphereConnection struct { Password string Hostname string Port string + CACert string Insecure bool RoundTripperCount uint credentialsLock sync.Mutex @@ -130,11 +135,50 @@ func (connection *VSphereConnection) login(ctx context.Context, client *vim25.Cl // Logout calls SessionManager.Logout for the given connection. func (connection *VSphereConnection) Logout(ctx context.Context) { m := session.NewManager(connection.Client) + + hasActiveSession, err := m.SessionIsActive(ctx) + if err != nil { + glog.Errorf("Logout failed: %s", err) + return + } + if !hasActiveSession { + glog.Errorf("No active session, cannot logout") + return + } if err := m.Logout(ctx); err != nil { glog.Errorf("Logout failed: %s", err) } } +var ( + ErrCaCertNotReadable = errors.New("Could not read CA cert file") + ErrCaCertInvalid = errors.New("Could not parse CA cert file") + ErrUnsupportedTransport = errors.New("Only support HTTP transport if configuring TLS") +) + +func (connection *VSphereConnection) ConfigureTransportWithCA(transport http.RoundTripper) error { + caCertBytes, err := ioutil.ReadFile(connection.CACert) + if err != nil { + glog.Errorf("Could not read CA cert file, %s", connection.CACert) + return ErrCaCertNotReadable + } + certPool := x509.NewCertPool() + + if ok := certPool.AppendCertsFromPEM(caCertBytes); !ok { + glog.Errorf("Cannot add CA to cert pool") + return ErrCaCertInvalid + } + httpTransport, ok := transport.(*http.Transport) + if !ok { + glog.Errorf("Failed to http transport") + return ErrUnsupportedTransport + } + + httpTransport.TLSClientConfig.RootCAs = certPool + + return nil +} + // NewClient creates a new govmomi client for the VSphereConnection obj func (connection *VSphereConnection) NewClient(ctx context.Context) (*vim25.Client, error) { url, err := soap.ParseURL(net.JoinHostPort(connection.Hostname, connection.Port)) @@ -144,11 +188,19 @@ func (connection *VSphereConnection) NewClient(ctx context.Context) (*vim25.Clie } sc := soap.NewClient(url, connection.Insecure) + + if connection.CACert != "" { + if err := connection.ConfigureTransportWithCA(sc.Client.Transport); err != nil { + return nil, err + } + } + client, err := vim25.NewClient(ctx, sc) if err != nil { glog.Errorf("Failed to create new client. err: %+v", err) return nil, err } + err = connection.login(ctx, client) if err != nil { return nil, err diff --git a/pkg/cloudprovider/providers/vsphere/vclib/connection_test.go b/pkg/cloudprovider/providers/vsphere/vclib/connection_test.go new file mode 100644 index 00000000000..ab76b4cc227 --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/connection_test.go @@ -0,0 +1,122 @@ +package vclib_test + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib" +) + +func createTestServer(t *testing.T, caCertPath, serverCertPath, serverKeyPath string, handler http.HandlerFunc) *httptest.Server { + caCertPEM, err := ioutil.ReadFile(caCertPath) + if err != nil { + t.Fatalf("Could not read ca cert from file") + } + + serverCert, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath) + if err != nil { + t.Fatalf("Could not load server cert and server key from files: %#v", err) + } + + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(caCertPEM); !ok { + t.Fatalf("Cannot add CA to CAPool") + } + + server := httptest.NewUnstartedServer(http.HandlerFunc(handler)) + server.TLS = &tls.Config{ + Certificates: []tls.Certificate{ + serverCert, + }, + RootCAs: certPool, + } + + return server +} + +func TestSomething(t *testing.T) { + caCertPath := "fixtures/ca.pem" + serverCertPath := "fixtures/server.pem" + serverKeyPath := "fixtures/server.key" + + gotRequest := false + handler := func(w http.ResponseWriter, r *http.Request) { + gotRequest = true + } + + server := createTestServer(t, caCertPath, serverCertPath, serverKeyPath, handler) + server.StartTLS() + + u, err := url.Parse(server.URL) + if err != nil { + t.Fatalf("Cannot parse URL: %v", err) + } + + connection := &vclib.VSphereConnection{ + Hostname: u.Hostname(), + Port: u.Port(), + CACert: "fixtures/ca.pem", + } + + // Ignoring error here, because we only care about the TLS connection + connection.NewClient(context.Background()) + + if !gotRequest { + t.Fatalf("Never saw a request, TLS connection could not be established") + } +} + +func TestWithInvalidCaCertPath(t *testing.T) { + connection := &vclib.VSphereConnection{ + Hostname: "should-not-matter", + Port: "should-not-matter", + CACert: "invalid-path", + } + + _, err := connection.NewClient(context.Background()) + + if err != vclib.ErrCaCertNotReadable { + t.Fatalf("should have occoured") + } +} + +func TestInvalidCaCert(t *testing.T) { + connection := &vclib.VSphereConnection{ + Hostname: "should-not-matter", + Port: "should-not-matter", + CACert: "fixtures/invalid.pem", + } + + _, err := connection.NewClient(context.Background()) + + if err != vclib.ErrCaCertInvalid { + t.Fatalf("should have occoured") + } +} + +func TestUnsupportedTransport(t *testing.T) { + notHttpTransport := new(fakeTransport) + + connection := &vclib.VSphereConnection{ + Hostname: "should-not-matter", + Port: "should-not-matter", + CACert: "fixtures/ca.pem", + } + + err := connection.ConfigureTransportWithCA(notHttpTransport) + if err != vclib.ErrUnsupportedTransport { + t.Fatalf("should have occured") + } +} + +type fakeTransport struct{} + +func (ft fakeTransport) RoundTrip(*http.Request) (*http.Response, error) { + return nil, nil +} diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.key b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.key new file mode 100644 index 00000000000..7c9848e9ff3 --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.pem b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.pem new file mode 100644 index 00000000000..4359bb5045e --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/ca.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE/DCCAuQCCQDsqyXMQlDdzzANBgkqhkiG9w0BAQsFADBAMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQ0ExEzARBgNVBAoMCkFjbWUsIEluYy4xDzANBgNVBAMMBnNv +bWVDQTAeFw0xODA2MDQxNDU3NDZaFw00NjA2MTcxNDU3NDZaMEAxCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJDQTETMBEGA1UECgwKQWNtZSwgSW5jLjEPMA0GA1UEAwwG +c29tZUNBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsQRltjBKCgsI +C4xGN5OWzJSaIhA1F8l4YO/7yGBj4ehKmEBrjbOFMnUOp7Aq1rj/yrLzHNeKja9P +D3fSsLMgbv34DR4JMXlbh2Cm84XBp1ibrqxmqsz9nAGiDcJKf0QodxzAYcq8CZ0X +os1glgwHBSuf2hflWZoi4L/OAF4lDSmXk5yBA+hcxdnjV3FTW5MuYlLezL1R+OG7 +Wo30D2NYdCXyK+Xa0IbEnD2vtMvoZXid/PuNC18oPLuIjLs5XnCqoznNsbk/S4Hr +Eb8YntosTiaa+Vc/MdVj7rILTueDo0qRNB+RnujALMtp0RP/mTLZ2AoVvpaB+AWX +8DNN+uhf8ZexunFldEStf0yL1vtaszoCAzHVi3Y6SON3YCZiNHGtjKabhg/1J6ib +AwmdugzB7D3CRJfDyaJ0x/qAzdSMWqQfZ2tJReLiK7+I6civo4XpqbSdN3Sul9X0 +qaz2z/gjoacSmAEqKnKEjXbjPU38f+F+KwDslHYAp999DNlhygOnGjam0/UIrdq1 +eFvLbWE7d9iqot0CJlQ4Hv2Sw5rhMibNYFfcw3aHLtWL9OYTQ2a0vLddllupTvxh +Y+OQLOU+EAZ2e4Gm37CQou205cwKRz/4rO81StRukrAyymClebIiii0qGbTGvZ86 +/LTiJtIG8r2mYmdHYcF3EiKitHTCK7kCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA +b6zjyhxWUFo+cI8K1lMegBTulE1ivCeMtEjvGoU3X42Gr/6y7hz5P4evgzezF+vN +eArqsxQ4qxPSDvmUwKJ7ovoggMi6mZqzZSTSSViIMsJUJ1ukDUsdI6iw49R6T6Cj +MX2SXsj1go7f0Mhw/a9BJfHXbnawnEA4V2Tr6Epm1tW+oAQi5eimOdr36stlLTF0 +uoJTEfckN9AsyYLSx71C8aq/pGTzXGG7aP6yyaHHD1+u3StQB+63DnPsU7zODOpN +cTjpuZy1WYvY5m5+iiEUT9tSBtIjIN2lPwZCCRd/l8bybb/6T3nttHv9ijON1vSF +RG8BxJwYzzXEVYY67ztM6dxp5c9gfegyE1Q/MtF6nwgsZ4WpZQbMDRbAd4yyCLM+ +aKDnDlB+I4e4WegodvTlDaYvtJUQaAlefGmfP+r9I5fPvbGRJDJ7AoirJnv2MCLs +hNbEXk/+/oNWIHAFQTNNzS40U05ytPBrtPoAOL2e+a92rHcWfsZ/ApYSCesZxZNw +1k2siPQ8uKBM10AW0fMU8wZs02LYcR5Xq1Zaj/GWooZaAy6QQfZK6AELpdLSZmMs +tzPz6gxckswJEnZ3w/wyzHmSMSJrr2+dydYA/UN509V/nQQolgE/ZQoMUlu74wCf +HEeQH71L269fQM1RxuAEfBz0RuEZjQc7A9NETm5I0BM= +-----END CERTIFICATE----- diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/createCerts.sh b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/createCerts.sh new file mode 100755 index 00000000000..0efd8d6ddc7 --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/createCerts.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -eu + +# 1. create CA key +# 2. create CA cert +# 3. create CSR with IP SAN +# 4. create server cert with IP SAN + +createKey() { + openssl genrsa -out "$1" 4096 +} + +createCaCert() { + openssl req -x509 \ + -subj "/C=US/ST=CA/O=Acme, Inc./CN=someCA" \ + -new -nodes -key "$2" -sha256 -days 10240 -out "$1" +} + +createCSR() { + openssl req -new -sha256 \ + -key "$2" \ + -subj "/C=US/ST=CA/O=Acme, Inc./CN=localhost" \ + -reqexts SAN \ + -config <( + cat /etc/ssl/openssl.cnf ; \ + printf '\n[SAN]\n' ; \ + getSAN + ) \ + -out "$1" +} + +signCSR() { + openssl x509 -req -in "$2" \ + -CA "$3" \ + -CAkey "$4" \ + -CAcreateserial \ + -days 3650 -sha256 \ + -extfile <( getSAN ) \ + -out "$1" +} + +getSAN() { + printf "subjectAltName=DNS:localhost,IP:127.0.0.1" +} + +main() { + local caCertPath="./ca.pem" + local caKeyPath="./ca.key" + local serverCsrPath="./server.csr" + local serverCertPath="./server.pem" + local serverKeyPath="./server.key" + + createKey "$caKeyPath" + createCaCert "$caCertPath" "$caKeyPath" + createKey "$serverKeyPath" + createCSR "$serverCsrPath" "$serverKeyPath" + signCSR "$serverCertPath" "$serverCsrPath" "$caCertPath" "$caKeyPath" +} + +main "$@" diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/invalid.pem b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/invalid.pem new file mode 100644 index 00000000000..253d11958ad --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/invalid.pem @@ -0,0 +1 @@ +this is some invalid content diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.csr b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.csr new file mode 100644 index 00000000000..c6998bb632f --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.csr @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEtTCCAp0CAQAwQzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRMwEQYDVQQK +DApBY21lLCBJbmMuMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDRtnbdQFkM8IgTx2KU2lyZitrgqhcxRVGaMrw+d+Q0 +3IZ9y8L4sVFjBqAXUIkCndfQSVyzAnSgrvJqpFk43TIbg7TY/ZpLdN+tHn+PT3qN +MYjOIMR+BVpT8Fi23Ds5ghVw26Av4ZfT6a3R2e4ndsdvwM7nf+8YwU2qPwu48cDN +OLCGoV9JUZADoHHVQOC9LZfUWtSxU0zhnnEWyu33OInzBtwTSdrxQui287svcJBT +XkMlRzj5adAZBJ8zhxsNxs6lPQX/C7V3yjTEdSvnYAtoGbFPqh5D4aHw7Vjrax7Z +ihUCpIJ+Nf01UzzxqkXhlf08qcMxA8MIRDujwsY2DKu1TpZqn+fDP0nfWUe/PJ7d +7hUHkdvH09D8csP72Ryu+Nmgbx1gPgv+srGHCwVCFa1Hg+oq6u2Uq2eW6Vt3LcRc +8S1zG6CjvFthU7Eurh6a2dsDvWDSst3JNnkOFeSLjOqa5tDSNh5SZIbZ17BiiiD6 +MIqRDBiK4jGh20E0m1cVk1e1QRpD33ViLDGqD628bCcOtD86eUhrv1k+fqPr1gF0 +fdbQXJoYonPvfHlEzQPXprPNdv8JSQ/aKQqWO7yC8H/V+bpbRwL+vDMkQ3YMC3zf +bj7z8bJvQ7DOf0Ogy0wyP5FG1Jm47FE66ckKTZdLCf8VrxO8cnrXFGZ0CQIEu4Rn +PQIDAQABoC0wKwYJKoZIhvcNAQkOMR4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SH +BH8AAAEwDQYJKoZIhvcNAQELBQADggIBADh1KvKchPJTEDX9oonipCyvHLzysJpI +Li1ZWDSu0v4ASlYMJrAxQNphHYDu/FZdqvDvqQ0M8naM9sNdq0UkgiAdOtjtDB9J +k18/sOOjXCqAWIcFr9vdwbs0bKD1x2sBBVN2O3G+YX7w8YwWX1VQdBoObmJiSAmw +V6l1yltaiw6fb1RpO71JFjL19QvRuSs7pVhDjuI1gu/bggebxh6AIpAHXq3lukMS +a3mOyeiik5PlsDwxMMJNCKzqf+DYLzslMKq3IaVDyyqPxaZeVCb80525W4W+2/eV +kmcMpqUJaG+iqYfuaX1S+7jZRri0lhkejE8HJ1xT8JWaovZ7G2WzWDEqUPVvVoX4 +g2MgLVtcb6WcstQpsQ+AY0rKPq8K4T5t420JtRLI9oUt5eQm7yWGFF1p4URWzuYq +/TOTPxIrufeFfqBsH8XAOydmuwUGunpefoxvD0tp9SFpRm6NWliYyunFZP82xtOK +eP3aW3A1KnPdLgAyi9/iwi9VzGQTu1iWAc9si9xTyqTNOtvgYUzy2wmic2HgWfEs +NEv+wW6CNh1qR1Vq7j0zkl327CFrGBvKbR1d5cleNx8EiWZ1QNmickMaCHBiwkql +yIbSPtlmzme8y8N73bUaV74DbKExZvu2CnUzdIHOt0Dpax6TkBHc1HI530v58eN/ +VDP7+tBBVo+c +-----END CERTIFICATE REQUEST----- diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.key b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.key new file mode 100644 index 00000000000..b08afaaee01 --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.pem b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.pem new file mode 100644 index 00000000000..a43b6366e13 --- /dev/null +++ b/pkg/cloudprovider/providers/vsphere/vclib/fixtures/server.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFJDCCAwygAwIBAgIJAOcEAbv8NslcMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTETMBEGA1UECgwKQWNtZSwgSW5jLjEPMA0GA1UE +AwwGc29tZUNBMB4XDTE4MDYwNDE0NTgwNloXDTI4MDYwMTE0NTgwNlowQzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgMAkNBMRMwEQYDVQQKDApBY21lLCBJbmMuMRIwEAYD +VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDR +tnbdQFkM8IgTx2KU2lyZitrgqhcxRVGaMrw+d+Q03IZ9y8L4sVFjBqAXUIkCndfQ +SVyzAnSgrvJqpFk43TIbg7TY/ZpLdN+tHn+PT3qNMYjOIMR+BVpT8Fi23Ds5ghVw +26Av4ZfT6a3R2e4ndsdvwM7nf+8YwU2qPwu48cDNOLCGoV9JUZADoHHVQOC9LZfU +WtSxU0zhnnEWyu33OInzBtwTSdrxQui287svcJBTXkMlRzj5adAZBJ8zhxsNxs6l +PQX/C7V3yjTEdSvnYAtoGbFPqh5D4aHw7Vjrax7ZihUCpIJ+Nf01UzzxqkXhlf08 +qcMxA8MIRDujwsY2DKu1TpZqn+fDP0nfWUe/PJ7d7hUHkdvH09D8csP72Ryu+Nmg +bx1gPgv+srGHCwVCFa1Hg+oq6u2Uq2eW6Vt3LcRc8S1zG6CjvFthU7Eurh6a2dsD +vWDSst3JNnkOFeSLjOqa5tDSNh5SZIbZ17BiiiD6MIqRDBiK4jGh20E0m1cVk1e1 +QRpD33ViLDGqD628bCcOtD86eUhrv1k+fqPr1gF0fdbQXJoYonPvfHlEzQPXprPN +dv8JSQ/aKQqWO7yC8H/V+bpbRwL+vDMkQ3YMC3zfbj7z8bJvQ7DOf0Ogy0wyP5FG +1Jm47FE66ckKTZdLCf8VrxO8cnrXFGZ0CQIEu4RnPQIDAQABox4wHDAaBgNVHREE +EzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggIBAJkQELAVTJSS +1Rfmn9aRXOd6Uap7+3oE3ZOuUU2XxBJKXClVhKcCJ8nToDHsSV6D7fMcgJvg4fOt +pkQldEi845mUx0t8F/uKqwO1fx+Cjnr8meArGpwn2bma+4cpZyLbFgAZH7Cst+er +FbewtoPbfnADZlZzK35jcSzGf+oYJU11QJjTyRRXBp9eC73Fa9fM53GrA5GkJiSc +u7+mRlAKKEMmNNdnZa48X/cKI7UXp+qHSdkE9zDZ/LrbZPV8OuJY/RpwLT1A59pM +wkvC1AaGIa+ysfLw7s+0K2B1xmquAdopzcwGmK5CGJQmoF3YEFc+QN5fkC4UbizM +XiXZ3Uu0bbg9qNjR99Z0N/knVce+KpAeJg9cEFSsdTM2oaz/53KsalxOWsDkxvuI +HGwjSMPO22JWPs9zs06/1CsiQvn7O/Kx4xjwuSzxjKOJaolynF0lUsNXljOnF6FC +cQCBsY3TFdJFAtjYouiUu5UI4tvQ6pFmCDfj2Ioj+XhF0pd5G/bM3Hmb+WL9We4s +ajoaB85yAy5YdxBuLW5/TdeRqWZHB3AXbAXY8KoOPaOv9NCRWwwaGsiBSUSPdUzT +UkqWjMhcZnmHFeKVDQWpVIy1zrVojR17B8/xY9uypg5yZxGpssUwbfi9m/TnPH/A +a6U8cIpiDSFdeNYPGwKm8O06pY67+w1I +-----END CERTIFICATE----- diff --git a/pkg/cloudprovider/providers/vsphere/vsphere.go b/pkg/cloudprovider/providers/vsphere/vsphere.go index fbe5d50e98c..af1408a4eed 100644 --- a/pkg/cloudprovider/providers/vsphere/vsphere.go +++ b/pkg/cloudprovider/providers/vsphere/vsphere.go @@ -121,6 +121,9 @@ type VSphereConfig struct { VCenterPort string `gcfg:"port"` // True if vCenter uses self-signed cert. InsecureFlag bool `gcfg:"insecure-flag"` + // Specifies the path to a CA certificate in PEM format. Optional; if not + // configured, the system's CA certificates will be used. + CAFile string `gcfg:"ca-file"` // Datacenter in which VMs are located. // Deprecated. Use "datacenters" instead. Datacenter string `gcfg:"datacenter"` @@ -345,7 +348,9 @@ func populateVsphereInstanceMap(cfg *VSphereConfig) (map[string]*VSphereInstance Insecure: cfg.Global.InsecureFlag, RoundTripperCount: vcConfig.RoundTripperCount, Port: vcConfig.VCenterPort, + CACert: cfg.Global.CAFile, } + vsphereIns := VSphereInstance{ conn: &vSphereConn, cfg: &vcConfig, diff --git a/pkg/cloudprovider/providers/vsphere/vsphere_test.go b/pkg/cloudprovider/providers/vsphere/vsphere_test.go index 0533405b190..aa13921a117 100644 --- a/pkg/cloudprovider/providers/vsphere/vsphere_test.go +++ b/pkg/cloudprovider/providers/vsphere/vsphere_test.go @@ -19,6 +19,8 @@ package vsphere import ( "context" "crypto/tls" + "crypto/x509" + "io/ioutil" "log" "os" "strconv" @@ -89,8 +91,12 @@ func configFromEnv() (cfg VSphereConfig, ok bool) { return } -// configFromSim starts a vcsim instance and returns config for use against the vcsim instance. func configFromSim() (VSphereConfig, func()) { + return configFromSimWithTLS(new(tls.Config), true) +} + +// configFromSim starts a vcsim instance and returns config for use against the vcsim instance. +func configFromSimWithTLS(tlsConfig *tls.Config, insecureAllowed bool) (VSphereConfig, func()) { var cfg VSphereConfig model := simulator.VPX() @@ -99,7 +105,7 @@ func configFromSim() (VSphereConfig, func()) { log.Fatal(err) } - model.Service.TLS = new(tls.Config) + model.Service.TLS = tlsConfig s := model.Service.NewServer() // STS simulator @@ -109,7 +115,8 @@ func configFromSim() (VSphereConfig, func()) { // Lookup Service simulator model.Service.RegisterSDK(lookup.New()) - cfg.Global.InsecureFlag = true + cfg.Global.InsecureFlag = insecureAllowed + cfg.Global.VCenterIP = s.URL.Hostname() cfg.Global.VCenterPort = s.URL.Port() cfg.Global.User = s.URL.User.Username() @@ -160,6 +167,7 @@ insecure-flag = true datacenter = us-west vm-uuid = 1234 vm-name = vmname +ca-file = /some/path/to/a/ca.pem `)) if err != nil { t.Fatalf("Should succeed when a valid config is provided: %s", err) @@ -180,6 +188,10 @@ vm-name = vmname if cfg.Global.VMName != "vmname" { t.Errorf("incorrect vm-name: %s", cfg.Global.VMName) } + + if cfg.Global.CAFile != "/some/path/to/a/ca.pem" { + t.Errorf("incorrect ca-file: %s", cfg.Global.CAFile) + } } func TestNewVSphere(t *testing.T) { @@ -250,6 +262,57 @@ func TestVSphereLoginByToken(t *testing.T) { vcInstance.conn.Logout(ctx) } +func TestVSphereLoginWithCaCert(t *testing.T) { + caCertPath := "./vclib/fixtures/ca.pem" + serverCertPath := "./vclib/fixtures/server.pem" + serverKeyPath := "./vclib/fixtures/server.key" + + caCertPEM, err := ioutil.ReadFile(caCertPath) + if err != nil { + t.Fatalf("Could not read ca cert from file") + } + + serverCert, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath) + if err != nil { + t.Fatalf("Could not load server cert and server key from files: %#v", err) + } + + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(caCertPEM); !ok { + t.Fatalf("Cannot add CA to CAPool") + } + + tlsConfig := tls.Config{ + Certificates: []tls.Certificate{serverCert}, + RootCAs: certPool, + } + + cfg, cleanup := configFromSimWithTLS(&tlsConfig, false) + defer cleanup() + + cfg.Global.CAFile = caCertPath + + // Create vSphere configuration object + vs, err := newControllerNode(cfg) + if err != nil { + t.Fatalf("Failed to construct/authenticate vSphere: %s", err) + } + + ctx := context.Background() + + // Create vSphere client + vcInstance, ok := vs.vsphereInstanceMap[cfg.Global.VCenterIP] + if !ok { + t.Fatalf("Couldn't get vSphere instance: %s", cfg.Global.VCenterIP) + } + + err = vcInstance.conn.Connect(ctx) + if err != nil { + t.Errorf("Failed to connect to vSphere: %s", err) + } + vcInstance.conn.Logout(ctx) +} + func TestZones(t *testing.T) { cfg := VSphereConfig{} cfg.Global.Datacenter = "myDatacenter"