mirror of https://github.com/k3s-io/k3s
Add client cert authentication for Azure cloud provider
parent
cdc9770346
commit
5462d06ce3
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -34,6 +36,7 @@ import (
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/crypto/pkcs12"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,6 +83,10 @@ type Config struct {
|
||||||
AADClientID string `json:"aadClientId" yaml:"aadClientId"`
|
AADClientID string `json:"aadClientId" yaml:"aadClientId"`
|
||||||
// The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs
|
// The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs
|
||||||
AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
|
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
|
// Enable exponential backoff to manage resource request retries
|
||||||
CloudProviderBackoff bool `json:"cloudProviderBackoff" yaml:"cloudProviderBackoff"`
|
CloudProviderBackoff bool `json:"cloudProviderBackoff" yaml:"cloudProviderBackoff"`
|
||||||
// Backoff retry limit
|
// Backoff retry limit
|
||||||
|
@ -119,6 +126,54 @@ func init() {
|
||||||
cloudprovider.RegisterCloudProvider(CloudProviderName, NewCloud)
|
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
|
// NewCloud returns a Cloud with initialized clients
|
||||||
func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
||||||
var az Cloud
|
var az Cloud
|
||||||
|
@ -141,16 +196,7 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthConfig, err := az.Environment.OAuthConfigForTenant(az.TenantID)
|
servicePrincipalToken, err := newServicePrincipalToken(&az)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
servicePrincipalToken, err := azure.NewServicePrincipalToken(
|
|
||||||
*oauthConfig,
|
|
||||||
az.AADClientID,
|
|
||||||
az.AADClientSecret,
|
|
||||||
az.Environment.ServiceManagementEndpoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -585,6 +585,8 @@ func TestNewCloudFromJSON(t *testing.T) {
|
||||||
"subscriptionId": "--subscription-id--",
|
"subscriptionId": "--subscription-id--",
|
||||||
"aadClientId": "--aad-client-id--",
|
"aadClientId": "--aad-client-id--",
|
||||||
"aadClientSecret": "--aad-client-secret--",
|
"aadClientSecret": "--aad-client-secret--",
|
||||||
|
"aadClientCertPath": "--aad-client-cert-path--",
|
||||||
|
"aadClientCertPassword": "--aad-client-cert-password--",
|
||||||
"resourceGroup": "--resource-group--",
|
"resourceGroup": "--resource-group--",
|
||||||
"location": "--location--",
|
"location": "--location--",
|
||||||
"subnetName": "--subnet-name--",
|
"subnetName": "--subnet-name--",
|
||||||
|
@ -625,6 +627,8 @@ tenantId: --tenant-id--
|
||||||
subscriptionId: --subscription-id--
|
subscriptionId: --subscription-id--
|
||||||
aadClientId: --aad-client-id--
|
aadClientId: --aad-client-id--
|
||||||
aadClientSecret: --aad-client-secret--
|
aadClientSecret: --aad-client-secret--
|
||||||
|
aadClientCertPath: --aad-client-cert-path--
|
||||||
|
aadClientCertPassword: --aad-client-cert-password--
|
||||||
resourceGroup: --resource-group--
|
resourceGroup: --resource-group--
|
||||||
location: --location--
|
location: --location--
|
||||||
subnetName: --subnet-name--
|
subnetName: --subnet-name--
|
||||||
|
@ -659,6 +663,12 @@ func validateConfig(t *testing.T, config string) {
|
||||||
if azureCloud.AADClientSecret != "--aad-client-secret--" {
|
if azureCloud.AADClientSecret != "--aad-client-secret--" {
|
||||||
t.Errorf("got incorrect value for AADClientSecret")
|
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--" {
|
if azureCloud.ResourceGroup != "--resource-group--" {
|
||||||
t.Errorf("got incorrect value for ResourceGroup")
|
t.Errorf("got incorrect value for ResourceGroup")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue