mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
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
This commit is contained in:
commit
db46e4f8e6
@ -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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user