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:
parent
a2d975e02b
commit
85907f6947
@ -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))
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user