From f2dae23b3d6d30bb5676fb52ae9068cd17e6b407 Mon Sep 17 00:00:00 2001 From: Martin Necas Date: Tue, 5 Nov 2024 12:22:20 +0100 Subject: [PATCH] Allow to customize the TLS config for the govmomi client Signed-off-by: Martin Necas --- pkg/controller/plan/adapter/vsphere/client.go | 79 ++++++++++++++++++- pkg/controller/plan/adapter/vsphere/host.go | 3 + .../provider/container/vsphere/collector.go | 72 +++++++++++++++++ .../vmware/govmomi/vim25/soap/client.go | 29 +++++++ 4 files changed, 180 insertions(+), 3 deletions(-) diff --git a/pkg/controller/plan/adapter/vsphere/client.go b/pkg/controller/plan/adapter/vsphere/client.go index 41fc0b7c3..9d6e8c175 100644 --- a/pkg/controller/plan/adapter/vsphere/client.go +++ b/pkg/controller/plan/adapter/vsphere/client.go @@ -2,9 +2,13 @@ package vsphere import ( "context" + "crypto/tls" "fmt" + "net/http" liburl "net/url" + "os" "strconv" + "strings" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" @@ -28,8 +32,10 @@ import ( ) const ( - snapshotName = "forklift-migration-precopy" - snapshotDesc = "Forklift Operator warm migration precopy" + snapshotName = "forklift-migration-precopy" + snapshotDesc = "Forklift Operator warm migration precopy" + TLS_CIPHERS = "TLS_CIPHERS" + TLS_MAX_VERSION = "TLS_MAX_VERSION" ) // vSphere VM Client @@ -318,6 +324,68 @@ func (r *Client) getClient(vm *model.VM, hosts util.HostsFunc) (client *vim25.Cl return } +// CipherSuiteId copied and edited the CipherSuiteName from tls lib +func CipherSuiteId(name string) uint16 { + for _, c := range tls.CipherSuites() { + if c.Name == name { + return c.ID + } + } + for _, c := range tls.InsecureCipherSuites() { + if c.Name == name { + return c.ID + } + } + return 0 +} + +func GetCipherSuitesIds(names []string) []uint16 { + var resp []uint16 + for _, name := range names { + if id := CipherSuiteId(name); id != 0 { + resp = append(resp, id) + } + } + return resp +} + +func GetTransport() *http.Transport { + var t *http.Transport + if d, ok := http.DefaultTransport.(*http.Transport); ok { + t = d.Clone() + } else { + t = new(http.Transport) + } + return t +} + +func VersionNumber(versionName string) uint16 { + switch versionName { + case "1.0": + return tls.VersionTLS10 + case "1.1": + return tls.VersionTLS11 + case "1.2": + return tls.VersionTLS12 + case "1.3": + return tls.VersionTLS13 + default: + return 0 + } +} + +func SetTLSClientConfig(c *tls.Config) { + if tlsCiphers := os.Getenv(TLS_CIPHERS); tlsCiphers != "" { + tlsCiphersList := strings.Split(tlsCiphers, ",") + c.CipherSuites = GetCipherSuitesIds(tlsCiphersList) + } + if tlsMaxVersion := os.Getenv(TLS_MAX_VERSION); tlsMaxVersion != "" { + if version := VersionNumber(tlsMaxVersion); version != 0 { + c.MaxVersion = version + } + } +} + func (r *Client) getHostClient(hostDef *v1beta1.Host, host *model.Host) (client *vim25.Client, err error) { url, err := liburl.Parse("https://" + hostDef.Spec.IpAddress + "/sdk") if err != nil { @@ -338,9 +406,11 @@ func (r *Client) getHostClient(hostDef *v1beta1.Host, host *model.Host) (client err = liberr.Wrap(err) return } - url.User = liburl.UserPassword(string(secret.Data["user"]), string(secret.Data["password"])) + tr := GetTransport() + SetTLSClientConfig(tr.TLSClientConfig) soapClient := soap.NewClient(url, r.getInsecureSkipVerifyFlag()) + soapClient.Client.Transport = tr soapClient.SetThumbprint(url.Host, host.Thumbprint) vimClient, err := vim25.NewClient(context.TODO(), soapClient) if err != nil { @@ -426,7 +496,10 @@ func (r *Client) connect() error { return liberr.Wrap(err) } url.User = liburl.UserPassword(r.user(), r.password()) + tr := GetTransport() + SetTLSClientConfig(tr.TLSClientConfig) soapClient := soap.NewClient(url, r.getInsecureSkipVerifyFlag()) + soapClient.Client.Transport = tr soapClient.SetThumbprint(url.Host, r.thumbprint()) vimClient, err := vim25.NewClient(context.TODO(), soapClient) if err != nil { diff --git a/pkg/controller/plan/adapter/vsphere/host.go b/pkg/controller/plan/adapter/vsphere/host.go index 5549fe702..0ca12a7d6 100644 --- a/pkg/controller/plan/adapter/vsphere/host.go +++ b/pkg/controller/plan/adapter/vsphere/host.go @@ -73,7 +73,10 @@ func (r *EsxHost) connect(ctx context.Context) (err error) { url.User = liburl.UserPassword( r.user(), r.password()) + tr := GetTransport() + SetTLSClientConfig(tr.TLSClientConfig) soapClient := soap.NewClient(url, r.getInsecureSkipVerifyFlag()) + soapClient.Client.Transport = tr soapClient.SetThumbprint(url.Host, r.thumbprint()) vimClient, err := vim25.NewClient(ctx, soapClient) if err != nil { diff --git a/pkg/controller/provider/container/vsphere/collector.go b/pkg/controller/provider/container/vsphere/collector.go index f7df8e7e5..06f679902 100644 --- a/pkg/controller/provider/container/vsphere/collector.go +++ b/pkg/controller/provider/container/vsphere/collector.go @@ -2,8 +2,10 @@ package vsphere import ( "context" + "crypto/tls" "net/http" liburl "net/url" + "os" "path" "strconv" "strings" @@ -25,6 +27,11 @@ import ( meta "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + TLS_CIPHERS = "TLS_CIPHERS" + TLS_MAX_VERSION = "TLS_MAX_VERSION" +) + // Settings const ( // Connect retry delay. @@ -488,6 +495,68 @@ func (r *Collector) watch() (list []*libmodel.Watch) { return } +// CipherSuiteId copied and edited the CipherSuiteName from tls lib +func CipherSuiteId(name string) uint16 { + for _, c := range tls.CipherSuites() { + if c.Name == name { + return c.ID + } + } + for _, c := range tls.InsecureCipherSuites() { + if c.Name == name { + return c.ID + } + } + return 0 +} + +func GetCipherSuitesIds(names []string) []uint16 { + var resp []uint16 + for _, name := range names { + if id := CipherSuiteId(name); id != 0 { + resp = append(resp, id) + } + } + return resp +} + +func GetTransport() *http.Transport { + var t *http.Transport + if d, ok := http.DefaultTransport.(*http.Transport); ok { + t = d.Clone() + } else { + t = new(http.Transport) + } + return t +} + +func VersionNumber(versionName string) uint16 { + switch versionName { + case "1.0": + return tls.VersionTLS10 + case "1.1": + return tls.VersionTLS11 + case "1.2": + return tls.VersionTLS12 + case "1.3": + return tls.VersionTLS13 + default: + return 0 + } +} + +func SetTLSClientConfig(c *tls.Config) { + if tlsCiphers := os.Getenv(TLS_CIPHERS); tlsCiphers != "" { + tlsCiphersList := strings.Split(tlsCiphers, ",") + c.CipherSuites = GetCipherSuitesIds(tlsCiphersList) + } + if tlsMaxVersion := os.Getenv(TLS_MAX_VERSION); tlsMaxVersion != "" { + if version := VersionNumber(tlsMaxVersion); version != 0 { + c.MaxVersion = version + } + } +} + // Build the client. func (r *Collector) connect(ctx context.Context) (status int, err error) { r.close() @@ -499,7 +568,10 @@ func (r *Collector) connect(ctx context.Context) (status int, err error) { url.User = liburl.UserPassword( r.user(), r.password()) + tr := GetTransport() + SetTLSClientConfig(tr.TLSClientConfig) soapClient := soap.NewClient(url, r.getInsecureSkipVerifyFlag()) + soapClient.Client.Transport = tr soapClient.SetThumbprint(url.Host, r.thumbprint()) vimClient, err := vim25.NewClient(ctx, soapClient) if err != nil { diff --git a/vendor/github.com/vmware/govmomi/vim25/soap/client.go b/vendor/github.com/vmware/govmomi/vim25/soap/client.go index 912aaf5a3..ab8adba3d 100644 --- a/vendor/github.com/vmware/govmomi/vim25/soap/client.go +++ b/vendor/github.com/vmware/govmomi/vim25/soap/client.go @@ -132,6 +132,35 @@ func ParseURL(s string) (*url.URL, error) { return u, nil } +func NewClientWithTransport(u *url.URL, insecure bool, t *http.Transport) *Client { + if t != nil{ + if d, ok := http.DefaultTransport.(*http.Transport); ok { + t = d.Clone() + } else { + t = new(http.Transport) + } + } + + if insecure { + if t.TLSClientConfig == nil { + t.TLSClientConfig = new(tls.Config) + } + t.TLSClientConfig.InsecureSkipVerify = insecure + } + + c := newClientWithTransport(u, insecure, t) + + // Always set DialTLS and DialTLSContext, even if InsecureSkipVerify=true, + // because of how certificate verification has been delegated to the host's + // PKI framework in Go 1.18. Please see the following links for more info: + // + // * https://tip.golang.org/doc/go1.18 (search for "Certificate.Verify") + // * https://github.com/square/certigo/issues/264 + t.DialTLSContext = c.dialTLSContext + + return c +} + func NewClient(u *url.URL, insecure bool) *Client { var t *http.Transport