Skip to content

Commit

Permalink
Fix VM creation and update when sshAccess is disabled. (#80)
Browse files Browse the repository at this point in the history
* Generate "fake" PublicKey if sshAccess is disabled

* Add unit test for getVMParameters

* Add V(3) log when fake key is created
  • Loading branch information
AleksandarSavchev authored Apr 17, 2023
1 parent 1d20246 commit 7c79593
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 5 deletions.
4 changes: 2 additions & 2 deletions pkg/azure/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1949,7 +1949,7 @@ func assertVMResourcesForMachineCreation(
},
StatusCode: 404,
})
VMParameters := getVMParameters(vmName, vmImageRef, NICId, providerSpec, machineRequest.Secret)
VMParameters, _ := getVMParameters(vmName, vmImageRef, NICId, providerSpec, machineRequest.Secret)
fakeClients.VM.EXPECT().CreateOrUpdate(gomock.Any(), resourceGroupName, *VMParameters.Name, VMParameters).Return(compute.VirtualMachinesCreateOrUpdateFuture{}, *vmCreateOrUpdateError)
} else {

Expand All @@ -1959,7 +1959,7 @@ func assertVMResourcesForMachineCreation(
},
StatusCode: 404,
})
VMParameters := getVMParameters(vmName, vmImageRef, NICId, providerSpec, machineRequest.Secret)
VMParameters, _ := getVMParameters(vmName, vmImageRef, NICId, providerSpec, machineRequest.Secret)
fakeClients.VM.EXPECT().CreateOrUpdate(gomock.Any(), resourceGroupName, *VMParameters.Name, VMParameters).Return(VMFutureAPI, nil)
}
}
Expand Down
54 changes: 51 additions & 3 deletions pkg/azure/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ SPDX-License-Identifier: Apache-2.0
package azure

import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"fmt"
Expand All @@ -17,6 +20,8 @@ import (
"sync"
"time"

"golang.org/x/crypto/ssh"

"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
"github.com/Azure/azure-sdk-for-go/profiles/latest/network/mgmt/network"
"github.com/Azure/go-autorest/autorest"
Expand Down Expand Up @@ -157,7 +162,31 @@ func generateDataDisks(vmName string, azureDataDisks []api.AzureDataDisk) []comp
return dataDisks
}

func getVMParameters(vmName string, image *compute.VirtualMachineImage, networkInterfaceReferenceID string, providerSpec *api.AzureProviderSpec, secret *corev1.Secret) compute.VirtualMachine {
func generateSSHAuthorizedKeys(privateKey *rsa.PrivateKey) ([]byte, error) {
pubKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, err
}

publicKey := ssh.MarshalAuthorizedKey(pubKey)
return bytes.Trim(publicKey, "\x0a"), nil
}

func generateDummyPublicKey() (string, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return "", err
}

sshPublicKey, err := generateSSHAuthorizedKeys(privateKey)
if err != nil {
return "", err
}

return string(sshPublicKey), nil
}

func getVMParameters(vmName string, image *compute.VirtualMachineImage, networkInterfaceReferenceID string, providerSpec *api.AzureProviderSpec, secret *corev1.Secret) (compute.VirtualMachine, error) {

var (
diskName = dependencyNameFromVMName(vmName, diskSuffix)
Expand Down Expand Up @@ -273,7 +302,23 @@ func getVMParameters(vmName string, image *compute.VirtualMachineImage, networkI
}
}

return VMParameters
if len(providerSpec.Properties.OsProfile.LinuxConfiguration.SSH.PublicKeys.KeyData) == 0 {
// We create a dummy SSH Public key, since it is required for VM creation to have a public key
publicKey, err := generateDummyPublicKey()
if err != nil {
return compute.VirtualMachine{}, err
}

VMParameters.VirtualMachineProperties.OsProfile.LinuxConfiguration.SSH.PublicKeys = &[]compute.SSHPublicKey{
{
Path: &providerSpec.Properties.OsProfile.LinuxConfiguration.SSH.PublicKeys.Path,
KeyData: &publicKey,
},
}
klog.V(3).Info("Fake SSH Public key has been set to allow VM creation")
}

return VMParameters, nil
}

