github.com/vmware/govmomi@v0.37.2/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  }