Skip to content

Commit

Permalink
ed25519 ssh key support for compute and storage account local user (#…
Browse files Browse the repository at this point in the history
…27202)

* Decouple ssh validation func from storage

So compute's ssh validation can be refactored

* Supports ed25519 ssh keys for compute

* Also support ed25519 ssh for storage account local user resource

* acctest for ed25519 ssh

* Merge changes from #26615
  • Loading branch information
gerrytan authored Aug 28, 2024
1 parent a65b388 commit 8195152
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ func TestAccLinuxVirtualMachine_authSSH(t *testing.T) {
})
}

func TestAccLinuxVirtualMachine_authEd25519SSH(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test")
r := LinuxVirtualMachineResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.authEd25519SSH(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccLinuxVirtualMachine_authSSHMultipleKeys(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test")
r := LinuxVirtualMachineResource{}
Expand Down Expand Up @@ -194,6 +209,40 @@ resource "azurerm_linux_virtual_machine" "test" {
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineResource) authEd25519SSH(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_linux_virtual_machine" "test" {
name = "acctestVM-%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
size = "Standard_F2"
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.test.id,
]
admin_ssh_key {
username = "adminuser"
public_key = local.ed25519_public_key
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineResource) authSSHMultiple(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ func (LinuxVirtualMachineResource) templateBasePublicKey() string {
return `
# note: whilst these aren't used in all tests, it saves us redefining these everywhere
locals {
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN [email protected]"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp [email protected]"
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN [email protected]"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp [email protected]"
ed25519_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDqzSi9IHoYnbE3YQ+B2fQEVT8iGFemyPovpEtPziIVB [email protected]"
}
`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,35 @@ func TestAccLinuxVirtualMachineScaleSet_authUpdatingSSHKeys(t *testing.T) {
})
}

func TestAccLinuxVirtualMachineScaleSet_authEd25519SSHKeys(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test")
r := LinuxVirtualMachineScaleSetResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.authEd25519SSHKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.authSSHKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.authEd25519SSHKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccLinuxVirtualMachineScaleSet_authDisablePasswordAuthUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test")
r := LinuxVirtualMachineScaleSetResource{}
Expand Down Expand Up @@ -209,6 +238,49 @@ resource "azurerm_linux_virtual_machine_scale_set" "test" {
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineScaleSetResource) authEd25519SSHKey(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_linux_virtual_machine_scale_set" "test" {
name = "acctestvmss-%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Standard_F2"
instances = 1
admin_username = "adminuser"
admin_ssh_key {
username = "adminuser"
public_key = local.ed25519_public_key
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
network_interface {
name = "example"
primary = true
ip_configuration {
name = "internal"
primary = true
subnet_id = azurerm_subnet.test.id
}
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineScaleSetResource) authSSHKeyUpdated(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ func (LinuxVirtualMachineScaleSetResource) templatePublicKey() string {
return `
# note: whilst these aren't used in all tests, it saves us redefining these everywhere
locals {
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN [email protected]"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp [email protected]"
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN [email protected]"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp [email protected]"
ed25519_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDqzSi9IHoYnbE3YQ+B2fQEVT8iGFemyPovpEtPziIVB [email protected]"
}
`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,35 @@ func TestAccOrchestratedVirtualMachineScaleSet_verify_key_data_changed(t *testin
})
}

func TestAccOrchestratedVirtualMachineScaleSet_linux_ed25119_ssh_key(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test")
r := OrchestratedVirtualMachineScaleSetResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.linuxEd25119SshKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("os_profile.0.linux_configuration.0.admin_password", "os_profile.0.custom_data"),
{
Config: r.linux(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("os_profile.0.linux_configuration.0.admin_password", "os_profile.0.custom_data"),
{
Config: r.linuxEd25119SshKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("os_profile.0.linux_configuration.0.admin_password", "os_profile.0.custom_data"),
})
}

func TestAccOrchestratedVirtualMachineScaleSet_basicApplicationSecurity(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_orchestrated_virtual_machine_scale_set", "test")
r := OrchestratedVirtualMachineScaleSetResource{}
Expand Down Expand Up @@ -1773,6 +1802,109 @@ resource "azurerm_orchestrated_virtual_machine_scale_set" "test" {
`, data.RandomInteger, data.Locations.Primary, r.natgateway_template(data))
}

func (OrchestratedVirtualMachineScaleSetResource) linuxEd25119SshKey(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-OVMSS-%[1]d"
location = "%[2]s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvn-%[1]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
address_space = ["10.0.0.0/8"]
}
resource "azurerm_subnet" "test" {
name = "acctestsn-%[1]d"
resource_group_name = azurerm_resource_group.test.name
virtual_network_name = azurerm_virtual_network.test.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_public_ip" "test" {
name = "acctestpip-%[1]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_lb" "test" {
name = "acctestlb-%[1]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Standard"
frontend_ip_configuration {
name = "ip-address"
public_ip_address_id = azurerm_public_ip.test.id
}
}
resource "azurerm_lb_backend_address_pool" "test" {
name = "acctestbap-%[1]d"
loadbalancer_id = azurerm_lb.test.id
}
resource "azurerm_orchestrated_virtual_machine_scale_set" "test" {
name = "acctestOVMSS-%[1]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku_name = "Standard_F2"
instances = 1
platform_fault_domain_count = 2
os_profile {
custom_data = "Y3VzdG9tIGRhdGEh"
linux_configuration {
computer_name_prefix = "prefix"
admin_username = "ubuntu"
admin_ssh_key {
username = "ubuntu"
public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDqzSi9IHoYnbE3YQ+B2fQEVT8iGFemyPovpEtPziIVB [email protected]"
}
}
}
network_interface {
name = "TestNetworkProfile"
primary = true
ip_configuration {
name = "TestIPConfiguration"
primary = true
subnet_id = azurerm_subnet.test.id
load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.test.id]
}
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}
`, data.RandomInteger, data.Locations.Primary)
}

func (OrchestratedVirtualMachineScaleSetResource) linuxKeyDataUpdated(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
13 changes: 10 additions & 3 deletions internal/services/compute/validate/ssh_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package validate

import (
"crypto/ed25519"
"crypto/rsa"
"encoding/base64"
"fmt"
Expand Down Expand Up @@ -35,9 +36,8 @@ func SSHKey(i interface{}, k string) (warnings []string, errors []error) {
return nil, []error{fmt.Errorf("parsing %q as a public key object", k)}
}

if pubKey.Type() != ssh.KeyAlgoRSA {
return nil, []error{fmt.Errorf("- the provided %s SSH key is not supported. Only RSA SSH keys are supported by Azure", pubKey.Type())}
} else {
switch pubKey.Type() {
case ssh.KeyAlgoRSA:
rsaPubKey, ok := pubKey.(ssh.CryptoPublicKey).CryptoPublicKey().(*rsa.PublicKey)
if !ok {
return nil, []error{fmt.Errorf("- could not retrieve the RSA public key from the SSH public key")}
Expand All @@ -46,6 +46,13 @@ func SSHKey(i interface{}, k string) (warnings []string, errors []error) {
if rsaPubKeyBits < 2048 {
return nil, []error{fmt.Errorf("- the provided RSA SSH key has %d bits. Only ssh-rsa keys with 2048 bits or higher are supported by Azure", rsaPubKeyBits)}
}
case ssh.KeyAlgoED25519:
_, ok := pubKey.(ssh.CryptoPublicKey).CryptoPublicKey().(ed25519.PublicKey)
if !ok {
return nil, []error{fmt.Errorf("- could not retrieve the ED25519 public key from the SSH public key")}
}
default:
return nil, []error{fmt.Errorf("- the provided %s SSH key is not supported. Only RSA and ED25519 SSH keys are supported by Azure", pubKey.Type())}
}
} else {
return nil, []error{fmt.Errorf("%q is not a complete SSH2 Public Key", k)}
Expand Down
2 changes: 1 addition & 1 deletion internal/services/compute/validate/ssh_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestSSHKey(t *testing.T) {
},
{
input: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOwlR9xtbM69hWLJbB5nHi0a65TuRvtaldgTJQ4ClL1W",
expected: false,
expected: true,
},
{
input: "ssh-rsa",
Expand Down
Loading

0 comments on commit 8195152

Please sign in to comment.