Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ed25519 ssh key support for compute and storage account local user #27202

Merged
merged 5 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 you@me.com"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp you@me.com"
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN you@me.com"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp you@me.com"
ed25519_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDqzSi9IHoYnbE3YQ+B2fQEVT8iGFemyPovpEtPziIVB you@me.com"
}
`
}
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 you@me.com"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp you@me.com"
first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN you@me.com"
second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp you@me.com"
ed25519_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDqzSi9IHoYnbE3YQ+B2fQEVT8iGFemyPovpEtPziIVB you@me.com"
}
`
}
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 hello@world.com"
}
}
}

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
Loading