Authenticating to consul KV from client VMs

Hello. I am looking to deploy a small cluster consisting of three Consul servers. My intention is to use KV to store common params for environments.
To protect data, I would like my client VMs to use JWT tokens as the authentication method.
Can I use Azure AD to obtain these tokens and send them to Consul? If so, how can it be done? (I wasn’t able to exactly understand from the documentation how it’s done).

Thank you!

Anyone can shed more light regarding my question, please?

Hi @yevgeny.zamsky,

First you need create an auth method to use for logging in to Consul. You can do that using the /v1/acl/auth-method API endpoint. Below is an example JSON payload that you might use to create the auth method.

{
  "Name": "azure-jwt-auth",
  "Type": "jwt",
  "Description": "Azure AD JWT auth",
  "Config": {
    "BoundIssuer": "azure-issuer",
    "JWTValidationPubKeys": [
      "<public key PEM>"
    ],
    "ClaimMappings": {
      "http://example.com/first_name": "first_name",
      "http://example.com/last_name": "last_name"
    },
    "ListClaimMappings": {
      "http://example.com/groups": "groups"
    }
  }
}

Next, you need to create an ACL binding rule (/v1/acl/binding-rule) that matches on claims present in the JWT. If there’s a match, Consul will issue a token with the assigned policy or role.

The example below will match if there there is a claim present named first_name and the value equals blake. The generated token will be assigned the Consul ACL role named admin-user-role.

{
  "Description": "example rule",
  "AuthMethod": "azure-jwt-auth",
  "Selector": "first_name==blake",
  "BindType": "role",
  "BindName": "admin-user-role"
}

Lastly, the application will need to use the /v1/acl/login endpoint to
login to Consul using the JWT.

{
  "AuthMethod": "azure-jwt-auth",
  "BearerToken": "<jwt_token>"
}

Hello Blake, thank you for your assistance! To make the question even more precise, the VMs in Azure will use their Managed Identity to obtain the token from Azure AD. Such tokens won’t have names, last names or similar claims a user would normally have.

How the Auth Method should look like considering this?

Thanks!

I’m not familiar with the tokens issued by Azure. Are there any claims present in these identity tokens that you can match with a binding rule?

Hi, here is an example of the JWT token returned by Azure AD for a VM with Managed Identity.

  • I deleted a few identifying parameters to keep it safe.
{
  "aud": "https://management.azure.com/",
  "iss": "https://sts.windows.net/XXXTenantIDXXX/",
  "iat": 1692784469,
  "nbf": 1692784469,
  "exp": 1692871169,
  "aio": "E2FgYDDYpHdj982qA6JWZbNyvnxiBgA=",
  "appid": "XXXThe object Id for VMs Managed Identity",
  "appidacr": "2",
  "groups": [
    "XXXobjectID of Azure AD group",
    "XXXobjectID of Azure AD group",
    "XXXobjectID of Azure AD group"
  ],
  "idp": "https://sts.windows.net/XXX-TenandID-XXX/",
  "idtyp": "app",
  "oid": "XXX-SystemAssignedIdentity-XXX",
  "rh": "0.AYIAexqZP5PqaUGyjMNv8-Ww0UZIf3kAutdPukPawfj2MBOCAAA.",
  "sub": "XXX-SystemAssignedID-XXX",
  "tid": "XXX-TenandID-XXX",
  "uti": "ZfViFfLdZ0eeHvRFp92JAQ",
  "ver": "1.0",
  "xms_mirid": "/subscriptions/XXX-SubscriptionID-XXX/resourcegroups/resourcegroup/providers/Microsoft.Compute/virtualMachines/VMName1",
  "xms_tcdt": "1396893460"
}

As you can see, this is the information we get when obtaining a token from AzureAD for a VM with a managed identity.

What do you think of that? Can it be used to authenticate to Consul from a VM that would run a script?

You should be able to use this to authenticate to Consul using the JWT auth method.

You will need to construct a binding rule that matches some of the claims in these tokens and assigns a policy or role based on that info. The selector field in binding rules supports the same go-bexpr syntax that is documented on Consul’s API filtering docs.

Here’s an (untested) example of a selector that matches if the following criteria are satisfied:

  1. The ver (version) of the access token is 1.0.
  2. the appidacr (authentication method of the client) claim is present and the value equals 2, meaning the client authenticated with a certificate.
  3. The tid (tenant ID) is not empty.
{
  "Description": "example rule",
  "AuthMethod": "azure-jwt-auth",
  "Selector": "ver==\"1.0\" and (appidacr is not empty and appidacr==2) and (tid is not empty)",
  "BindType": "policy",
  "BindName": "tenant-${tid}-policy"
}

If all of the above criteria match, Consul will create a token and assign it the policy after interpolating the tenant ID.

For example if value of tid is 9188040d-6c67-4c5b-b112-36a304b66dad, Consul will attempt to assign the policy named tenant-9188040d-6c67-4c5b-b112-36a304b66dad-policy.

You would need to pre-create the policies for each Azure tenant that you expect to to have VMs that will authenticate to Consul.

Below is an example policy that grants write access to the KV path tenants/9188040d-6c67-4c5b-b112-36a304b66dad/config/.

# tenant-9188040d-6c67-4c5b-b112-36a304b66dad-policy.hcl

key_prefix "tenants/9188040d-6c67-4c5b-b112-36a304b66dad/config/" {
  policy = "write"
}

Then create the policy.

$ consul acl policy create -name=tenant-9188040d-6c67-4c5b-b112-36a304b66dad-policy -rules=@tenant-9188040d-6c67-4c5b-b112-36a304b66dad-policy.hcl

Hey, Blake! Thank you for the detailed response!
As far as I can understand, in this case all VMs that obtained a JWT token from AzureAD will be able to authenticate to Consul KV.
How can I restrict access based on group membership?
For example if a VM is a member of group “Consul_ActiveDirectory_Members” then they will be allowed to Consul, otherwise - denied.

Thank you again!

You might be able to achieve this using a selector such as this:

{
  "Description": "example rule",
  "AuthMethod": "azure-jwt-auth",
  "Selector": "\"Consul_ActiveDirectory_Members\" in groups",
  "BindType": "policy",
  "BindName": "kv-access-policy"
}

Hi, apologies for my late reply! Was on vacation. I will try to configure this and let you know! Appreciate your time!