From 4a62d4bea44b1f5af8885e01b370144b7207aa2a Mon Sep 17 00:00:00 2001 From: Joseph Callen Date: Fri, 27 Jun 2025 14:38:31 -0400 Subject: [PATCH 1/3] do not merge, vsphere privelge logging our continued issue determining what privelges are required for components --- pkg/controller/vsphere/session/session.go | 94 +++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pkg/controller/vsphere/session/session.go b/pkg/controller/vsphere/session/session.go index 85c9b2276e..b3b2705768 100644 --- a/pkg/controller/vsphere/session/session.go +++ b/pkg/controller/vsphere/session/session.go @@ -20,7 +20,10 @@ import ( "context" "errors" "fmt" + "io" + "net/http" "net/url" + "strings" "sync" "time" @@ -28,6 +31,7 @@ import ( "github.com/vmware/govmomi/vapi/tags" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/vmware/govmomi/vim25/xml" "github.com/google/uuid" "github.com/vmware/govmomi" @@ -57,6 +61,88 @@ type Session struct { sessionKey string } +// #### Start: This section was added by cursor + +// SOAPResponse represents the structure of SOAP responses +type SOAPResponse struct { + XMLName xml.Name `xml:"Envelope"` + Body struct { + XMLName xml.Name `xml:"Body"` + Fault *struct { + XMLName xml.Name `xml:"Fault"` + Code struct { + XMLName xml.Name `xml:"faultcode"` + Value string `xml:",chardata"` + } `xml:"faultcode"` + Reason struct { + XMLName xml.Name `xml:"faultstring"` + Value string `xml:",chardata"` + } `xml:"faultstring"` + Detail struct { + XMLName xml.Name `xml:"detail"` + Content string `xml:",chardata"` + } `xml:"detail"` + } `xml:"Fault,omitempty"` + } `xml:"Body"` +} + +// CustomTransport wraps the default transport to intercept SOAP responses +type CustomTransport struct { + http.RoundTripper +} + +func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) { + // Call the original transport + resp, err := t.RoundTripper.RoundTrip(req) + if err != nil { + return resp, err + } + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return resp, err + } + resp.Body.Close() + + // Check if it's a SOAP response + if strings.Contains(string(body), " Date: Fri, 27 Jun 2025 16:11:41 -0400 Subject: [PATCH 2/3] issues with insecure setting --- pkg/controller/vsphere/session/session.go | 38 ++++++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/pkg/controller/vsphere/session/session.go b/pkg/controller/vsphere/session/session.go index b3b2705768..b2a1b991bc 100644 --- a/pkg/controller/vsphere/session/session.go +++ b/pkg/controller/vsphere/session/session.go @@ -21,14 +21,17 @@ import ( "errors" "fmt" "io" + "log" "net/http" "net/url" "strings" "sync" "time" + "github.com/vmware/govmomi/session" "github.com/vmware/govmomi/vapi/rest" "github.com/vmware/govmomi/vapi/tags" + "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/xml" @@ -147,17 +150,42 @@ func newClientWithTimeout(ctx context.Context, u *url.URL, insecure bool, timeou clientCreateCtx, clientCreateCtxCancel := context.WithTimeout(ctx, timeout) defer clientCreateCtxCancel() // It makes call to vcenter during new client creation, so pass context with timeout there. - client, err := govmomi.NewClient(clientCreateCtx, u, insecure) - if err != nil { - return nil, err - } + /* + client, err := govmomi.NewClient(clientCreateCtx, u, insecure) + if err != nil { + return nil, err + } + + */ customTransport := &CustomTransport{ RoundTripper: http.DefaultTransport, } + soapClient := soap.NewClient(u, insecure) + soapClient.Transport = customTransport + + // Create vim25 client + vimClient, err := vim25.NewClient(clientCreateCtx, soapClient) + if err != nil { + log.Fatalf("Failed to create vim25 client: %v", err) + } + + // Create govmomi client + client := &govmomi.Client{ + Client: vimClient, + SessionManager: session.NewManager(vimClient), + } + + // Login to vSphere + err = client.Login(ctx, u.User) + if err != nil { + log.Fatalf("Failed to login to vSphere: %v", err) + } + defer client.Logout(clientCreateCtx) + // Create SOAP client with custom transport - client.Transport = customTransport + //client.Transport = customTransport client.Timeout = timeout return client, nil From 50b5201a8e2cd682a71a24a32cfafd9c971288d1 Mon Sep 17 00:00:00 2001 From: Joseph Callen Date: Mon, 30 Jun 2025 12:13:18 -0400 Subject: [PATCH 3/3] fix insecure --- pkg/controller/vsphere/session/session.go | 76 ++++++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/pkg/controller/vsphere/session/session.go b/pkg/controller/vsphere/session/session.go index b2a1b991bc..be4229e341 100644 --- a/pkg/controller/vsphere/session/session.go +++ b/pkg/controller/vsphere/session/session.go @@ -18,6 +18,7 @@ package session import ( "context" + "crypto/tls" "errors" "fmt" "io" @@ -114,24 +115,48 @@ func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) { var soapResp SOAPResponse if err := xml.Unmarshal(body, &soapResp); err == nil { if soapResp.Body.Fault != nil { - klog.Error("=== PRIVILEGE ERROR DETECTED ===") - klog.Errorf("Fault Code: %s\n", soapResp.Body.Fault.Code.Value) - klog.Errorf("Fault Reason: %s\n", soapResp.Body.Fault.Reason.Value) - klog.Errorf("Fault Detail: %s\n", soapResp.Body.Fault.Detail.Content) - klog.Error("================================\n") + klog.Error("=== SOAP FAULT DETECTED ===") + klog.Errorf("Fault Code: %s", soapResp.Body.Fault.Code.Value) + klog.Errorf("Fault Reason: %s", soapResp.Body.Fault.Reason.Value) + klog.Errorf("Fault Detail: %s", soapResp.Body.Fault.Detail.Content) + + // Check if this is an authentication error + if strings.Contains(strings.ToLower(soapResp.Body.Fault.Reason.Value), "incorrect user name or password") || + strings.Contains(strings.ToLower(soapResp.Body.Fault.Reason.Value), "cannot complete login") { + klog.Error("=== AUTHENTICATION ERROR DETECTED ===") + klog.Error("Please verify your vSphere username and password credentials") + klog.Error("================================================") + } + klog.Error("================================") } } - // Check for privilege-related error messages in the response + // Check for authentication-related error messages in the response bodyStr := string(body) + authKeywords := []string{ + "incorrect user name or password", "cannot complete login", "invalidlogin", + "authentication failed", "login failed", "invalid credentials", + } + for _, keyword := range authKeywords { + if strings.Contains(strings.ToLower(bodyStr), strings.ToLower(keyword)) { + klog.Errorf("=== AUTHENTICATION ISSUE DETECTED (keyword: %s) ===", keyword) + klog.Error("Response contains authentication-related content") + klog.Error("Please verify your vSphere username and password") + klog.Error("================================================") + break + } + } + + // Check for privilege-related error messages in the response privilegeKeywords := []string{ "privilege", "permission", "access denied", "unauthorized", "forbidden", - "NoPermission", "InvalidLogin", "InvalidPrivilege", + "NoPermission", "InvalidPrivilege", "insufficient privileges", } for _, keyword := range privilegeKeywords { if strings.Contains(strings.ToLower(bodyStr), strings.ToLower(keyword)) { - klog.Errorf("=== POTENTIAL PRIVILEGE ISSUE DETECTED (keyword: %s) ===\n", keyword) - klog.Error("Response contains privilege-related content\n") + klog.Errorf("=== POTENTIAL PRIVILEGE ISSUE DETECTED (keyword: %s) ===", keyword) + klog.Error("Response contains privilege-related content") + klog.Error("Please verify user has sufficient vSphere permissions") klog.Error("==================================================") break } @@ -159,7 +184,7 @@ func newClientWithTimeout(ctx context.Context, u *url.URL, insecure bool, timeou */ customTransport := &CustomTransport{ - RoundTripper: http.DefaultTransport, + RoundTripper: createTransport(insecure), } soapClient := soap.NewClient(u, insecure) @@ -177,12 +202,8 @@ func newClientWithTimeout(ctx context.Context, u *url.URL, insecure bool, timeou SessionManager: session.NewManager(vimClient), } - // Login to vSphere - err = client.Login(ctx, u.User) - if err != nil { - log.Fatalf("Failed to login to vSphere: %v", err) - } - defer client.Logout(clientCreateCtx) + // Note: We don't login here because u.User is nil + // The actual login happens later in GetOrCreate with proper credentials // Create SOAP client with custom transport //client.Transport = customTransport @@ -210,7 +231,7 @@ func GetOrCreate( return &session, nil } } - klog.Infof("No existing vCenter session found, creating new session") + klog.Infof("No existing vCenter session found, creating new session for server: %s, datacenter: %s, username: %s", server, datacenter, username) soapURL, err := soap.ParseURL(server) if err != nil { @@ -230,6 +251,12 @@ func GetOrCreate( // Set up user agent before login for being able to track mapi component in vcenter sessions list client.UserAgent = "machineAPIvSphereProvider" if err := client.Login(ctx, url.UserPassword(username, password)); err != nil { + // Check if it's a credential-related error + if strings.Contains(err.Error(), "incorrect user name or password") || + strings.Contains(err.Error(), "Cannot complete login") || + strings.Contains(err.Error(), "InvalidLogin") { + return nil, fmt.Errorf("vSphere authentication failed - please verify username and password: %w", err) + } return nil, fmt.Errorf("unable to login to vCenter: %w", err) } @@ -363,3 +390,18 @@ func (s *Session) WithCachingTagsManager(ctx context.Context, f func(m *CachingT return f(m) } + +// createTransport creates a transport that respects the insecure flag +func createTransport(insecure bool) http.RoundTripper { + if insecure { + // Create a transport that skips TLS verification + transport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + return transport + } + // Use default transport for secure connections + return http.DefaultTransport +}