mirror of
https://github.com/k3s-io/k3s.git
synced 2024-06-07 19:41:36 +00:00
154 lines
6.4 KiB
Go
154 lines
6.4 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package auth
|
|
|
|
import (
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strings"
|
|
|
|
"github.com/Azure/go-autorest/autorest/adal"
|
|
"github.com/Azure/go-autorest/autorest/azure"
|
|
"golang.org/x/crypto/pkcs12"
|
|
|
|
"k8s.io/klog"
|
|
)
|
|
|
|
var (
|
|
// ErrorNoAuth indicates that no credentials are provided.
|
|
ErrorNoAuth = fmt.Errorf("no credentials provided for Azure cloud provider")
|
|
// ADFSIdentitySystem indicates value of tenantId for ADFS on Azure Stack.
|
|
ADFSIdentitySystem = "ADFS"
|
|
)
|
|
|
|
// AzureAuthConfig holds auth related part of cloud config
|
|
type AzureAuthConfig struct {
|
|
// The cloud environment identifier. Takes values from https://github.com/Azure/go-autorest/blob/ec5f4903f77ed9927ac95b19ab8e44ada64c1356/autorest/azure/environments.go#L13
|
|
Cloud string `json:"cloud,omitempty" yaml:"cloud,omitempty"`
|
|
// The AAD Tenant ID for the Subscription that the cluster is deployed in
|
|
TenantID string `json:"tenantId,omitempty" yaml:"tenantId,omitempty"`
|
|
// The ClientID for an AAD application with RBAC access to talk to Azure RM APIs
|
|
AADClientID string `json:"aadClientId,omitempty" yaml:"aadClientId,omitempty"`
|
|
// The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs
|
|
AADClientSecret string `json:"aadClientSecret,omitempty" yaml:"aadClientSecret,omitempty"`
|
|
// The path of a client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
|
AADClientCertPath string `json:"aadClientCertPath,omitempty" yaml:"aadClientCertPath,omitempty"`
|
|
// The password of the client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
|
AADClientCertPassword string `json:"aadClientCertPassword,omitempty" yaml:"aadClientCertPassword,omitempty"`
|
|
// Use managed service identity for the virtual machine to access Azure ARM APIs
|
|
UseManagedIdentityExtension bool `json:"useManagedIdentityExtension,omitempty" yaml:"useManagedIdentityExtension,omitempty"`
|
|
// UserAssignedIdentityID contains the Client ID of the user assigned MSI which is assigned to the underlying VMs. If empty the user assigned identity is not used.
|
|
// More details of the user assigned identity can be found at: https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview
|
|
// For the user assigned identity specified here to be used, the UseManagedIdentityExtension has to be set to true.
|
|
UserAssignedIdentityID string `json:"userAssignedIdentityID,omitempty" yaml:"userAssignedIdentityID,omitempty"`
|
|
// The ID of the Azure Subscription that the cluster is deployed in
|
|
SubscriptionID string `json:"subscriptionId,omitempty" yaml:"subscriptionId,omitempty"`
|
|
// Identity system value for the deployment. This gets populate for Azure Stack case.
|
|
IdentitySystem string `json:"identitySystem,omitempty" yaml:"identitySystem,omitempty"`
|
|
}
|
|
|
|
// GetServicePrincipalToken creates a new service principal token based on the configuration
|
|
func GetServicePrincipalToken(config *AzureAuthConfig, env *azure.Environment) (*adal.ServicePrincipalToken, error) {
|
|
var tenantID string
|
|
if strings.EqualFold(config.IdentitySystem, ADFSIdentitySystem) {
|
|
tenantID = "adfs"
|
|
} else {
|
|
tenantID = config.TenantID
|
|
}
|
|
|
|
if config.UseManagedIdentityExtension {
|
|
klog.V(2).Infoln("azure: using managed identity extension to retrieve access token")
|
|
msiEndpoint, err := adal.GetMSIVMEndpoint()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Getting the managed service identity endpoint: %v", err)
|
|
}
|
|
if len(config.UserAssignedIdentityID) > 0 {
|
|
klog.V(4).Info("azure: using User Assigned MSI ID to retrieve access token")
|
|
return adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint,
|
|
env.ServiceManagementEndpoint,
|
|
config.UserAssignedIdentityID)
|
|
}
|
|
klog.V(4).Info("azure: using System Assigned MSI to retrieve access token")
|
|
return adal.NewServicePrincipalTokenFromMSI(
|
|
msiEndpoint,
|
|
env.ServiceManagementEndpoint)
|
|
}
|
|
|
|
oauthConfig, err := adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, tenantID, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating the OAuth config: %v", err)
|
|
}
|
|
|
|
if len(config.AADClientSecret) > 0 {
|
|
klog.V(2).Infoln("azure: using client_id+client_secret to retrieve access token")
|
|
return adal.NewServicePrincipalToken(
|
|
*oauthConfig,
|
|
config.AADClientID,
|
|
config.AADClientSecret,
|
|
env.ServiceManagementEndpoint)
|
|
}
|
|
|
|
if len(config.AADClientCertPath) > 0 && len(config.AADClientCertPassword) > 0 {
|
|
klog.V(2).Infoln("azure: using jwt client_assertion (client_cert+client_private_key) to retrieve access token")
|
|
certData, err := ioutil.ReadFile(config.AADClientCertPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading the client certificate from file %s: %v", config.AADClientCertPath, err)
|
|
}
|
|
certificate, privateKey, err := decodePkcs12(certData, config.AADClientCertPassword)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decoding the client certificate: %v", err)
|
|
}
|
|
return adal.NewServicePrincipalTokenFromCertificate(
|
|
*oauthConfig,
|
|
config.AADClientID,
|
|
certificate,
|
|
privateKey,
|
|
env.ServiceManagementEndpoint)
|
|
}
|
|
|
|
return nil, ErrorNoAuth
|
|
}
|
|
|
|
// ParseAzureEnvironment returns azure environment by name
|
|
func ParseAzureEnvironment(cloudName string) (*azure.Environment, error) {
|
|
var env azure.Environment
|
|
var err error
|
|
if cloudName == "" {
|
|
env = azure.PublicCloud
|
|
} else {
|
|
env, err = azure.EnvironmentFromName(cloudName)
|
|
}
|
|
return &env, err
|
|
}
|
|
|
|
// decodePkcs12 decodes a PKCS#12 client certificate by extracting the public certificate and
|
|
// the private RSA key
|
|
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
|
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("decoding the PKCS#12 client certificate: %v", err)
|
|
}
|
|
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
|
|
if !isRsaKey {
|
|
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain a RSA private key")
|
|
}
|
|
|
|
return certificate, rsaPrivateKey, nil
|
|
}
|