Managing MuleSoft API Policies using Maven
Creating a custom Maven Plugin to manage API Policies automatically
Introduction
MuleSoft is a market leader in Gartner’s Magic Quadrant for Enterprise Integration Platform as a Service (iPaaS) and Full Life Cycle API Management. Their offering is extensive, robust, and easy to implement.
API policies are used to configure Security, Compliance, Transformation, Quality of Service, and Troubleshooting. More information can be found here. The most common policies I’ve seen applied are Security and Compliance, specifically JWT validation and Client ID enforcement.
Managing your API policies can easily be applied through the platform. API Manager allows you to apply different types of policy to your API, allows you to manage access to the API through Contacts and SLA Tiers and allows you assign alerts to your APIs.
The Mule Maven plugin can be used to build and deploy applications to Runtime Manager. Although this requires you to build your application from source code every time you want to deploy the application it offers a way for developers to create a Continuous Integration/Continuous Delivery (CI/CD) pipeline quickly and easily.
API Manager offers great functionality, but if you want to follow a CI/CD process then API Manager can often limit the effectiveness of your integration and deployment pipeline. Currently (May 2020) the only way to set policies is through the UI (or using the Platform APIs).
There are two main problems with this:
1. It becomes very hard to accurately version your API. As you’re manually changing policies in API Manager you may make breaking changes to your API that is currently running the same version.
2. Mistakes happen when things are done manually, forgetting to apply a policy in Production could have drastic implications to your business. We need a way to ensure that policies are always applied correctly, and we need an audit trail of what the policies were at a given time.
API Manager Plugin
The API Manager Maven plugin I’ve developed allows developers to manage policies, alerts, and SLA tiers and major/minor API Versioning. This allows developers to write their policies and other API configuration as code, and we can ensure that the policies in each environment are as described in the code.
How it works
The plugin interacts with the Anypoint Platform APIs which requires us to set the credentials in our pom file. All the values set in the plugin configuration are passed to the plugin when the goal is executed (as you’ll see later). Each section of the configuration can be exposed as Java objects, as you can see here with this Credentials object that has username and password properties:
pom.xml
<plugin>
<groupId>com.slalom</groupId>
<artifactId>api-manager-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<credentials>
<username>${cloudhub.username}</username>
<password>${cloudhub.password}</password>
</credentials>
...
API Manager Plugin
package domain.auth;
import lombok.Data;@Data
public class Credentials { private String username;
private String password;
I’ve used Lombok to reduce the verbosity of setting getters and setters. For more information around Lombok, read here: https://projectlombok.org
The plugin will authenticate against the Anypoint Platform APIs and then the next step is to work out if this API is an existing API or not. For this we need access to the published RAML file in Exchange. This asset in Exchange can be identified using the Group ID, Asset ID, and the Version (GAV). As well as this information I’ve added the productVersion, so the major version of the API.
pom.xml
<exchangeAsset>
<groupId>9a5c9957-32a7-449b-5678-1ce91fac7061</groupId>
<assetId>mike-test</assetId>
<version>1.0.0</version>
<productVersion>v1</productVersion>
</exchangeAsset>
Using the GAV we can work out if there’s already an API in the environment specified. If there is no API then we will create an API for this Asset. If there is an API then it will ensure that the version of the API set is the version of the asset declared in the pom. The plugin will also create new major versions of APIs, so if you release a new major version of the API then it will create that as a separate version in API Manager and manage individually.
This plugin was created for a project that requires IP Whitelisting, Client ID Enforcement, SLA Tiers and Alerts configured. The plugin will keep the API policies in sync with what’s defined in the plugin. It handles create, modify and delete. If a section is removed that policy, alert or tier will be removed, if it’s added it will be created and if it’s changed then it’ll be updated in API Manager.
This plugin was created for a project that requires IP Whitelisting, Client ID Enforcement, SLA Tiers and Alerts configured. The plugin will keep the API policies in sync with what’s defined in the plugin. It handles create, modify and delete. If a section is removed that policy, alert or tier will be removed, if it’s added it will be created and if it’s changed then it’ll be updated in API Manager.
IP Whitelisting
An IP Whitelisting policy can be added by adding an ipWhitelist block into the configuration, each policy in MuleSoft has a configuration block. For IP Whitelisting you need an array of IP addresses and an expression to resolve.
pom.xml
<ipWhitelist>
<configurationData>
<ips>
<param>80.80.80.100</param>
<param>80.80.80.101</param>
</ips>
<ipExpression>#[attributes.headers[‘x-real-ip’]]</ipExpression>
</configurationData>
</ipWhitelist>
Client ID Enforcement
The Client ID enforcement policy is slightly different to other policies in that by default it will give you a basic client id enforcement structure if you don’t pass all the configuration. APIs will be secured with Client ID Enforcement with the client id and secret to be passed as a HTTP Basic Authentication Header and will apply to all endpoints in the API unless specified otherwise.
pom.xml
<clientIdEnforcement>
<configurationData>
<isHttpEndpoint>true</isHttpEndpoint>
</configurationData>
</clientIdEnforcement>
In this clientIdEnforcement the only thing we’re customising is that the API is a HTTP Endpoint, all other configuration is defaulted. This stops verbose plugins when you want basic functionality.
SLA Tiers
SLA Tiers can be applied in a similar way, and you can set as many or as few as you’d like
pom.xml
<tiers>
<tier>
<status>ACTIVE</status>
<autoApprove>true</autoApprove>
<name>Test</name>
<description>A Test SLA Tier</description>
<limits>
<limit>
<visible>true</visible>
<maximumRequests>10</maximumRequests
<timePeriodInMilliseconds>60000</timePeriodInMilliseconds>
</limit>
</limits>
</tier>
</tiers>
Alerts
Again, alerts can be configured, and you can configure multiple alerts. There’s a slight inconsistency in the data model returned from the Anypoint Platform APIs for Alerts, with some filter values containing a single string value, and others accepting an array of string values.
pom.xml
<alerts>
<alert>
<name>Fake Alert</name>
<enabled>true</enabled>
<severity>Info</severity>
<type>api-response-time</type>
<apiAlertsVersion>1.0.0</apiAlertsVersion>
<recipients>
<recipient>
<type>email</type>
<value>mike.jakeman@slalom.com</value>
</recipient>
</recipients>
<condition>
<aggregate>COUNT</aggregate>
<operator>LESS_THAN</operator>
<value>10</value>
<resourceType>api</resourceType>
<filter>
<param>
<operator>GREATER_THAN</operator>
<property>responseTime</property>
<values>
<value>1000</value>
</values>
</param>
</filter>
</condition>
<period>
<repeat>1</repeat>
<duration>
<count>1</count>
<weight>DAYS</weight>
</duration>
</period>
</alert>
</alerts>
To get around this when setting the value property ensure that you accept single values in an array and then you can treat them the same in your code.
API Manager Plugin
package domain.alerts;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.List;@Data
public class Filter {
private String operator;
private String property;
@JsonFormat(with=JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Integer> values;}
Future enhancements
Full API & Application Management
It makes no sense to have the API Manager and Runtime Manager plugins decoupled. While we want to have each plugin to have the ability to execute ideally what we want is to run the plugin and for our CI/CD solution to create and manage the API in API Manager, deploy the application to Runtime Manager and link the API and Application using autodiscovery without the developer needing to manage the API ID.
For this to work I’m in the process of refactoring the plugin to contain multiple projects, using modules in the plugins main pom.
pom.xml
<modules>
<module>runtime-manager-plugin</module>
<module>api-manager-plugin</module>
<module>core</module>
<module>deployer</module>
</modules>
This allows us to have a Runtime Manager plugin, an API Manager plugin and a Deployer plugin that will reuse the methods in both Runtime and API Manager plugins. In future when running the deploy phase we’ll ensure that the API is configured correctly, we’ll create an immutable version of the application, store that into a repository like Nexus and deploy the application.
Policy Versions
Currently the project only uses the latest version of the policy. As you can see there are many different versions of each policy, and without investigating each version it’s hard to find all the differences.
This feature is a lot lower down on my list of priorities as I do not need to support previous versions of policies.
Conclusion
Now we have a way of applying policies to our APIs automatically. We can remove access for developers to API Manager and we’re ensuring that the policy will be as the code states. Any changes to policies will have to go through change control processes and APIs can be versioned along with their API Policy configuration, something that is incredibly hard to do if you’re manually editing policies in API Manager.
The current plugin looks something like this:
pom.xml
<plugin>
<groupId>com.slalom</groupId>
<artifactId>api-manager-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<credentials>
<username>${cloudhub.username}</username>
<password>${cloudhub.username}</password>
</credentials>
<exchangeAsset>
<groupId>9a5c9957-32a7-449b-5678-1ce91fac7061</groupId>
<assetId>mike-test</assetId>
<version>1.0.0</version>
<productVersion>v1</productVersion>
</exchangeAsset>
<environment>Sandbox</environment>
<ipWhitelist>
<configurationData>
<ips>
<param>80.80.80.100</param>
<param>80.80.80.101</param>
</ips>
<ipExpression>#[vars.test]</ipExpression>
</configurationData>
</ipWhitelist>
<clientIdEnforcement>
<configurationData>
<isHttpEndpoint>true</isHttpEndpoint>
</configurationData>
</clientIdEnforcement>
<tiers>
<tier>
<status>ACTIVE</status>
<autoApprove>true</autoApprove>
<name>Test</name>
<description>A Test SLA Tier</description>
<limits>
<limit>
<visible>true</visible>
<maximumRequests>10</maximumRequests>
<timePeriodInMilliseconds>60000</timePeriodInMilliseconds>
</limit>
</limits>
</tier>
</tiers>
<alerts>
<alert>
<name>Fake Alert</name>
<enabled>true</enabled>
<severity>Info</severity>
<type>api-response-time</type>
<apiAlertsVersion>1.0.0</apiAlertsVersion>
<recipients>
<recipient>
<type>email</type>
<value>mike.jakeman@slalom.com</value>
</recipient>
</recipients>
<condition>
<aggregate>COUNT</aggregate>
<operator>LESS_THAN</operator>
<value>10</value>
<resourceType>api</resourceType>
<filter>
<param>
<operator>GREATER_THAN</operator>
<property>responseTime</property>
<values>
<value>1000</value>
</values>
</param>
</filter>
</condition>
<period>
<repeat>1</repeat>
<duration>
<count>1</count>
<weight>DAYS</weight>
</duration>
</period>
</alert>
</alerts>
</configuration>
<executions>
<execution>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
Running the plugin you’d add the plugin to the plugins section and the dependency to the dependencies section. After this is set run:
mvn com.slalom:api-manager-plugin:1.0.0-SNAPSHOT:apply-policies
This plugin would run prior to your deployment step so that the developer ensures the API is correctly configured at each deployment and part of the deploy phase when, if you’re using the Mule Maven Plugin the deployment will also be done.
It’s worth noting that Mule 3 and Mule 4 policies have different JSON structures in the Platform APIs so to manage different versions of APIs we’d need either different plugins or definitions to both mule 3 and mule 4 policies. I’ve decided to only develop for Mule 4.