github.com/vmware/govmomi@v0.37.1/lookup/client.go (about) 1 /* 2 Copyright (c) 2018 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package lookup 18 19 import ( 20 "context" 21 "crypto/x509" 22 "encoding/base64" 23 "fmt" 24 "log" 25 "net/url" 26 27 "github.com/vmware/govmomi/internal" 28 "github.com/vmware/govmomi/lookup/methods" 29 "github.com/vmware/govmomi/lookup/types" 30 "github.com/vmware/govmomi/object" 31 "github.com/vmware/govmomi/vim25" 32 "github.com/vmware/govmomi/vim25/soap" 33 vim "github.com/vmware/govmomi/vim25/types" 34 ) 35 36 const ( 37 Namespace = "lookup" 38 Version = "2.0" 39 Path = "/lookupservice" + vim25.Path 40 ) 41 42 var ( 43 ServiceInstance = vim.ManagedObjectReference{ 44 Type: "LookupServiceInstance", 45 Value: "ServiceInstance", 46 } 47 ) 48 49 // Client is a soap.Client targeting the SSO Lookup Service API endpoint. 50 type Client struct { 51 *soap.Client 52 53 RoundTripper soap.RoundTripper 54 55 ServiceContent types.LookupServiceContent 56 } 57 58 // NewClient returns a client targeting the SSO Lookup Service API endpoint. 59 func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) { 60 path := &url.URL{Path: Path} 61 // PSC may be external, attempt to derive from sts.uri if not using envoy sidecar 62 if !internal.UsingEnvoySidecar(c) && c.ServiceContent.Setting != nil { 63 m := object.NewOptionManager(c, *c.ServiceContent.Setting) 64 opts, err := m.Query(ctx, "config.vpxd.sso.sts.uri") 65 if err == nil && len(opts) == 1 { 66 u, err := url.Parse(opts[0].GetOptionValue().Value.(string)) 67 if err == nil { 68 path.Scheme = u.Scheme 69 path.Host = u.Host 70 } 71 } 72 } 73 74 sc := c.Client.NewServiceClient(path.String(), Namespace) 75 sc.Version = Version 76 77 req := types.RetrieveServiceContent{ 78 This: ServiceInstance, 79 } 80 81 res, err := methods.RetrieveServiceContent(ctx, sc, &req) 82 if err != nil { 83 return nil, err 84 } 85 86 return &Client{sc, sc, res.Returnval}, nil 87 } 88 89 // RoundTrip dispatches to the RoundTripper field. 90 func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error { 91 return c.RoundTripper.RoundTrip(ctx, req, res) 92 } 93 94 func (c *Client) List(ctx context.Context, filter *types.LookupServiceRegistrationFilter) ([]types.LookupServiceRegistrationInfo, error) { 95 req := types.List{ 96 This: *c.ServiceContent.ServiceRegistration, 97 FilterCriteria: filter, 98 } 99 100 res, err := methods.List(ctx, c, &req) 101 if err != nil { 102 return nil, err 103 } 104 return res.Returnval, nil 105 } 106 107 func (c *Client) SiteID(ctx context.Context) (string, error) { 108 req := types.GetSiteId{ 109 This: *c.ServiceContent.ServiceRegistration, 110 } 111 112 res, err := methods.GetSiteId(ctx, c, &req) 113 if err != nil { 114 return "", err 115 } 116 return res.Returnval, nil 117 } 118 119 // EndpointURL uses the Lookup Service to find the endpoint URL and thumbprint for the given filter. 120 // If the endpoint is found, its TLS certificate is also added to the vim25.Client's trusted host thumbprints. 121 // If the Lookup Service is not available, the given path is returned as the default. 122 func EndpointURL(ctx context.Context, c *vim25.Client, path string, filter *types.LookupServiceRegistrationFilter) string { 123 // Services running on vCenter can bypass lookup service. 124 if useSidecar := internal.UsingEnvoySidecar(c); useSidecar { 125 return fmt.Sprintf("http://%s%s", c.URL().Host, path) 126 } 127 if lu, err := NewClient(ctx, c); err == nil { 128 info, _ := lu.List(ctx, filter) 129 if len(info) != 0 && len(info[0].ServiceEndpoints) != 0 { 130 endpoint := &info[0].ServiceEndpoints[0] 131 path = endpoint.Url 132 133 if u, err := url.Parse(path); err == nil { 134 // Set thumbprint only for endpoints on hosts outside this vCenter. 135 // Platform Services may live on multiple hosts. 136 if c.URL().Host != u.Host && c.Thumbprint(u.Host) == "" { 137 c.SetThumbprint(u.Host, endpointThumbprint(endpoint)) 138 } 139 } 140 } 141 } 142 return path 143 } 144 145 // endpointThumbprint converts the base64 encoded endpoint certificate to a SHA1 thumbprint. 146 func endpointThumbprint(endpoint *types.LookupServiceRegistrationEndpoint) string { 147 if len(endpoint.SslTrust) == 0 { 148 return "" 149 } 150 enc := endpoint.SslTrust[0] 151 152 b, err := base64.StdEncoding.DecodeString(enc) 153 if err != nil { 154 log.Printf("base64.Decode(%q): %s", enc, err) 155 return "" 156 } 157 158 cert, err := x509.ParseCertificate(b) 159 if err != nil { 160 log.Printf("x509.ParseCertificate(%q): %s", enc, err) 161 return "" 162 } 163 164 return soap.ThumbprintSHA1(cert) 165 }