AzureSignTool icon indicating copy to clipboard operation
AzureSignTool copied to clipboard

Issue with Per-Secret Azure KV RBAC Permissions

Open bn-jswick opened this issue 11 months ago • 9 comments

Hello, I am trying to figure out if an issue I'm having with the tool is specific to the signing tool, or if it's an Azure Key Vault Issue.

I've setup a premium keyvault with Azure RBAC access policies, and created a custom role for the key vault based on this comment:

{
    "id": "/subscriptions/xxxx/providers/Microsoft.Authorization/roleDefinitions/xxxx",
    "properties": {
        "roleName": "Key Vault Signing Certificate User",
        "description": "Role to allow use of code signing certificates.\n\nBased on information from the AzureSignTool github repo",
        "assignableScopes": [
            "/subscriptions/xxxx"
        ],
        "permissions": [
            {
                "actions": [],
                "notActions": [],
                "dataActions": [
                    "Microsoft.KeyVault/vaults/certificates/read",
                    "Microsoft.KeyVault/vaults/secrets/readMetadata/action",
                    "Microsoft.KeyVault/vaults/keys/read",
                    "Microsoft.KeyVault/vaults/keys/sign/action",
                    "Microsoft.KeyVault/vaults/keys/verify/action"
                ],
                "notDataActions": []
            }
        ]
    }
}

If I assign the role to the identity performing the signing on the signing certificate itself, the vault returns a 403 forbidden on the KeyGet and KeySign operations when I attempt to sign an executable using the AzureSignTool. However, if I assign the same role at the Key Vault level, the signing operation succeeds.

I would prefer to be able assign permissions at the per-secret level in the key vault, as it will allow for other uses of the vault rather than having to maintain multiple similar vaults. From what I've been able to glean from the logs on the key vault, the tool is going direct to the certificate, and not trying to list what's in the vault to find it, so the per-secret permissions should work.

Before I open a ticket with Microsoft on the Key Vault, is there something else that I'm completely overlooking with the use of the tool?

Thank you.

bn-jswick avatar Jan 15 '25 15:01 bn-jswick

These are the permission you need:


{
    "id": "/subscriptions/xxxx/providers/Microsoft.Authorization/roleDefinitions/xxxx",
    "properties": {
        "roleName": "CR CodeSigning",
        "description": "Custom Role to retain the same permissions given before with access policies for Code Signing",
        "assignableScopes": [
            "/subscriptions/xxxx/resourceGroups/Keyvaults",
            "/subscriptions/xxxx/resourceGroups/Certificates"
        ],
        "permissions": [
            {
                "actions": [],
                "notActions": [],
                "dataActions": [
                    "Microsoft.KeyVault/vaults/certificates/read",
                    "Microsoft.KeyVault/vaults/keys/read",
                    "Microsoft.KeyVault/vaults/keys/sign/action",
                    "Microsoft.KeyVault/vaults/keys/verify/action",
                    "Microsoft.KeyVault/vaults/secrets/getSecret/action",
                    "Microsoft.KeyVault/vaults/secrets/readMetadata/action"
                ],
                "notDataActions": []
            }
        ]
    }
}

Just recently moved from access policies to RBAC for signing.

This was quite an interesting article

The most usefull part was the mapping of the access policies to RBAC, more info here.

Great tool, time to give something back to the community :-)

wimmme avatar Jan 27 '25 11:01 wimmme

@wimmme Have you tested your policy at a per-secret level? it doesn't work for me when assigned directly to the signing cert. The original RBAC policy I posted works when assigned to the key vault, with the only difference between the two is the addition of the getSecret action, which doesn't apply to HSM backed secrets.

I have opened a support case with Microsoft on this, as I was able to replicate the issue with the az cli, giving me the evidence that this isn't an issue with the AzureSignTool utility itself.

bn-jswick avatar Jan 27 '25 14:01 bn-jswick

@wimmme Have you tested your policy at a per-secret level? it doesn't work for me when assigned directly to the signing cert. The original RBAC policy I posted works when assigned to the key vault, with the only difference between the two is the addition of the getSecret action, which doesn't apply to HSM backed secrets.

I have opened a support case with Microsoft on this, as I was able to replicate the issue with the az cli, giving me the evidence that this isn't an issue with the AzureSignTool utility itself.

No, only at RG-level. I f uou map the Access Policy to RBAC via the table you have to add that one too. Key: Verify, Sign, Get, List Secret: Get, List Certificate:: Get, List

Microsoft.KeyVault/vaults/keys/read Microsoft.KeyVault/vaults/keys/sign/action Microsoft.KeyVault/vaults/keys/verify/action Microsoft.KeyVault/vaults/secrets/getSecret/action Microsoft.KeyVault/vaults/secrets/readMetadata/action Microsoft.KeyVault/vaults/certificates/read

We use it for an HSM backed code signing certificate

wimmme avatar Jan 27 '25 15:01 wimmme

No, only at RG-level. I f uou map the Access Policy to RBAC via the table you have to add that one too. Key: Verify, Sign, Get, List Secret: Get, List Certificate:: Get, List

That's not the point of this issue. The point is to highlight that there was a problem with setting a per-secret RBAC role in the Key Vault for least privilege of service accounts so that they only have access to the secrets they actually need. Assigning permissions at the Vault or even RG level could result in an identity having more access to secrets than it should have.

Everyone's use cases are going to be a little different. However, my requirement for the setup is that the identities using the signing cert only have access to that specific secret in the vault and nothing else that may be in the vault, unless that specific identity also needs the other secret.

bn-jswick avatar Jan 27 '25 15:01 bn-jswick

