vSphere: add token auth support for tags client

SAML auth support for the vCenter rest API endpoint came to govmomi
a bit after Zone support came to vSphere Cloud Provider.

Fixes #75511
This commit is contained in:
Doug MacEachern 2019-03-20 10:32:23 -07:00
parent a2d975e02b
commit 85907f6947
3 changed files with 50 additions and 18 deletions

View File

@ -86,34 +86,27 @@ func (connection *VSphereConnection) Connect(ctx context.Context) error {
return nil return nil
} }
// login calls SessionManager.LoginByToken if certificate and private key are configured, // Signer returns an sts.Signer for use with SAML token auth if connection is configured for such.
// otherwise calls SessionManager.Login with user and password. // Returns nil if username/password auth is configured for the connection.
func (connection *VSphereConnection) login(ctx context.Context, client *vim25.Client) error { func (connection *VSphereConnection) Signer(ctx context.Context, client *vim25.Client) (*sts.Signer, error) {
m := session.NewManager(client)
connection.credentialsLock.Lock()
defer connection.credentialsLock.Unlock()
// TODO: Add separate fields for certificate and private-key. // TODO: Add separate fields for certificate and private-key.
// For now we can leave the config structs and validation as-is and // For now we can leave the config structs and validation as-is and
// decide to use LoginByToken if the username value is PEM encoded. // decide to use LoginByToken if the username value is PEM encoded.
b, _ := pem.Decode([]byte(connection.Username)) b, _ := pem.Decode([]byte(connection.Username))
if b == nil { if b == nil {
klog.V(3).Infof("SessionManager.Login with username '%s'", connection.Username) return nil, nil
return m.Login(ctx, neturl.UserPassword(connection.Username, connection.Password))
} }
klog.V(3).Infof("SessionManager.LoginByToken with certificate '%s'", connection.Username)
cert, err := tls.X509KeyPair([]byte(connection.Username), []byte(connection.Password)) cert, err := tls.X509KeyPair([]byte(connection.Username), []byte(connection.Password))
if err != nil { if err != nil {
klog.Errorf("Failed to load X509 key pair. err: %+v", err) klog.Errorf("Failed to load X509 key pair. err: %+v", err)
return err return nil, err
} }
tokens, err := sts.NewClient(ctx, client) tokens, err := sts.NewClient(ctx, client)
if err != nil { if err != nil {
klog.Errorf("Failed to create STS client. err: %+v", err) klog.Errorf("Failed to create STS client. err: %+v", err)
return err return nil, err
} }
req := sts.TokenRequest{ req := sts.TokenRequest{
@ -123,9 +116,31 @@ func (connection *VSphereConnection) login(ctx context.Context, client *vim25.Cl
signer, err := tokens.Issue(ctx, req) signer, err := tokens.Issue(ctx, req)
if err != nil { if err != nil {
klog.Errorf("Failed to issue SAML token. err: %+v", err) klog.Errorf("Failed to issue SAML token. err: %+v", err)
return nil, err
}
return signer, nil
}
// login calls SessionManager.LoginByToken if certificate and private key are configured,
// otherwise calls SessionManager.Login with user and password.
func (connection *VSphereConnection) login(ctx context.Context, client *vim25.Client) error {
m := session.NewManager(client)
connection.credentialsLock.Lock()
defer connection.credentialsLock.Unlock()
signer, err := connection.Signer(ctx, client)
if err != nil {
return err return err
} }
if signer == nil {
klog.V(3).Infof("SessionManager.Login with username %q", connection.Username)
return m.Login(ctx, neturl.UserPassword(connection.Username, connection.Password))
}
klog.V(3).Infof("SessionManager.LoginByToken with certificate %q", connection.Username)
header := soap.Header{Security: signer} header := soap.Header{Security: signer}
return m.LoginByToken(client.WithHeader(ctx, header)) return m.LoginByToken(client.WithHeader(ctx, header))

View File

@ -38,7 +38,7 @@ import (
"github.com/vmware/govmomi/vapi/tags" "github.com/vmware/govmomi/vapi/tags"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
vmwaretypes "github.com/vmware/govmomi/vim25/types" vmwaretypes "github.com/vmware/govmomi/vim25/types"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types" k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
@ -1432,10 +1432,20 @@ func (vs *VSphere) NodeManager() (nodeManager *NodeManager) {
func withTagsClient(ctx context.Context, connection *vclib.VSphereConnection, f func(c *rest.Client) error) error { func withTagsClient(ctx context.Context, connection *vclib.VSphereConnection, f func(c *rest.Client) error) error {
c := rest.NewClient(connection.Client) c := rest.NewClient(connection.Client)
user := url.UserPassword(connection.Username, connection.Password) signer, err := connection.Signer(ctx, connection.Client)
if err := c.Login(ctx, user); err != nil { if err != nil {
return err return err
} }
if signer == nil {
user := url.UserPassword(connection.Username, connection.Password)
err = c.Login(ctx, user)
} else {
err = c.LoginByToken(c.WithSigner(ctx, signer))
}
if err != nil {
return err
}
defer func() { defer func() {
if err := c.Logout(ctx); err != nil { if err := c.Logout(ctx); err != nil {
klog.Errorf("failed to logout: %v", err) klog.Errorf("failed to logout: %v", err)

View File

@ -344,6 +344,10 @@ func TestZones(t *testing.T) {
cfg, cleanup := configFromSim() cfg, cleanup := configFromSim()
defer cleanup() defer cleanup()
// Configure for SAML token auth
cfg.Global.User = localhostCert
cfg.Global.Password = localhostKey
// Create vSphere configuration object // Create vSphere configuration object
vs, err := newControllerNode(cfg) vs, err := newControllerNode(cfg)
if err != nil { if err != nil {
@ -382,8 +386,11 @@ func TestZones(t *testing.T) {
// Tag manager instance // Tag manager instance
m := tags.NewManager(rest.NewClient(vsi.conn.Client)) m := tags.NewManager(rest.NewClient(vsi.conn.Client))
user := url.UserPassword(vsi.conn.Username, vsi.conn.Password) signer, err := vsi.conn.Signer(ctx, vsi.conn.Client)
if err = m.Login(ctx, user); err != nil { if err != nil {
t.Fatal(err)
}
if err = m.LoginByToken(m.WithSigner(ctx, signer)); err != nil {
t.Fatal(err) t.Fatal(err)
} }