A couple of weeks ago, one of my customers mentioned that when they tried to create a Linux virtual machine through the Azure portal, they weren’t able to download the generated SSH private key. For those who know me personally or read the posts in this blog, you know that as soon as I read the issue, I absolutely had to get to the bottom of it.

At the time, I was a little more than a month in to learning the Azure platform, so I had a bit of ramping up to do before I could replicate my customer’s environment. Armed with the details to reproduce the issue, I set up a test subscription1 and started looking at the permissions defined for the built-in roles2 in the platform. For those who are more familiar with AWS, it provides the equivalent functionality by the AWS managed policies for job functions3. I figured that the Virtual Machine Contributor4 role would be a good place to start based on the associated permissions. The documentation states that the role “Lets you manage virtual machines, but not access to them, and not the virtual network or storage account they’re connected to.”

I created a test user, tied the role to it, and logged in to the portal using a separate Firefox container5. I then proceeded to click through the Virtual Machine wizard to create a standard Linux VM without any customizations. Once I created the VM, I was presented with a modal dialog box that included the button to download the private key for the machine. I clicked it and was presented with the following:

Considering I was using a built-in role without any customizations, this wasn’t what I was expecting. In my experience, any time I encounter an issue I look for the corresponding log to be able to tell me more. In this case, I figured that the Azure Activity Log6 would have a record of the error, and why. I pulled it up, sorted through all of the recent events and none of them indicated an issue with permissions. At this point, I had to think about this problem from another perpsective.

With the expectation that the activity log captures actions committed against a subscription, I decided to use my administrator user to create the VM. I used the same click-click-click (I’m so not used to this) accepting all the defaults and attempted to create the instance. Success!

In reviewing the Activity Log, I discovered this gem…

1
2
3
4
5
{
    "authorization": {
        "action": "Microsoft.Compute/sshPublicKeys/generateKeyPair/action",
        "scope": "/subscriptions/redacted/resourcegroups/rg-sandbox/providers/Microsoft.Compute/sshPublicKeys/sshkeytest_key"
    },

What in the world?! Check out the action defined in line 3 of the above JSON.

Microsoft.Compute/sshPublicKeys/generateKeyPair

Now compare the list of actions defined in the public documentation7, and you’ll notice that the only sshPublicKeys actions are:

  • read
  • write
  • delete

Now this presents a problem from a least privilege perspective especially at the enterprise level. If the generateKeyPair action cannot be explicitly set in a policy, then policy administrators are forced to append a wildcard to Microsoft.Compute/sshPublicKeys/. This would enable any user assigned to the role the privilege to read, write, and delete ANY key within the subscription.

Wildcards != Least Privilege :)

My biggest concern was the delete and I wanted to address is. After some thought, I came up with an ARM8 template to create a custom role9. The role that it creates is effectively a clone of the Virtual Machine Contributor4 role with the following additions:

1
2
3
4
5
6
"Microsoft.Compute/sshPublicKeys/*"
     ],
     "notActions": [
       "Microsoft.Compute/sshPublicKeys/delete"
     ]
   }

Yes, the wildcard is necessary to ensure that the generateKeyPair action is covered in line 1. However, I specifically limited the delete action in line 4.

With the role in place and associated to my test user, I was able to successfully create a VM through the portal, and the CLI without issue. As a nuance, the CLI generates the keypair on the calling machine, and so the generateKeyPair action is never called. This is why any user tied to the Virtual Machine Contributor role has no issue instantiating VMs through the CLI.

There are a couple of other use cases that I want to test out when using this role. I’ll post my research and observations to this series.