Hey @bn-jswick, just taking a look into this myself as I'm moving to RBAC. Can you give a bit more detail about the roles you are applying and to which resources? Are you applying to both the "secrets" subresource, the key "subresource" and the "certificates" subresource?

You can get the path to other subresources with below commands.

Secret:

az keyvault certificate show \
    --vault-name MyVaultName \
    --name MyCertName \
    --query "sid"

Key:

az keyvault certificate show \
    --vault-name MyVaultName \
    --name MyCertName \
    --query "kid"

davej avatar Feb 09 '25 18:02 davej

@davej , The custom role is the one in my original message. I got it to work by assigning the role to the hidden "key" object that goes with the certificate. However, since this is completely undocumented by Microsoft, I have a support case with them open (that TBH, I expect to go nowhere and they won't do anything about it).

From what is documented by Microsoft, the reasonable expectation is that a role assigned to the certificate object would also apply to its corresponding hidden secret and key objects, as the only way to know about (or apply) permissions assigned to them otherwise is via the CLI or other API tool, as they're completely hidden in the portal.

bn-jswick avatar Feb 10 '25 13:02 bn-jswick

For anybody coming across this. Here is how I got this working at subresource level:

az role definition create --role-definition '{ \
   "Name": "Testing a scoped agent", \
   "Description": "Description here", \
    "Actions": [ 
    ], \
    "DataActions": [ \
        "Microsoft.KeyVault/vaults/certificates/read", \
        "Microsoft.KeyVault/vaults/keys/read", \
        "Microsoft.KeyVault/vaults/keys/sign/action", \
        "Microsoft.KeyVault/vaults/keys/verify/action", \
        "Microsoft.KeyVault/vaults/secrets/getSecret/action", \
        "Microsoft.KeyVault/vaults/secrets/readMetadata/action" \
    ], \
    "NotDataActions": [ 
   ], \
    "AssignableScopes": ["/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/secrets/ddd", \
     "/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/keys/ddd", \
     "/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/certificates/ddd"] \
}'

az role assignment create \
    --assignee xxx \
    --role "Testing a scoped agent" \
    --scope "/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/secrets/ddd"

az role assignment create \
    --assignee xxx \
    --role "Testing a scoped agent" \
    --scope "/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/keys/ddd"

az role assignment create \
    --assignee xxx \
    --role "Testing a scoped agent" \
    --scope "/subscriptions/aaa/resourceGroups/bbb/providers/Microsoft.KeyVault/vaults/ccc/certificates/ddd"

Legend:

aaa = Subscription ID
bbb = Resource Group
ccc = Key Vault
ddd = Secret, Key, or Certificate

davej avatar Feb 10 '25 14:02 davej

For what it's worth, the secret assignment (and getSecret action) is superfluous, as you can't get the secret with an HSM backed key, which is the requirement for code signing certs.

It doesn't change things either way, but IMO it's better to leave the unnecessary permission out.

bn-jswick avatar Feb 10 '25 18:02 bn-jswick

After beating my head against the wall with Microsoft support, to which the end result is that Microsoft won't fix their documentation or how things are displayed on the Azure portal with regards to per-secret IAM permissions on certificates. So one ends up having to discover all of this on their own.

In order to implement your code signing cert following the principle of least privilege, one needs to use a tool that directly interfaces with the Azure API, whether it be the az cli, powershell, or terraform, to assign your custom role to both the Certificate and Key objects.

This is the custom role that is working for my implementation of it:

{
    "properties": {
        "roleName": "Key Vault Signing Certificate User",
        "description": "Role to allow use of code signing certificates.\n\nBased on information from the AzureSignTool github repo",
        "assignableScopes": [
            "/subscriptions/<subid>"
        ],
        "permissions": [
            {
                "actions": [],
                "notActions": [],
                "dataActions": [
                    "Microsoft.KeyVault/vaults/certificates/read",
                    "Microsoft.KeyVault/vaults/secrets/readMetadata/action",
                    "Microsoft.KeyVault/vaults/keys/read",
                    "Microsoft.KeyVault/vaults/keys/sign/action",
                    "Microsoft.KeyVault/vaults/keys/verify/action"
                ],
                "notDataActions": []
            }
        ]
    }
}

I think I tested this role without the Microsoft.KeyVault/vaults/secrets/readMetadata/action permission and something didn't work as intended, but I'm not 100% sure at this point. I just set the assignable scope at the subscription because all that does is change where you can apply the permission to. For some IAM roles it may be warranted, but for this since you're assigning the permissions by hand and not via the portal, I think it's unnecessary to restrict the assignable scope further in this case, but it wouldn't make a difference usability wise to restrict it to the specific objects.

Once you've created the role via your method of choice, you then need to assign it to the appropriate identities on both the certificate object and key object for your code signing cert in the keyvault. Here's an example of it using the az cli:

ROLE_ID="/subscriptions/<sub-id>/providers/Microsoft.Authorization/roleDefinitions/<role-uid>"
KV_ID="/subscriptions/<sub-id>/resourceGroups/<rg-name>/providers/Microsoft.KeyVault/vaults/<kv-name>"
CERT_NAME="my-code-sign-cert"
SPN_ID="<service-principal-uid>"

az role assignment create --role "$ROLE_ID" --scope "$KV_ID/certificates/$CERT_NAME" --assignee-object-id "$SPN_ID" --assignee-principal-type ServicePrincipal

az role assignment create --role "$ROLE_ID" --scope "$KV_ID/keys/$CERT_NAME" --assignee-object-id "$SPN_ID" --assignee-principal-type ServicePrincipal

bn-jswick avatar Apr 08 '25 15:04 bn-jswick