Checking correctness of traceparent http header in Azure API Management - azure

Is there a way to check correctness of traceparent http header using Azure API Management policies before is forwarded to backend service?

The best that I could find is to create API management policy with regular expression. It will find most of invalid cases, expect some of corner cases. If invalid http traceparent header supplied then 400 - bad request, response is returned.
<choose>
<when condition="#(!Regex.IsMatch(context.Request.Headers.GetValueOrDefault("traceparent",""), #"^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(500)))">
<return-response>
<set-status code="400" reason="Bad request - Missing or invalid traceparent http header" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#(new JObject(new JProperty("statusCode", 400), new JProperty("message", "Missing or invalid &apos;traceparent&apos; http header for distributed tracing. More information how to supply one at https://www.w3.org/TR/trace-context-1/")).ToString())</set-body>
</return-response>
</when>
</choose>

Only manually. You can use choose policy with a policy expression where you get value of the header and validate it using C# code. And then take different actions depending on whether you deem it valid or not.

Related

Change APIM Subscription key header name that's already in use

We are currently using the default Ocp-Apim-Subscription-Key header name for Azure APIM subscriptions, and it is used in production by a fair amount of people. We would like to change that without breaking any existing integration, by for example adding support for a second ApiKey header that fulfils the same role, until we can phase out the default APIM one.
Azure only offers the option to change the existing one - which would be a breaking change in our case. How can we deprecate this header safely in favor of a more readable one?
That is not going to be trivial, unfortunately. But there is a way. If you change API's subscription key header name any request coming in with old header will produce 401 response. You could intercept that in API's on-error section and check if old header name is present or not, and if it does - reissue the request. Something along these lines:
<on-error>
<base />
<choose>
<when condition="#(context.Response.StatusCode == 401 && context.Request.Headers.ContainsKey("OCP-APIM-Subscription-Key"))">
<send-request mode="copy" response-variable-name="response">
<set-url>#{
var urlParts = context.Request.OriginalUrl.ToString().Split('/');
urlParts[2] = "127.0.0.1"; //Overriding host to keep request local, but Host header also need to be reset to make SSL work
return string.Join("/", urlParts);
}</set-url>
<set-header name="Host">
<value>#(context.Request.OriginalUrl.Host)</value>
</set-header>
<set-header name="ApiKey">
<value>#(context.Request.Headers.GetValueOrDefault("OCP-APIM-Subscription-Key"))</value>
</set-header>
</send-request>
<return-response response-variable-name="response" />
</when>
</choose>
</on-error>

How to setup 405 Method Not Allowed for each of the methods in APIs using azure API Management

In azure API Management how to setup the 405 (Method not allowed) policy. I am using azure API management APIs and adding different policies like jwt validation, IP filtering, rate limit and all. But I couldn't find a way for add the 405 method not allowed in APIM. I want to setup this for each of the methods. That means I want to block the incoming unrecognized method requests from APIM. (eg: Get instead of POST (Throws 405 method not allowed from APIM). Currently APIM passes the wrong method to backend and it returns the 404 from the application. Anyone know how we can block the wrong request from APIM side and returns 405 instead of passing it to backend and returns 404?.
You could use a Control Flow policy along with the Context Variable on the Inbound policy of each Method to intercept any requests that don't match the defined http method and then use a Set Status policy to return a 405. So for a GET method something along the lines of:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Method.ToString() != "GET")">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
If you've got multiple methods with the same path you might need to apply this at the API level rather than the Method level and make the condition equals methods not in use rather than not equal to method in use
To set this at the API level and check against a collection of methods not in use create a policy along the lines of:
<policies>
<inbound>
<choose>
<when condition="#{
ICollection<string> disallowedMethods = new List<string>() { "POST", "PUT" };
return disallowedMethods.Contains(context.Request.Method.ToString());
}">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
The http methods not in use in this example are POST and PUT but you can change the list to whatever applies in your use case.

Azure API Management to Accept Event Grid Web Hook (Error Validating The Provided Endpoint)

