Skip to content

Commit

Permalink
Add flags in Azure cluster create CLI for WI
Browse files Browse the repository at this point in the history
This commit adds flags in the Azure cluster create CLI to support
workload identity as the authentication type. Flags added allow the MSI
client IDs to be passed in on creation as well as the issuer URL and
service account token issue private key.

Signed-off-by: Bryan Cox <[email protected]>
  • Loading branch information
bryan-cox committed Nov 11, 2024
1 parent 0bf8c04 commit 9eb898d
Showing 1 changed file with 71 additions and 15 deletions.
86 changes: 71 additions & 15 deletions cmd/cluster/azure/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"sigs.k8s.io/yaml"
)

const SATokenIssuerSecret = "sa-token-issuer-key"

func DefaultOptions(client crclient.Client, log logr.Logger) (*RawCreateOptions, error) {
rawCreateOptions := &RawCreateOptions{
Location: "eastus",
Expand Down Expand Up @@ -66,6 +68,12 @@ func bindCoreOptions(opts *RawCreateOptions, flags *pflag.FlagSet) {
flags.StringVar(&opts.NetworkSecurityGroupID, "network-security-group-id", opts.NetworkSecurityGroupID, "The Network Security Group ID to use in the default NodePool.")
flags.StringToStringVarP(&opts.ResourceGroupTags, "resource-group-tags", "t", opts.ResourceGroupTags, "Additional tags to apply to the resource group created (e.g. 'key1=value1,key2=value2')")
flags.StringVar(&opts.SubnetID, "subnet-id", opts.SubnetID, "The subnet ID where the VMs will be placed.")
flags.StringVar(&opts.IssuerURL, "oidc-issuer-url", "", "The OIDC provider issuer URL")
flags.StringVar(&opts.ServiceAccountTokenIssuerKeyPath, "sa-token-issuer-private-key-path", "", "The path to the private key for the service account token issuer")
flags.StringVar(&opts.ImageRegistryClientID, "ir-client-id", "", "The MSI client ID associated with the image-registry controller service-account on the HostedCluster.")
flags.StringVar(&opts.CSIDiskClientID, "csi-disk-client-id", "", "The MSI client ID associated with the CSI disk controller service-account on the HostedCluster.")
flags.StringVar(&opts.CSIFileClientID, "csi-file-client-id", "", "The MSI client ID associated with the CSI file controller service-account on the HostedCluster.")
flags.StringVar(&opts.CloudNetworkConfigClientID, "cncc-client-id", "", "The MSI client ID associated with the cloud network configuration controller service-account on the HostedCluster.")

if opts.TechPreviewEnabled {
flags.StringVar(&opts.KMSClientID, "kms-client-id", opts.KMSClientID, "The client ID of a managed identity used in KMS to authenticate to Azure.")
Expand All @@ -82,20 +90,26 @@ func BindDeveloperOptions(opts *RawCreateOptions, flags *pflag.FlagSet) {
}

type RawCreateOptions struct {
CredentialsFile string
Location string
EncryptionKeyID string
AvailabilityZones []string
ResourceGroupName string
VnetID string
NetworkSecurityGroupID string
ResourceGroupTags map[string]string
SubnetID string
RHCOSImage string
KMSClientID string
KMSCertName string
TechPreviewEnabled bool
KeyVaultInfo ManagementKeyVaultInfo
CredentialsFile string
Location string
EncryptionKeyID string
AvailabilityZones []string
ResourceGroupName string
VnetID string
NetworkSecurityGroupID string
ResourceGroupTags map[string]string
SubnetID string
RHCOSImage string
KMSClientID string
KMSCertName string
TechPreviewEnabled bool
KeyVaultInfo ManagementKeyVaultInfo
IssuerURL string
ServiceAccountTokenIssuerKeyPath string
CSIDiskClientID string
CSIFileClientID string
CloudNetworkConfigClientID string
ImageRegistryClientID string

NodePoolOpts *azurenodepool.RawAzurePlatformCreateOptions
}
Expand Down Expand Up @@ -255,6 +269,14 @@ func (o *CreateOptions) ApplyPlatformSpecifics(cluster *hyperv1.HostedCluster) e

cluster.Spec.InfraID = o.infra.InfraID

cluster.Spec.IssuerURL = o.IssuerURL

if len(o.ServiceAccountTokenIssuerKeyPath) > 0 {
cluster.Spec.ServiceAccountSigningKey = &corev1.LocalObjectReference{
Name: SATokenIssuerSecret,
}
}

cluster.Spec.Platform = hyperv1.PlatformSpec{
Type: hyperv1.AzurePlatform,
Azure: &hyperv1.AzurePlatformSpec{
Expand All @@ -270,6 +292,10 @@ func (o *CreateOptions) ApplyPlatformSpecifics(cluster *hyperv1.HostedCluster) e

if o.TechPreviewEnabled {
cluster.Spec.Platform.Azure.ManagedIdentities = o.infra.ControlPlaneMIs
cluster.Spec.Platform.Azure.ManagedIdentities.DataPlane.ImageRegistryMSIClientID = o.ImageRegistryClientID
cluster.Spec.Platform.Azure.ManagedIdentities.DataPlane.DiskMSIClientID = o.CSIDiskClientID
cluster.Spec.Platform.Azure.ManagedIdentities.DataPlane.FileMSIClientID = o.CSIFileClientID
cluster.Spec.Platform.Azure.ManagedIdentities.DataPlane.CloudNetworkConfigMSIClientID = o.CloudNetworkConfigClientID
}

if o.encryptionKey != nil {
Expand Down Expand Up @@ -336,6 +362,19 @@ func credentialSecret(namespace, name string) *corev1.Secret {
}
}

func serviceAccountTokenIssuerSecret(namespace, name string) *corev1.Secret {
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
}

func (o *CreateOptions) GenerateNodePools(constructor core.DefaultNodePoolConstructor) []*hyperv1.NodePool {
var vmImage hyperv1.AzureVMImage
if o.MarketplacePublisher == "" {
Expand Down Expand Up @@ -453,14 +492,31 @@ func (o *CreateOptions) GenerateNodePools(constructor core.DefaultNodePoolConstr
}

func (o *CreateOptions) GenerateResources() ([]crclient.Object, error) {
var objects []crclient.Object

secret := credentialSecret(o.namespace, o.name)
secret.Data = map[string][]byte{
"AZURE_SUBSCRIPTION_ID": []byte(o.creds.SubscriptionID),
"AZURE_TENANT_ID": []byte(o.creds.TenantID),
"AZURE_CLIENT_ID": []byte(o.creds.ClientID),
"AZURE_CLIENT_SECRET": []byte(o.creds.ClientSecret),
}
return []crclient.Object{secret}, nil
objects = append(objects, secret)

if len(o.ServiceAccountTokenIssuerKeyPath) > 0 {
privateKey, err := os.ReadFile(o.ServiceAccountTokenIssuerKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read pull secret file: %w", err)
}

saSecret := serviceAccountTokenIssuerSecret(o.namespace, SATokenIssuerSecret)
saSecret.Data = map[string][]byte{
"key": privateKey,
}
objects = append(objects, saSecret)
}

return objects, nil
}

var _ core.Platform = (*CreateOptions)(nil)
Expand Down

0 comments on commit 9eb898d

Please sign in to comment.