The last post in this series addressed the authorization required to generate SSH keys in Azure when creating Linux VMs through the portal. In this post, I identify the differences in key generation workflows and identify unexpected locations for stored key data.

An SSH keypair consists of two components, private and public keys, that are used to ensure via cryptographic computation that the authenticating party is authentic. Key generation occurs in two distinct paths in relation to virtual machine deployment.

Through the portal, the platform generates the public and private keys, and the browser prompts the user to download the private key before starting the deployment.

For those as curious as I, you are now wondering not only if the platform stores the private key, but how the public pair makes it into the provisioned virtual machine instance. To answer the first question, the private key is not stored in the platform. However, the platform stores the public key in two different places!

The first is in a resource identified in the Microsoft.Compute namespace as resource type sshPublicKeys. The full details of the object can be viewed through the portal and via the API as shown below:

The second isn’t as obvious… I stumbled upon its location by simply being too curious for my own good. The public key is in the virtual machine object, in a buried property that can only be seen when called via the API, and not through the portal.

Why is the same data stored in two different places? Why wouldn’t there be an association of the key object to the virtual machine?

My second test was to perform the same scenario of creating a virtual machine through the CLI to learn how the SSH keypair was being handled.

In this case, the CLI generated both the private and public keys in the correct .ssh directory. How were they provided to the deployed virtual machine?

Based on the CLI debug output, the API makes a call to the management endpoint with a deployment script that includes the public SSH key as a property within the template as shown in line 41 below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
  "apiVersion": "2020-06-01",
  "type": "Microsoft.Compute/virtualMachines",
  "name": "redacted",
  "location": "eastus",
  "tags": {},
  "dependsOn": [
  ],
  "properties": {
    "hardwareProfile": {
      "vmSize": "Standard_DS1_v2"
    },
    "networkProfile": {
      "networkInterfaces": [
        {
        }
      ]
    },
    "storageProfile": {
      "osDisk": {
        "createOption": "fromImage",
        "name": null,
        "caching": "ReadWrite",
        "managedDisk": {
          "storageAccountType": null
        }
      },
      "imageReference": {
        "publisher": "Canonical",
        "offer": "UbuntuServer",
        "sku": "18.04-LTS",
        "version": "latest"
      }
    },
    "osProfile": {
      "linuxConfiguration": {
        "disablePasswordAuthentication": true,
        "ssh": {
          "publicKeys": [
            {
              "keyData": "ssh-rsa AAAAB3(...snip...)"
              "path": "/home/akale/.ssh/authorized_keys"
            }
          ]
        }
      }
    }
  }
}

I wanted to make sure I was comparing the process completely, so I used the same resource list API call to find the previous virtual machine’s SSH key object, and the number of objects returned was one. The CLI never creates an associated SSH key object as the portal does, and only associates the key data in the osProfile.linuxConfiguration.ssh.publicKeys.keyData property within the virtual machine object.

Through this post, we learned that the portal, and the API (via the CLI) handle the association of SSH keys for virtual machine creation differently. This will be useful for the next post in this series where I dive into how to handle SSH key lifecylce management as it relates to security compliance.