Setup TLS with CA Cert

- Extend config to take a path to a CA Certificate
- Use the CA Cert when establishing a connection with the SOAP client

Testing
We provide certs and keys for tests as fixtures, `vclib/fixtures`.
Those were created (and can be regenerated) using `vclib/fixtures/createCerts.sh`.

At the moment it's possible to configure a CA path and at the same time allow insecure
communication between vsphere cloud provider and vcenter. This may
change in the future; we might opt for overwriting the insecure
communication if a CA is configured / log and transparently pass the
arguments to the vcenter command / other. To be discussed.

At the moment the CA is a global level configuration. In other
words, all vcenter servers need to use certificates signed by the same
CA. There might be use cases for different CA per vcenter server; to be
discussed.
This commit is contained in:
Maria Ntalla 2018-06-05 11:25:23 +01:00 committed by Hannes Hörl
parent ec37c0f643
commit 9fc231e5c0
11 changed files with 496 additions and 3 deletions

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAsQRltjBKCgsIC4xGN5OWzJSaIhA1F8l4YO/7yGBj4ehKmEBr
jbOFMnUOp7Aq1rj/yrLzHNeKja9PD3fSsLMgbv34DR4JMXlbh2Cm84XBp1ibrqxm
qsz9nAGiDcJKf0QodxzAYcq8CZ0Xos1glgwHBSuf2hflWZoi4L/OAF4lDSmXk5yB
A+hcxdnjV3FTW5MuYlLezL1R+OG7Wo30D2NYdCXyK+Xa0IbEnD2vtMvoZXid/PuN
C18oPLuIjLs5XnCqoznNsbk/S4HrEb8YntosTiaa+Vc/MdVj7rILTueDo0qRNB+R
nujALMtp0RP/mTLZ2AoVvpaB+AWX8DNN+uhf8ZexunFldEStf0yL1vtaszoCAzHV
i3Y6SON3YCZiNHGtjKabhg/1J6ibAwmdugzB7D3CRJfDyaJ0x/qAzdSMWqQfZ2tJ
ReLiK7+I6civo4XpqbSdN3Sul9X0qaz2z/gjoacSmAEqKnKEjXbjPU38f+F+KwDs
lHYAp999DNlhygOnGjam0/UIrdq1eFvLbWE7d9iqot0CJlQ4Hv2Sw5rhMibNYFfc
w3aHLtWL9OYTQ2a0vLddllupTvxhY+OQLOU+EAZ2e4Gm37CQou205cwKRz/4rO81
StRukrAyymClebIiii0qGbTGvZ86/LTiJtIG8r2mYmdHYcF3EiKitHTCK7kCAwEA
AQKCAgBkX3LxCJai9Thdm++gyd5DKKvxTrFcSJAqn0lsiEN6sEXD6RtTYQzQ3JEv
wnO4B3R7UlcJ7qoQxuwUgEQGj7t/VCDYB0T9OawNql9gTGLPai30sKsShGP1lvN1
y8qEOXicecAYc2WGKf5iAQSYcD92zhK5Dr2svfqy5+9+Q+PMf94EBEUfmx0nzvHa
/lZe4aj2dbkB7QPTFOQwZ7eRFirsySt1esNFZHWNhmjgIpMnHmqvLU//t7hQH6JA
8lSIWWhYX4lkEf9y6DsLeAkU4e8nbTqI0dDyh+Y/TdOdrSb2a2zEWnYu3hlCDSF2
PVm8W5ospyNHS35szXcm62B3OlZT676vznnKmV3/S9G0XB3raE1Pa4dnBmpsWMZy
1MrJGUMyKwZcZFGMUPGhP2NFTkvl2eMZ0Gs+2tCsEw1QAyw3S9rglPOXu5ruut+W
G9JUs9yrYZNaX4lwMm+GHEkcv3v7kriqwH2Si8Td5KI+UXhz0Pvl8F3dyzvvlTTs
0IAg/TtdERHYbz64ZtrJJdeMwhxwKrjzJ3PjmTrnz9wfT4a0PAI7XA1hrncoVb3Y
VE3V00Ae0mcWgTmwNAx3qTUpJUgPc1jNc1ShJeluX9YR+vfP1F1WPcZKzODevmv2
NdnbJ8d4OnjKpo3clYkEV57F8pFK6zhcptgb8+KU4iCj3HW7gQKCAQEA6nFrUUCR
lN347OzpzLb9HzoIESVS6zoZ0QSOZ4m4PZJJyRcHYoNjFSwtmQP9B1BOmWIYdBlQ
Ft1c3dCfuj/t0PawPN48KAtH3BhlNlJ3sE9a803q0aavjuyxD2+KQpqAwGYT7t/y
Wk1FT8nvBKGrJWH+sfwFj9+EzuAMd2kOV2cixSp2A2qtmXQtyvOHJVzR2HBpJ1ct
NajuowlwU28mfb9AUeCfzBn/QSSI+G4BTTEqiwnTbfn5bwuFNs2U2LZW6hhPuFt1
jOC4J7qOOhSh452Bv6fhTMyXgjLqEEwSgiZ+HWUixPsalUWkZx2NenUePodIyZgq
qqU77WvVDjI8yQKCAQEAwUs5aRZEQrodyzLS78FblRbjioK49ED4GNC3477WoKID
SSWRuIPt/STmeeBaJbJJxUxa4JE0SlJu7G2xyBIPCc0KmAR8B6H82TGISfjPO10Z
A0XxzMzCvZBv9fCHOcqbhrrcwHCuCa8vrEEYv2X++YwwFOiX7f3YnztIHUTBj3cm
3uWHtDcIzpgc02ZGoFRvBOW4OOioB1/jnMmwAbVoQ3XyIJW8rVWyKQngS5iGy60Y
12vtsV7xtKAWHooE/plsMWXm3k/sbuIEtVhNNz9U2vlHj393DTfvdadHspB+cKyf
230znnfMKAD5bMR1EcavgZ0EXLWmnwRsgYUB0vEfcQKCAQEAzyFx/ZGcjfgnq7wN
PK8Xp/Uvl2Zwgh8NHBx4bIXC37NVuXK9NY57hgNILf7WGRYcu2tty3Vpyym8mMVv
ubAtvweU4dI/N+nvjUeIdJwb3wvdgUUACEbKqO356XdUok+7HUGSruPxTVMjv8Db
ii4D9b1Et5/AkkKbJePRX9bTsukOUUCYj6A6zG9W3g6XAq2lQSLf5MAi01vzqtv1
/+EeEs9cVnqs9DiryrQqx8L5J2ge/ESsJmhKto9pHOg5b9Z5p83e8TTtAJCyY3dx
nWMJPP612czLQ30nBwNQxSFQ4Oh9WB84vuxTqjqja+8yRlUfaYNBDcuBNs9RyQwS
ar578QKCAQAH6cd46ON0g+ASYItIK3dPXDeGhSGDRmGhynGszjRFMTzHMtWLY0NL
+MXCuY+XOXxRqnWR+f/VBxjpbvg3Q53//bfwT0awnU4XqjJ1LM13FbGfc66Zfsx+
LDqZK/atRAEn++BrtHE7jkN6XtPfihJtLvMM+BS4Nos2wZuLLzRpZixeNbFfjF08
7/dGJErB55L/9VOcaNHwM1nDInKlL0MMd/iootitk/OOQIxBLAZgsj5xG0cI2uU0
StV8/JOFxMwsHYrdERKR24jrz6ihmWMk782hL0u1a9PO0kFaKxYyEK8esjp5w1fF
T3zmmghc6PBocwApt3oRyoGSr9pKQ3rRAoIBAQCsh/N1WeKWjskxZ6iI+rlfQwbH
0qqBurMQYyUPBT1xlfPSB8UkLzX3hPIa6nYlysBcWNjQLDZBeS8gq+wQ5YtxTW+8
9ei/6DXGU0QS6LjNbYwDJg9wOillPDijnhUtQnh/gB8MXbObQ0VIAmpZsKLmvnI/
yirvAV5216v5rVbIeGquw+6qHw3Imkv6YCgkLwbWt+atIENLBef4w/3p9GAiCanr
Z6CInb5f2YeYhv68XMod3XE8vN88P28A3SzxSY2CO3dNHT8T8lpXRr/Q8sNru5u4
d7CocCMQaUbilm92TgbVSu6bKdxrGbu03jzbBbcUeYpqR74o+Y9Rgvgb2tIQ
-----END RSA PRIVATE KEY-----

View File

@ -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-----

View File

@ -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 "$@"

View File

@ -0,0 +1 @@
this is some invalid content

View File

@ -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-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@ -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-----

View File

@ -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,

View File

@ -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"