I have a Premium Azure Service Bus sending messages to a "productupdate" topic. Currently, I am using a Logic App to receive the message, but it's not quite what I would like to do. Instead, I would like for Event Grid to pick up on these messages and fire a Web Hook to an endpoint in my API Management. The problem I am having is configuring the endpoint in API Management to be able to accept the Web Hook (validation code?).
I've read through all of the documentation, blog posts, and forum posts I can find on the subject but nothing has worked thus far. My Web Hook Endpoint I am trying to use is: "https://[apim].azure-api.net/OrdersService/v1/Products?subscription-key=[apim-key].
In the Inbound policies for that endpoint in APIM, I have the following:
<set-variable name="Event" value="#(context.Variables.GetValueOrDefault<JArray>("Request")[0])" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<JObject>("Event")["eventType"].ToString() == "Microsoft.EventGrid.SubscriptionValidationEvent")">
<return-response>
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var validationResponse = new JObject(new JProperty("validationResponse",context.Variables.GetValueOrDefault<JObject>("Event")["data"]["validationCode"].ToString()));
return validationResponse.ToString();
}</set-body>
</return-response>
</when>
<otherwise>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="X-Event-Id" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["id"].ToString())</value>
</set-header>
<set-header name="X-Event-Subject" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["subject"].ToString())</value>
</set-header>
<set-header name="X-Event-Type" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["eventType"].ToString())</value>
</set-header>
<set-header name="X-Event-Time" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["eventTime"].ToString())</value>
</set-header>
<set-header name="X-Event-Data-Version" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["dataVersion"].ToString())</value>
</set-header>
<set-header name="X-Event-Metadata-Version" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["metadataVersion"].ToString())</value>
</set-header>
<set-header name="X-Event-Topic" exists-action="override">
<value>#(context.Variables.GetValueOrDefault<JObject>("Event")["topic"].ToString())</value>
</set-header>
<set-body>#(context.Variables.GetValueOrDefault<JObject>("Event")["data"].ToString())</set-body>
</otherwise>
</choose>
No matter what I set the Web Hook Endpoint to or what I put in the Inbound policy, I receive the following error when I click "Create" on the Event Subscription:
Deploying Event Subscription: productupdate
Deployment has failed with the following error: {"code":"Url
validation","message":"The attempt to validate the provided endpoint
https://<apim>.azure-api.net/OrdersService/v1/Products failed. For more details,
visit https://aka.ms/esvalidation."}
I have found plenty of information on the error, but nothing definitive that has applied to (or works with) being received by APIM. I'm pretty sure it has to do with returning the validation code, but I can't figure out how to make APIM do that. What am I missing or doing wrong?
See here on how to enable Azure Monitor and Log Analytics for your APIM service: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-azure-monitor. Once done you should get GatewayLogs table in Log Analytics with a record for each request processed by APIM. Those records will contain description of request APIM received and response it provided. Internally I see that last requests made to your endpoint all were denied with 401 because subscription-key you're passing doesn't match any key in your APIM service.
I have finally got it figured out and working. The problems ended up being in my APIM endpoint due to a fundamental misunderstanding of Event Grid on my part. It wasn't just one particular thing. Enabling Monitor and Log certainly helped me track it down. My APIM inbound policy was missing a key for setting the backend URL. Also, I didn't need to send the subscription key (because I have it disabled in APIM). I also had my endpoint set up to where it had a GET, a POST, and a PUT. I had to get rid of the GET and PUT.

Validating Authorization token in incoming requests with Azure API management and third party Authorization Server

I have to implement OAuth 2.0 in my project with a third party Authorization Server. Both my client and server are already registered on the AS. I have created an API management instance on Azure and imported the swagger APIs. I want every incoming request to be validated against my AS, so I just need to redirect the request to https://my-as.com/as/introspect.oauth2, and have the token validated. If the token is valid then let it proceed or else send 401. I was trying to implement this using "inbound processing" and referred to the following doc: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad#configure-a-jwt-validation-policy-to-pre-authorize-requests.
The only problem is that instead of Azure AD I'm using a third party AS. I tried replacing the URL in sample XML code with my URL, but its not working.
How do I redirect requests to the Authorization Server for validating access token?
Adding following inbound policy worked:
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="#(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
<!-- Send request to Token Server to validate token (see RFC 7662) -->
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://my-as.com/as/introspect.oauth2</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>#($"grant_type=urn:pingidentity.com:oauth2:grant_type:validate_bearer&client_id=UoM&client_secret=somesecret&token={(string)context.Variables["token"]}")</set-body>
</send-request>
<choose>
<!-- Check active property in response -->
<when condition="#((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<!-- Return 401 Unauthorized with http-problem payload -->
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
<base />
</inbound>

Azure API management POST request - All body parameters required

How can I apply validation that all request parameters in body are required for my post request? What policy expressions I may use?
I am using following expression:
<policies>
<inbound>
<base />
<choose>
<when condition="#((context.Request.Body) != null&& ((int)context.Request.Body.As<JObject>()["Id"])>0)">
<return-response>
</return-response>
</when>
<otherwise>
<return-response>
</return-response>
</otherwise>
</choose>
</inbound>
How can I restrict to input all body parameters for this post request?
See APIM's content validation policies: https://learn.microsoft.com/en-us/azure/api-management/validation-policies#validate-content They allow you to validate that request actually conforms to the schema specified in your spec.
As an alternative you could read body as JObject and manually checking each property of interest. Mind that you want to use context.Request.Body.As(preserveContent: true) to ensure that body is cached and available to later be sent to backend.

Resources