func getImageReference(providerSpec *api.AzureProviderSpec) compute.ImageReference {
Expand Down Expand Up @@ -505,7 +550,10 @@ func (d *MachinePlugin) createVMNicDisk(req *driver.CreateMachineRequest) (*comp
}

// Creating VMParameters for new VM creation request
VMParameters := getVMParameters(vmName, vmImageRef, *NIC.ID, providerSpec, req.Secret)
VMParameters, err := getVMParameters(vmName, vmImageRef, *NIC.ID, providerSpec, req.Secret)
if err != nil {
return nil, err
}
// VM creation request
klog.V(3).Infof("VM creation began for %q", vmName)
VMFuture, err := clients.GetVM().CreateOrUpdate(ctx, resourceGroupName, *VMParameters.Name, VMParameters)
Expand Down
105 changes: 105 additions & 0 deletions pkg/azure/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Gardener contributors
SPDX-License-Identifier: Apache-2.0
*/

// Package azure contains the cloud provider specific implementations to manage machines
package azure

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/pointer"

api "github.com/gardener/machine-controller-manager-provider-azure/pkg/azure/apis"
)

var _ = Describe("Utils", func() {

Describe("generateDummyPublicKey", func() {

It("should properly generate PublicKey string", func() {
publicKey, err := generateDummyPublicKey()
Expect(err).NotTo(HaveOccurred())

Expect(publicKey).NotTo(Equal(""))
})
})

Describe("getVMParameters", func() {
var vmName string
var networkInterfaceReferenceID string
var providerSpec *api.AzureProviderSpec
var azureProviderSecret *corev1.Secret

BeforeEach(func() {
vmName = "testName"
networkInterfaceReferenceID = "testID"
azureProviderSecret = &corev1.Secret{
Data: map[string][]byte{
"userData": []byte("dummy-data"),
"azureClientId": []byte("dummy-client-id"),
"azureClientSecret": []byte("dummy-client-secret"),
"azureSubscriptionId": []byte("dummy-subcription-id"),
"azureTenantId": []byte("dummy-tenant-id"),
},
}
providerSpec = &api.AzureProviderSpec{
Location: "westeurope",
Properties: api.AzureVirtualMachineProperties{
HardwareProfile: api.AzureHardwareProfile{
VMSize: "Standard_DS2_v2",
},
StorageProfile: api.AzureStorageProfile{
ImageReference: api.AzureImageReference{
URN: pointer.String("sap:gardenlinux:greatest:27.1.0"),
},
OsDisk: api.AzureOSDisk{
Caching: "None",
ManagedDisk: api.AzureManagedDiskParameters{
StorageAccountType: "Standard_LRS",
},
DiskSizeGB: 50,
CreateOption: "FromImage",
},
DataDisks: []api.AzureDataDisk{
{
StorageAccountType: "Standard_LRS",
Lun: getInt32Pointer(1),
DiskSizeGB: 50,
},
},
},
OsProfile: api.AzureOSProfile{
AdminUsername: "core",
LinuxConfiguration: api.AzureLinuxConfiguration{
DisablePasswordAuthentication: true,
SSH: api.AzureSSHConfiguration{
PublicKeys: api.AzureSSHPublicKey{
Path: "/home/core/.ssh/authorized_keys",
KeyData: "dummy keyData",
},
},
},
},
Zone: pointer.Int(2),
},
ResourceGroup: "shoot--project--seed-az",
SubnetInfo: api.AzureSubnetInfo{
VnetName: "shoot--project--seed-az",
SubnetName: "shoot--project--seed-az-nodes",
},
}
})

It("should properly generate PublicKey when missing", func() {
providerSpec.Properties.OsProfile.LinuxConfiguration.SSH.PublicKeys.KeyData = ""
VMParameters, err := getVMParameters(vmName, nil, networkInterfaceReferenceID, providerSpec, azureProviderSecret)

Expect(err).NotTo(HaveOccurred())
Expect((*VMParameters.VirtualMachineProperties.OsProfile.LinuxConfiguration.SSH.PublicKeys)[0].KeyData).NotTo(Equal(""))
})
})
})

0 comments on commit 7c79593

Please sign in to comment.