Commit db46e4f8 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #47286 from cosmincojocar/client_cert_azure_cloud_provider

Automatic merge from submit-queue (batch tested with PRs 47286, 47729) Add client certificate authentication to Azure cloud provider This adds support for client cert authentication in Azure cloud provider. The certificate can be provided in PKCS #12 format with password protection. Not that this authentication will be active only when no client secret is configured. cc @brendandburns @colemickens
parents 06fcfe23 0235cb9e
......@@ -42,6 +42,7 @@ go_library(
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/rubiojr/go-vhd/vhd:go_default_library",
"//vendor/golang.org/x/crypto/pkcs12:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
......
......@@ -17,6 +17,8 @@ limitations under the License.
package azure
import (
"crypto/rsa"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
......@@ -34,6 +36,7 @@ import (
"github.com/Azure/go-autorest/autorest/azure"
"github.com/ghodss/yaml"
"github.com/golang/glog"
"golang.org/x/crypto/pkcs12"
"k8s.io/apimachinery/pkg/util/wait"
)
......@@ -80,6 +83,10 @@ type Config struct {
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"`
// Enable exponential backoff to manage resource request retries
CloudProviderBackoff bool `json:"cloudProviderBackoff" yaml:"cloudProviderBackoff"`
// Backoff retry limit
......@@ -119,6 +126,54 @@ func init() {
cloudprovider.RegisterCloudProvider(CloudProviderName, NewCloud)
}
// 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
}
// newServicePrincipalToken creates a new service principal token based on the configuration
func newServicePrincipalToken(az *Cloud) (*azure.ServicePrincipalToken, error) {
oauthConfig, err := az.Environment.OAuthConfigForTenant(az.TenantID)
if err != nil {
return nil, fmt.Errorf("creating the OAuth config: %v", err)
}
if len(az.AADClientSecret) > 0 {
return azure.NewServicePrincipalToken(
*oauthConfig,
az.AADClientID,
az.AADClientSecret,
az.Environment.ServiceManagementEndpoint)
} else if len(az.AADClientCertPath) > 0 && len(az.AADClientCertPassword) > 0 {
certData, err := ioutil.ReadFile(az.AADClientCertPath)
if err != nil {
return nil, fmt.Errorf("reading the client certificate from file %s: %v", az.AADClientCertPath, err)
}
certificate, privateKey, err := decodePkcs12(certData, az.AADClientSecret)
if err != nil {
return nil, fmt.Errorf("decoding the client certificate: %v", err)
}
return azure.NewServicePrincipalTokenFromCertificate(
*oauthConfig,
az.AADClientID,
certificate,
privateKey,
az.Environment.ServiceManagementEndpoint)
} else {
return nil, fmt.Errorf("No credentials provided for AAD application %s", az.AADClientID)
}
}
// NewCloud returns a Cloud with initialized clients
func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
var az Cloud
......@@ -141,16 +196,7 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
}
}
oauthConfig, err := az.Environment.OAuthConfigForTenant(az.TenantID)
if err != nil {
return nil, err
}
servicePrincipalToken, err := azure.NewServicePrincipalToken(
*oauthConfig,
az.AADClientID,
az.AADClientSecret,
az.Environment.ServiceManagementEndpoint)
servicePrincipalToken, err := newServicePrincipalToken(&az)
if err != nil {
return nil, err
}
......
......@@ -585,6 +585,8 @@ func TestNewCloudFromJSON(t *testing.T) {
"subscriptionId": "--subscription-id--",
"aadClientId": "--aad-client-id--",
"aadClientSecret": "--aad-client-secret--",
"aadClientCertPath": "--aad-client-cert-path--",
"aadClientCertPassword": "--aad-client-cert-password--",
"resourceGroup": "--resource-group--",
"location": "--location--",
"subnetName": "--subnet-name--",
......@@ -606,15 +608,20 @@ func TestNewCloudFromJSON(t *testing.T) {
// Test Backoff and Rate Limit defaults (json)
func TestCloudDefaultConfigFromJSON(t *testing.T) {
config := `{}`
config := `{
"aadClientId": "--aad-client-id--",
"aadClientSecret": "--aad-client-secret--"
}`
validateEmptyConfig(t, config)
}
// Test Backoff and Rate Limit defaults (yaml)
func TestCloudDefaultConfigFromYAML(t *testing.T) {
config := ``
config := `
aadClientId: --aad-client-id--
aadClientSecret: --aad-client-secret--
`
validateEmptyConfig(t, config)
}
......@@ -625,6 +632,8 @@ tenantId: --tenant-id--
subscriptionId: --subscription-id--
aadClientId: --aad-client-id--
aadClientSecret: --aad-client-secret--
aadClientCertPath: --aad-client-cert-path--
aadClientCertPassword: --aad-client-cert-password--
resourceGroup: --resource-group--
location: --location--
subnetName: --subnet-name--
......@@ -659,6 +668,12 @@ func validateConfig(t *testing.T, config string) {
if azureCloud.AADClientSecret != "--aad-client-secret--" {
t.Errorf("got incorrect value for AADClientSecret")
}
if azureCloud.AADClientCertPath != "--aad-client-cert-path--" {
t.Errorf("got incorrect value for AADClientCertPath")
}
if azureCloud.AADClientCertPassword != "--aad-client-cert-password--" {
t.Errorf("got incorrect value for AADClientCertPassword")
}
if azureCloud.ResourceGroup != "--resource-group--" {
t.Errorf("got incorrect value for ResourceGroup")
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment