| /* |
| 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" |
| |
| "github.com/Azure/go-autorest/autorest/adal" |
| "github.com/Azure/go-autorest/autorest/azure" |
| "golang.org/x/crypto/pkcs12" |
| "k8s.io/klog" |
| ) |
| |
| // 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" yaml:"cloud"` |
| // The AAD Tenant ID for the Subscription that the cluster is deployed in |
| TenantID string `json:"tenantId" yaml:"tenantId"` |
| // The ClientID for an AAD application with RBAC access to talk to Azure RM APIs |
| AADClientID string `json:"aadClientId" yaml:"aadClientId"` |
| // The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs |
| AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"` |
| // The path of a client certificate for an AAD application with RBAC access to talk to Azure RM APIs |
| AADClientCertPath string `json:"aadClientCertPath" yaml:"aadClientCertPath"` |
| // The password of the client certificate for an AAD application with RBAC access to talk to Azure RM APIs |
| AADClientCertPassword string `json:"aadClientCertPassword" yaml:"aadClientCertPassword"` |
| // Use managed service identity for the virtual machine to access Azure ARM APIs |
| UseManagedIdentityExtension bool `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"` |
| // 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" yaml:"userAssignedIdentityID"` |
| // The ID of the Azure Subscription that the cluster is deployed in |
| SubscriptionID string `json:"subscriptionId" yaml:"subscriptionId"` |
| } |
| |
| // GetServicePrincipalToken creates a new service principal token based on the configuration |
| func GetServicePrincipalToken(config *AzureAuthConfig, env *azure.Environment) (*adal.ServicePrincipalToken, error) { |
| 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.NewOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID) |
| 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, fmt.Errorf("No credentials provided for AAD application %s", config.AADClientID) |
| } |
| |
| // 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 |
| } |