When working with Azure API Management, often we need to include secrets in our policies. For example, we may need to send a password in our authentication header, or to validate a key in a JWT token. There are several options to store these secrets. We could hardcode them into our policy, however this would mean anyone with access to our API Management instance could read them. An not just them, but also everyone who can look into our source control. because we deploy our policies as Infrastructure as Code.

The second option is to place the secret in a named value. This even provides us with the option to set the value as a secret, meaning it will not show the actual value in the overview.

Image result for azure api management named values secret"

However, anyone with access to API Management can still come into the instance, and untick the secret option, and grab the secret. Consequently, this is still not a good option, as we want the management of our secrets to be separate from our API Management administration. Therefor, we will instead store the secret in Azure Key Vault, and retrieve it in our policy.

 

Enable Managed Identity

Before we jump into the policy itself, we first need to do some groundwork. As we are going to retrieve the secret from Key Vault, we will assign a managed identity to API Management, which we then give permission to get the secrets. First, enable managed identity on your API Management.

Assign permissions

Once enabled, the next step is to assign the required permissions to this new identity in Key Vault, which has the name of our resource. We do this in the Access policies blade, where we provide Get permissions for the secrets. Important to note, this does give the identity access to all the secrets in this Key Vault. As such, it is important to have a good Key Vault policy around separation of secrets.

Get secret endpoint

Now that API Management can retrieve the secrets, we grab the endpoint of our secret. Remember, you can use the endpoint without a version, which allows us to always get the latest version of the secret.

Create policy

Now that we have everything in place, we are going to create the policy. In this scenario, we will send the secret as part of our basic authentication header. The endpoint we are using is on Request Bin, which allows us to easily inspect the call. Be careful using these types of services when using your own data, in which case I would recommend hosting your own Request Bin instance, as explained by Paco de la Cruz.

We are going to add the policy on the operation, although this could also have been done on the API, the product or even the global scope. In the policy we are going to call the Key Vault endpoint, using our managed identity as the authentication method. We do this using the following snippet.

<send-request ignore-error="false" timeout="20" response-variable-name="passwordResponse" mode="new">
    <set-url>https://https://kv-we-retrieve-kv-secret.vault.azure.net/secrets/MySecretValue/?api-version=7.0</set-url>
    <set-method>GET</set-method>
    <authentication-managed-identity resource="https://vault.azure.net" />
</send-request>

As a result, this stores the response with the secret value in the passwordResponse variable. We can now use this to update the outgoing request to our backend service, which in our case is Request Bin. We do this by adding the authorization header using the following snippet in our policy. Additionally, we could also save the secret to the cache at this point, but that is something for another post.

<authentication-basic username="myusername" password="@{ var secret = ((IResponse)context.Variables["passwordResponse"]).Body.As<JObject>(); return secret["value"].ToString(); }" />

For reference, the complete policy now looks as following.

<policies>
    <inbound>
        <base />
        <send-request ignore-error="false" timeout="20" response-variable-name="passwordResponse" mode="new">
            <set-url>https://kv-we-retrieve-kv-secret.vault.azure.net/secrets/MySecretValue/?api-version=7.0</set-url>
            <set-method>GET</set-method>
            <authentication-managed-identity resource="https://vault.azure.net" />
        </send-request>
        <rewrite-uri template="/" copy-unmatched-params="true" />
        <set-backend-service base-url="https://enbjeiqttutuh.x.pipedream.net/" />
        <authentication-basic username="myusername" password="@{ var secret = ((IResponse)context.Variables["passwordResponse"]).Body.As<JObject>(); return secret["value"].ToString(); }" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

Now when we call our endpoint, API Management calls out to Key Vault, and retrieves the secret value with our password. Finally, it adds this secret as the password value for the basic authentication header. Looking at Request Bin, we indeed notice that the header has been set, with the username and password base64 encoded.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.