Back to Blog Archive

Using MuleSoft’s Anypoint platform APIs to provision CloudHub Dedicated Load-Balancer

Posted on: May 11, 2017
Author:
Kevin Jervis

In this blog post I demonstrate how to use the Anypoint Platform APIs to automate the provisioning of a dedicated load-balancer for an API deployed on the CloudHub runtime.

Why would one consider using the Anypoint Platform APIs?  Well they enable one to automate and codify DevOps processes more formally, effectively treating the infrastructure as code.  Software defined infrastructure can be provisioned reliably and consistently from scratch.  The infrastructure configuration is managed and maintained as code using common DevOps tooling and processes.

So let’s explore the reasons why one might choose to provision a dedicated load-balancer, rather than make use of the multi-tenant publicly accessible load balancer.

Well CloudHub provides a default entry endpoint for all APIs and applications deployed to the mule runtime.  It also provides a vendor managed wildcard certificate for *.cloudhub.io domain to secure connections using HTTPS.  Great!  However, what if you wish to host an API on the platform but under a brand or company specific domain?  Also, what if your API or service is required to use 2-way or mutual authentication leveraging client certificates?

This is where the ability for the customer to configure and manage a dedicated load-balancer capability on the Anypoint Platform offers significant value.  I’ll walk through a scenario demonstrating how the platform APIs can be used to define and provision this infrastructure and its dependencies.

Note: while certificate management and scalability for more than handful of clients is a valid concern, TLS 2-way/mutual authentication remains a popular security approach in many organisations and traditional service providers.

Scenario

Let’s assume an organisation (ACME Inc) has a requirement to expose an API on domain (acme.co.uk), with clients authenticated by an x509 certificate using TLS (formerly SSL). The target platform architecture is depicted below:


Target CloudHub platform architecture

The API consumers expect the APIs to be available at: https://api.acme.co.uk/* – a DNS CNAME entry is used to alias the dedicated load-balancer DNS records provided by CloudHub.

We instruct CloudHub to provision a dedicated load-balancer, with supplied certificate chains for both client(s) and server.  CloudHub then provisions two load-balancer instances with a static IP address and register a DNS A record using the name given to the dedicated load-balancer when it is created, e.g. <name>.lb.anypointdns.net

The API is deployed as a Mule application to two CloudHub workers within a private Anypoint VPC, through association with an environment.  The dedicated load-balancer is configured to route inbound requests to a downstream Mule application using the app name, internal worker address and port. For example, from: https://api.acme.co.uk/<appname> to: http://internal-worker-mule-<appname>.eu.cloudhub.io:8091/*

Note: all resources are provisioned within a single CloudHub region

The CloudHub infrastructure components necessary to support this scenario are described below:

Component Description
Environment Logical container for deployment of CloudHub applications; associated with a VPC to attach a specific network configuration.

Anypoint VPC

(Virtual Private Cloud)

Private and isolated network to host dedicated CloudHub load-balancer and worker instances
Dedicated Load-Balancer Private instance that automates scalability provisioning and accessibility of the workers hosting applications/APIs
Worker Hosts the Mule runtime on which the API/application is deployed

Anypoint Platform API interactions

I will illustrate how to use the Access Management v1 and CloudHub v1.51 Anypoint Platform APIs to:

  • Authenticate and obtain an access token
  • Search for a CloudHub environment (and create it should it not exist)
  • Create an Anypoint VPC, associate with the environment and update the firewall rules
  • Create and start a dedicated load-balancer

MuleSoft publishes the Anypoint platform APIs, described in RAML, to a public developer portal: https://anypoint.mulesoft.com/apiplatform/anypoint-platform/#/portals

When prototyping against the Anypoint Platform APIs I’ve found Postman particularly helpful for rapidly creating testable collections of API invocations.  In the examples below I abstract hardcoded values as either environment or global variables and reference them using its double curly bracket notation ({{}}).  For example: {{environmentName}} and {{access_token}}.

Tip: If you’re not familiar with Postman I recommend you check out Testing APIs with Postman by my colleague Antonio Garcia.

Authenticate with Anypoint Platform

The first step is to obtain a bearer access token for use with all the subsequent API calls.
Postman variables:

username
password
anypoint_username
Sup3r_Secret!_Pa55word

I issue a POST request to the Anypoint Access Management endpoint supplying user credentials substituted from environment variables in the body:

POST https://anypoint.mulesoft.com/login/

Body:
{ "username": "{{username}}", "password": "{{password}}" }
Note: only users with admin privileges are allowed to create VPC and Dedicated Load-Balancer configurations.

It returns a 200 OK response with JSON body containing the token

{
  "access_token": "258ef8e2-5176-4314-8fe6-e239762fbc76",
  "token_type": "bearer",
  "redirectUrl": "/home/"
}
Tip: use a dedicated user account with strong credentials in accordance with your organisation’s security policy for API automation tasks.

I extract the access_token and store it in a global variable of the same name so it can be referenced and the value substituted in the next steps.

Provision an Environment

Before creating the VPC and dedicated load-balancer I should check whether the environment exists, and if not, create it.  I need the environment identifier to associate it with the VPC.

I’ve assumed the infrastructure needs to be provisioned within a single business group, for which the identifier is readily available/already known and the environment name is “Production”.

Postman variables:

access_token

businessGroupId

environmentName
258ef8e2-5176-4314-8fe6-e239762fbc76

7abeab4e-307e-4bc4-8a8f-2ee89c44ab08

Production

Using the access token obtained in the previous request I issue a GET request to the environments endpoint providing the name of the environment I am searching for as a request parameter:

GET https://anypoint.mulesoft.com/accounts/api/organizations/{{businessGroupId}}
/environments?search={{environmentName}}

Headers:
Authorization: bearer {{access_token}}

It returns a 200 OK response containing a collection of environments matching the search criteria provided.  So in my example the environment “Production” already exists.

{
  "data": [
    {
      "id": "cdb57a83-f247-4388-b4e5-868112ede1d7",
      "name": "Production",
      "organizationId": "7abeab4e-307e-4bc4-8a8f-2ee89c44ab08",
      "isProduction": true
    }
  ],
  "total": 1
}

I extract the id for the environment named “Production” and store it in a global variable env_id, so it can be referenced and the value substituted in the subsequent requests.

If the environment does not exist, I can POST an additional API request to the same endpoint to create the environment

POST https://anypoint.mulesoft.com/accounts/api/organizations/{{businessGroupId}}
/environments

Headers:
Authorization: bearer: {{access_token}}
Content-Type:  application/json

Body:
{
  "name": "{{environmentName}}",
  "isProduction": true
}

A successful response will return a 201 Created containing the new environment definition.

{
  "id": "ab667cf2-df68-4127-a66d-a731828b3790",
  "name": "Production",
  "isProduction": true,
  "organizationId": "7abeab4e-307e-4bc4-8a8f-2ee89c44ab08"
}

The new id is stored in the same global variable: env_id to be referenced and the value substituted in the subsequent requests.

Remember: an Anypoint Platform environment is simply a logical container that exists entirely within a single business group.

Provision a VPC

The next task is to provision the VPC, into which the Mule app (API) will be deployed and dedicated load-balancer assigned.  The steps include:

  1. Check if a VPC associated with required environment exists
  2. Create a default VPC configuration and associate environment
  3. Update the VPC firewall rules

Postman variables:

access_token
258ef8e2-5176-4314-8fe6-e239762fbc76
businessGroupId
7abeab4e-307e-4bc4-8a8f-2ee89c44ab08
environmentName
Production
env_id
cdb57a83-f247-4388-b4e5-868112ede1d7
vpcName
acme-vpc
vpc_id

I issue a GET request to the vpcs endpoint of the cloudhub API for the identified business group

GET https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs

Headers:
Authorization: bearer {{access_token}}

A 200 OK response and empty data collection indicates that there are no VPCs defined within this business group.  

{
  "data": [],
  "total": 0
}

Had the data array contained any items, I could have evaluated each item to determine whether:

  • VPC name matched the value of the vpcName variable
  • VPC was associated with the environment identified by the env_id variable.

However since the VPC does not exist, I need to create one.  I create a default VPC configuration named using the variable vpcName and associate it with the “Production” environment I created in the earlier stage using the variable env_id:  

POST https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs

Headers:
Authorization: bearer {{access_token}}Content-Type:  application/json

Body:
{
  "name": "{{vpcName}}",
  "region": "eu-west-1",
  "cidrBlock": "192.168.1.0/24",
  "isDefault": false,
  "associatedEnvironments": [
    "{{env_id}}"
  ],
  "firewallRules": [
    {
      "cidrBlock": "0.0.0.0/0",
      "protocol": "tcp",
      "fromPort": 8081,
      "toPort": 8082
    },
    {
      "cidrBlock": "192.168.1.0/24",
      "protocol": "tcp",
      "fromPort": 8091,
      "toPort": 8092
    }
  ]
}

Key attributes of the JSON payload are highlighted below:

region A VPC is region-specific, so the CloudHub region must be specified.
cidrBlock Internal private network address space.  The smallest range of private network addresses is defined in this example.
isDefault Associates the VPC to all CloudHub environments that are not explicitly associated to another VPC.  Any new environments within the business group will inherit the VPC by default.
This is set to false to ensure VPC is associated with the required environment only.

associated

Environments

Applies VPC configuration to list of environments
The variable env_id assigns the VPC configuration to the “Production” environment only.
firewallRules Defines the inbound connectivity rules for access to those resources hosted within the VPC. Specifically:

  • Any host may access ports 8081/8082
  • Only those connections originating from within the VPC private network space may access ports 8091/8092.  

Note: those familiar with CloudHub may recognise these as the default rules in place for the public multi-tenant worker cloud.

A successful response will return a 201 Created containing the new VPC definition

{
  "id": "vpc-6acd240d",
  "name": "acme-vpc",
  "region": "eu-west-1",
  "cidrBlock": "192.168.1.0/24",
  "internalDns": {
    "dnsServers": [],
    "specialDomains": []
  },
  "isDefault": false,
  "associatedEnvironments": [
    "cdb57a83-f247-4388-b4e5-868112ede1d7"
  ],
  "ownerId": "7abeab4e-307e-4bc4-8a8f-2ee89c44ab08",
  "sharedWith": [],
  "firewallRules": [
    {
      "cidrBlock": "192.168.1.0/24",
      "protocol": "tcp",
      "fromPort": 8091,
      "toPort": 8092
    },
    {
      "cidrBlock": "0.0.0.0/0",
      "protocol": "tcp",
      "fromPort": 8081,
      "toPort": 8082
    }
  ]
}

I extract the id for the VPC named “acme-vpc” and store it in a global variable vpc_id so it can be referenced and the value substituted in the subsequent requests.

I remove the firewall rule to prevent direct access to any CloudHub workers hosting a Mule application on port 8081 and 8082.  I execute a PUT request using the vpc_id as part of the URI and provide a modified JSON representation of VPC resource in the request body:

PUT https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs
/{{vpc_id}}

Headers:
Authorization: bearer {{access_token}}
Content-Type:  application/json

Body:
{
  "name": "{{vpcName}}",
  "isDefault": false,
  "associatedEnvironments": [
    "{{env_id}}"
  ],
  "firewallRules": [
    {
      "cidrBlock": "192.168.1.0/24",
      "protocol": "tcp",
      "fromPort": 8091,
      "toPort": 8092
    }
  ]
}
Note: the VPC API endpoint does not allow the values for ownerId, region or cidrBlock attributes to be overridden.

A successful response is indicated by a 200 OK and includes the new representation of VPC configuration:

Response: 200 OK

{
  "id": "vpc-6acd240d",
  "name": "acme-vpc",
  "region": "eu-west-1",
  "cidrBlock": "192.168.1.0/24",
  "internalDns": {
    "dnsServers": [],
    "specialDomains": []
  },
  "isDefault": false,
  "associatedEnvironments": [
    "cdb57a83-f247-4388-b4e5-868112ede1d7"
  ],
  "ownerId": "7abeab4e-307e-4bc4-8a8f-2ee89c44ab08",
  "sharedWith": [],
  "firewallRules": [
    {
      "cidrBlock": "192.168.1.0/24",
      "protocol": "tcp",
      "fromPort": 8091,
      "toPort": 8092
    }
  ]
}

The VPC has been successfully created and associated with the “Production” environment in the ACME business group.  Any applications deployed within the “Production” environment in region “eu-west-1” will no longer be publicly accessible from the internet, nor via the default multi-tenant public CloudHub load-balancer.

Provision a Dedicated Load-Balancer

The next task is to provision the dedicated load-balancer, which will route requests from the public internet to the Mule app (API) deployed privately within the VPC.  It will also negotiate the TLS connection handshake and establish a mutually authenticated HTTPS connection through certificate exchange.   

The steps include:

  1. Check if the dedicated load-balancer exists within the VPC
  2. Create the dedicated load-balancer configuration with digital certificates and application proxy mapping rules
  3. Start the dedicated load-balancer

Postman variables:

access_token
258ef8e2-5176-4314-8fe6-e239762fbc76
businessGroupId
7abeab4e-307e-4bc4-8a8f-2ee89c44ab08
environmentName
Production
env_id
cdb57a83-f247-4388-b4e5-868112ede1d7
vpcName
acme-vpc
vpc_id
vpc-6acd240d
lbName
acme-example
loadbalancer_id

I issue a GET request to the loadbalancers endpoint of the cloudhub API for the identified business group and VPC using the businessGroupId and vpc_id respectively.

GET https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs/
{{vpc_id}}/loadbalancers

Headers:
Authorization: bearer {{access_token}}

A 200 OK response and empty data collection indicates that there are no dedicated load-balancers defined within the identified VPC.  

{
  "data": [],
  "total": 0
}

Since no dedicated load-balancers exist within the VPC I can safely create a new one by sending a POST request to the same endpoint.  The VPC identifier provided in the URI will control where it is provisioned.  I need to provide a name, at least one sslEndpoint configuration, including a mapping rule to handle the mapping of the downstream Mule application name.

POST https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs
/{{vpc_id}}/loadbalancers

Headers:
Authorization: bearer {{access_token}}
Content-Type:  application/json

Body:
{
  "name": "{{lbName}}",
  "httpMode": "off",
  "defaultSslEndpoint": 0,
  "sslEndpoints": [
    {
        "publicKey": "-----BEGIN CERTIFICATE-----\nMIID8DCCAtigAwIBAgIJAP4p6K9rl8fSMA0G
CSqGSIb3DQEBCwUAMFgxCzAJBgNV\nBAYTAlVLMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYDVQQHEwZMb25kb24xD
TALBgNV\nBAoTBEFDTUUxFzAVBgNVBAMTDmFwaS5hY21lLmNvLnVrMB4XDTE2MDkyMjE4NTQ1\nOVoXDTE3MDky
MjE4NTQ1OVowWDELMAkGA1UEBhMCVUsxEDAOBgNVBAgTB0VuZ2xh\nbmQxDzANBgNVBAcTBkxvbmRvbjENMAsGA
1UEChMEQUNNRTEXMBUGA1UEAxMOYXBp\nLmFjbWUuY28udWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAo
IBAQDDKZn+\no0gSXg2Fe7Qk1ddvVzRGmc8NYp9zoDt3rtz0YS3uOSqW9hUV1aVbXoisXt5MkPeI\nW2AARxZTj
It8wLm1ctPAf9lxavo6M/JJYOFp4wPTYDRGbygSZBdsh5h6s7VG47R6\nxg20gjTvPkCv8+SGISf4G2AGxmR3Ka
qGiPs1MyCO+k7RlWTnx9mu84j6+kfDXyLn\nn/A4CrrwMhENQP8ZD0MrqTMPepabk8Ck/A3tRucDYp9N0dMnMFT
CKBQ/rqpSIhRX\nFVu2OxQQEbc4rFVoAo3Q5O27trNa3hTDhxHhsyGgDFAlv3baUi/ZQzzjRR/CXwCc\nouBxnm
7ImncI3w2jAgMBAAGjgbwwgbkwHQYDVR0OBBYEFC54kkOedUZ8gDId8qVJ\neznOYel+MIGJBgNVHSMEgYEwf4A
ULniSQ551RnyAMh3ypUl7Oc5h6X6hXKRaMFgx\nCzAJBgNVBAYTAlVLMRAwDgYDVQQIEwdFbmdsYW5kMQ8wDQYD
VQQHEwZMb25kb24x\nDTALBgNVBAoTBEFDTUUxFzAVBgNVBAMTDmFwaS5hY21lLmNvLnVrggkA/inor2uX\nx9I
wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAma+i5IJbFobUhOnK\ncztyh2rg5am1F1rf84NcP6p2
q/uC0IfNHcGJgZaEC/Avo3Qc9yWNWJdkV6RLvN/m\nLlhLAtlQ5VVZjyRW2tYR8v4P9L1vhrQdYdFnIfdSC+GcP
0+hWHyG8ajTONgIkxsP\nNPGF0QjERWf4oHzd3dtXlWo+6ZQpKSbyH9OBDdoPeHH6qz4TUnLtZjrr8ij3O66L\n
qUWMXlbQkFYzfwGPmuol+ei3oR8i230LkJbZMRcriWpVATpYj8OjSzxS6iiXB8Oa\nZaknZynJBVm1/D63gj3sW
u3y8Ib4zvac7WQgcpbjBNWJt5LHa6SS9BOZYQO/dtRv\nSGw9iw==\n-----END CERTIFICATE-----\n",
        "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwymZ/qNIEl4NhXu
0JNXXb1c0RpnPDWKfc6A7d67c9GEt7jkq\nlvYVFdWlW16IrF7eTJD3iFtgAEcWU4yLfMC5tXLTwH/ZcWr6OjPy
SWDhaeMD02A0\nRm8oEmQXbIeYerO1RuO0esYNtII07z5Ar/PkhiEn+BtgBsZkdymqhoj7NTMgjvpO\n0ZVk58f
ZrvOI+vpHw18i55/wOAq68DIRDUD/GQ9DK6kzD3qWm5PApPwN7UbnA2Kf\nTdHTJzBUwigUP66qUiIUVxVbtjsU
EBG3OKxVaAKN0OTtu7azWt4Uw4cR4bMhoAxQ\nJb922lIv2UM840Ufwl8AnKLgcZ5uyJp3CN8NowIDAQABAoIBA
AnISwzFeHZbOohH\nVBdIlLzi/R7Q1N0R5XYxQOnV3QNu35OCUA0zFol6pQg4a9dgBfqGhbHTCrPY3Fih\nwdgl
/OkIcW7Duc2ClDpYojtlXMcm9ukgeF5TcgnB9J6fCViVK2/7N++zf7pdkHOx\nhiKLshe7D7rS/HQ6e4yxjPejZ
Bwddop4WDyuiinPuFjnBv5Ihso1WKyqvuYByA6p\nAWs5s8TIR8nEwFhw1LKoNW/wCYGfyrXO0OLzoh4lGdfTUE
7JxYfHcyvidmpa9wOV\niaT128ztGy8ilMSHdcyE7KOHH2igoWL649ND3cdpEduYiQQBSwj0dr+A+1WFdyEp\n1
5nA3VECgYEA4jBSTD96DhKAzBFskgYe0MHvsDrOSq71HTeJKh5ODtmbWHpi3sNO\nx4H/1xkIqBx0JJ+opfHE3b
PswizANozkI9KV8LWRblOuA3W17tZpejudYD7IIrEl\nTwtCGIwP2sYatkUlYXTOP8JfVOFK+hz4+gnQPBNAOFh
kBxPrScxyy+kCgYEA3OJx\n3xxLRzhB+bHWdh0vO+Cdv1ttYnUA4bhD5g7kDebDwCOOU+SWo+OAbCVboijwWL9o
\nt4bfIWuIJuPedw6CLnjsxrZZ5k66KFQttVSBm8FToppQPAf6fmtLyG+YB1su5qux\npLk6E4kxVZQcX2klpZW
vIb5SJeqQT6eELYBXcasCgYAPKyRpvpwF05HVnZmFVWm9\noDPdwqPQ+Fixw+ar72pWrpsvXM3CmKBw2rP4jdI5
y+ayCg76SfNk1ssa3ntpbRtj\nl5y8jsCZAH2b98nioXhjXRoZuTwcr3qMCXo+QS5TApQKAysVlQq71eIkxBMOI
OFF\ni2Dulm/pF2xl2R0fTtmn+QKBgQCQva5Ctma5z6qj9Z4EHhGymfikyUN/bg5a2wVb\nPszAM0R4ppQAc10n
HdAsjbpjI5KDhUIYaT/TxYQdf9pqh85Up5b4i8uTOGmSPb8K\nAMtBhkjI13fWLqA9sQ+i00V7JIwySbYJq/rbj
F4QpxKEjwFFZTWy1eU8xV01ZR8f\nNeeXKwKBgG8vGFntuqvvRshpaeh19diiKf8f3r2tPNwBWXXEl8KhpT6A8d
AgFZi1\ndQsxTaGpuTwxQugVqpoLEYGXX+uluIMeU6rHPoKhC3XrGWNuEaOp9ya43wuWq4pb\n3lrSiiXjLaVAj
kzLASCW0IqmcKiDbWSTg1N7rVPbLOkEQPFcvioa\n-----END RSA PRIVATE KEY-----\n",
        "clientCert": "-----BEGIN CERTIFICATE-----\nMIIEUTCCAzmgAwIBAgIJAPJeWbf+PxPvMA0
GCSqGSIb3DQEBBQUAMHgxCzAJBgNV\nBAYTAlVLMQ8wDQYDVQQIEwZMb25kb24xDzANBgNVBAcTBkxvbmRvbjEV
MBMGA1UE\nChMMQUNNRSBDTElFTlQxMRQwEgYDVQQLEwtNb2JpbGUgQXBwczEaMBgGA1UEAxMR\nbW9iaWxlLmF
jbWUuY28udWswHhcNMTYwOTIyMTg0MDA4WhcNMTcwOTIyMTg0MDA4\nWjB4MQswCQYDVQQGEwJVSzEPMA0GA1UE
CBMGTG9uZG9uMQ8wDQYDVQQHEwZMb25k\nb24xFTATBgNVBAoTDEFDTUUgQ0xJRU5UMTEUMBIGA1UECxMLTW9ia
WxlIEFwcHMx\nGjAYBgNVBAMTEW1vYmlsZS5hY21lLmNvLnVrMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIB
CgKCAQEAsuKa302NxYQLTQEuKdu8umFNLbtDlSS3q2wt9KHOJTJt4871\nTPD6u9WNTre4lPZnZo84mYQpZ2e87
CekWctt8amKubErzFyiFk+CXRsQRADTgZFF\nqgqDpTqgTNCh2+fdldPkQXw8E0FOrK94tuia7G80y9q5fUDZHv
M9dmgQCT9T8BmP\nJq4GFgMiZ1VanAvwp2P6lVgwdgYJNF3BuibUVHrUZAtm/POKXoSp5ggejVb7o7Dv\nP9+7a
ccP0v91WQwOaO0m3twBpo3HpGQLkJgEAX2/ah32HDVGuP9DPV14dGyEDA0p\nwZHiqVJhRndcTzS0/mLd4GBRw1
JL/LO/yaoxWwIDAQABo4HdMIHaMB0GA1UdDgQW\nBBSt1l3gqaUukUhiqBzgNFg4VK2MsDCBqgYDVR0jBIGiMIG
fgBSt1l3gqaUukUhi\nqBzgNFg4VK2MsKF8pHoweDELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEP\nMA
0GA1UEBxMGTG9uZG9uMRUwEwYDVQQKEwxBQ01FIENMSUVOVDExFDASBgNVBAsT\nC01vYmlsZSBBcHBzMRowGAY
DVQQDExFtb2JpbGUuYWNtZS5jby51a4IJAPJeWbf+\nPxPvMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBALDYSCSy0AdUiIv9\nifRAoZKTs/BV8rXbfDF2Z1Uwu2Ecvf3lzbSin+yQsaY3kt6is11ZaagKp7lndqFu\
npPtCH9L9AcZlpTgdNo42ocwPbR5Y5K/1RRwmuiH5BTRfb0JkPisQYGYcMcNZ+8TI\ncrVVVoe06PDfwAn7CrgZ
rhSku0575lB2VjvaT10tOLbLU7X18LWIwoflpgRXNGgF\n7zlxqsfdAiFWHNFnI+tJxTKcejujPEFRzFh2crjIR
oDxY8WsWZu479jFdIs3QM3A\nSjGrUJUnnmQFDMHjq62P5P71hcGG6HMBQ1cDN5qdIEC0btrp4cJmSVABnIRux1
z6\njm9sGUo=\n-----END CERTIFICATE-----\n",
        "verifyClientMode": "on",
        "mappings": [
            {
                "inputUri": "/{app}/",
                "appName": "{app}",
                "appUri": "/"
            }
        ]
    }
  ]
}

Key attributes of the JSON payload are highlighted below:

name Human readable name of the load-balancer – I use a Postman environment variable to set this.
httpMode Defines whether the load-balancer will accept HTTP requests on port 80.  It can also be configured to redirect HTTP request on port 80 to HTTPS port 443.  
I set this to ‘off’ so all requests to port 80 will be refused.
defaultSslEndpoint An integer array index that indicates which sslEndpoint configuration should be considered the default.
sslEndpoint An array of sslEndpoint configurations.
publicKey PEM encoded x509 certificate (or entire certificate chain) that the dedicated load-balancer will serve to clients.
Note: the PEM encoded certificate should not be encrypted and include newline characters (\n).
privateKey PEM encoded private key that corresponds to the first x509 certificate provided in the preceding publicKey attribute.
Note: the PEM encoded private key must be passphraseless and include newline characters (\n).
clientCert PEM encoded x509 certificate (or certificate trust chain) that will be used to authenticate clients.
Note: the PEM encoded certificate should not be encrypted and include newline characters (\n).
verifyClientMode Enable client authentication – I set this to ‘on’ so 2-way/mutual authentication is required.
mappings An array of mapping rules, which define how components of an input URL are used to construct an output URL that targets a downstream CloudHub application.
inputUri Defines how components of the URL path are parameterised as variables, indicated by curly braces ({}).
appName Defines how the target CloudHub application name is set.
appUri Defines the URL path passed to the target CloudHub application.

Mappings Example: assume a client request made directly to the dedicated load-balancer:  acme.lb.anypointdns.net/acme-demo/api

inputUri: /{app}/ will parameterise “acme-demo” as the variable {app}
appName: {app} will use an application name “acme-demo” in DNS address
apiUri: / will result in a target application URL of: mule-internal-worker-acme-demo.eu.cloudhub.io/api

A successful response will return a 201 Created with the JSON representation of the load-balancer resource.

{
  "id": "58fa052de4b053182666af20",
  "name": "acme",
  "domain": "lb.anypointdns.net",
  "state": "STOPPED",
  "ipAddresses": [
    "34.253.47.169",
    "34.252.254.34"
  ],
  "ipWhitelist": [
    "0.0.0.0/0"
  ],
  "httpMode": "off",
  "defaultSslEndpoint": 0,
  "sslEndpoints": [
    {
      "privateKeyDigest": "6fcae5836a07083132686b637917c214f926117b",
      "publicKeyDigest": "85d466678d4acebae9ce946b7fa751444326642e",
      "publicKeyCN": "api.acme.co.uk",
      "clientCertDigest": "a4c385d7974f9b92692888e90d067f35d9fbfa4b",
      "clientCertCN": "mobile.acme.co.uk",
      "verifyClientMode": "on",
      "tlsv1": false,
      "mappings": [
        {
          "inputUri": "/{app}/",
          "appName": "{app}",
          "appUri": "/",
          "upstreamProtocol": "http"
        }
      ]
    }
  ]
}

I extract the id for the load-balancer and store it in a global variable loadbalancer_id as I will need to use it in subsequent API requests.

Before I move on there are a couple of points to note about attributes of the JSON payload:

state Initial state is STOPPED; CloudHub must be told to start the dedicated load-balancer
ipAddresses CloudHub provisions a dedicated load-balancer with two instances for redundancy.
*Digest The API only returns the digest of each certificate.  The CN (Common Name) within each certificate has been extracted for identification purposes.
mappings Mapping rule includes an upstreamProtocol attribute.  Since it  was not provided in the request it uses a default value: “http”.  This means the private communication within the VPC between the dedicated load-balancer and the CloudHub workers hosting the Mule application uses HTTP (target port: 8091).  
Note: to use HTTPS include the upstreamProtocol attribute in the mapping rule with value “https” (target port: 8092).

With the dedicated load-balancer created I can verify the deployment status using the deployments endpoint passing the variable loadbalancer_id in the URI:

GET https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs
/{{vpc_id}}/loadbalancers/{{loadbalancer_id}}/deployments

Headers:
Authorization: bearer {{access_token}}

A 200 OK response returns a collection of deployment states  

{
  "data": [
    {
      "id": "58fa0534e4b053182666af35",
      "state": "DONE",
      "ticketConfig": "{\"id\":\"58fa052de4b053182666af20\",\"name\":\"acme\",
\"prvtDomainPrefix\":\"mule-worker-internal-\",\"prvtDomainSuffix\":\"eu.cloudhub.io\",
\"imageName\":\"NGINX_V3\",\"instanceSize\":\"c4.large\",\"vpcId\":\"vpc-6acd240d\",
\"workers\":0,\"deploymentId\":\"58fa0534e4b053182666af35\",\"version\":2}",
      "createTime": 1492780340338,
      "startTime": 1492780341152,
      "endTime": 1492780342017
    }
  ],
  "total": 1
}

To start the dedicated load-balancer I patch the API resource I created previously.  The semantics of the JSON PATCH operation are defined by RFC 6902.  Effectively the payload identifies a replace operation to perform on the state attribute with the value STARTED.

PATCH https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs
/{{vpc_id}}/loadbalancers/{{loadbalancer_id}}

Headers:
Authorization: bearer {{access_token}}
Content-Type:  application/json

Body:
[
  {
    "op": "replace",
    "path": "/state",
    "value": "STARTED"
  }
]

A 200 OK response and a representation of the resource with a state: STARTING indicates that the dedicated load-balancer has been instructed to start.  

{
  "id": "58fa052de4b053182666af20",
  "name": "acme",
  "domain": "lb.anypointdns.net",
  "state": "STARTING",
  "ipAddresses": [
    "34.253.47.169",
    "34.252.254.34"
  ],
  "ipWhitelist": [
    "0.0.0.0/0"
  ],
  "httpMode": "on",
  "defaultSslEndpoint": 0,
  "sslEndpoints": [
    {
      "privateKeyDigest": "6fcae5836a07083132686b637917c214f926117b",
      "publicKeyDigest": "85d466678d4acebae9ce946b7fa751444326642e",
      "publicKeyCN": "api.acme.co.uk",
      "clientCertDigest": "a4c385d7974f9b92692888e90d067f35d9fbfa4b",
      "clientCertCN": "mobile.acme.co.uk",
      "verifyClientMode": "on",
      "tlsv1": false,
      "mappings": [
        {
          "inputUri": "/{app}/",
          "appName": "{app}",
          "appUri": "/",
          "upstreamProtocol": "http"
        }
      ]
    }
  ]
}

Using the deployments endpoint with the load-balancer identifier captured previously, I can monitor the status of the load-balancer startup

GET https://anypoint.mulesoft.com/cloudhub/api/organizations/{{businessGroupId}}/vpcs
/{{vpc_id}}/loadbalancers/{{loadbalancer_id}}/deployments

Headers:
Authorization: bearer {{access_token}}

Initially the state reported will display PROCESSING

{
  "data": [
    {
      "id": "58fa3541e4b053182666e92a",
      "state": "PROCESSING",
      "ticketConfig": "{\"id\":\"58fa052de4b053182666af20\",\"name\":\"acme\",
\"prvtDomainPrefix\":\"mule-worker-internal-\",\"prvtDomainSuffix\":\"eu.cloudhub.io\",
\"imageName\":\"NGINX_V3\",\"instanceSize\":\"c4.large\",\"vpcId\":\"vpc-6acd240d\",
\"workers\":2,\"deploymentId\":\"58fa3541e4b053182666e92a\",
\"activeDeploymentId\":\"58fa0534e4b053182666af35\",\"version\":2}",
      "createTime": 1492792641513,
      "startTime": 1492792642115
    },
    {
      "id": "58fa0534e4b053182666af35",
      "state": "DONE",
      "ticketConfig": "{\"id\":\"58fa052de4b053182666af20\",\"name\":\"acme\",
\"prvtDomainPrefix\":\"mule-worker-internal-\",\"prvtDomainSuffix\":\"eu.cloudhub.io\",
\"imageName\":\"NGINX_V3\",\"instanceSize\":\"c4.large\",\"vpcId\":\"vpc-6acd240d\",
\"workers\":0,\"deploymentId\":\"58fa0534e4b053182666af35\",\"version\":2}",
      "createTime": 1492780340338,
      "startTime": 1492780341152,
      "endTime": 1492780342017
    }
  ],
  "total": 2
}

The state will be replaced with DONE once complete and a corresponding endTime field populated.  Furthermore, another GET on the identified load-balancer API endpoint will return the JSON representation with state: STARTED once the process is complete.

Manage Client DNS

CloudHub uses a set of naming conventions to manage Mule application worker DNS entries: https://docs.mulesoft.com/runtime-manager/cloudhub-networking-guide

In my example the Mule application named ‘acme-demo’ is deployed in a european region. CloudHub will register the following internal DNS entry: mule-internal-worker-acme-demo.eu.cloudhub.io

Note: the IP addresses associated with this DNS entry will only be routable from within the VPC (e.g. the dedicated load-balancer).

CloudHub will also manage a DNS A record for the dedicated load-balancer using the name provided when the configuration was created (e.g. “acme”): acme.lb.anypointdns.net

Performing an nslookup on this DNS address reveals the same IP addresses returned by the API for the dedicated load-balancer resource.

nslookup acme.lb.anypointdns.net

Non-authoritative answer:
Name: acme.lb.anypointdns.net
Address: 34.253.47.169
Name: acme.lb.anypointdns.net
Address: 34.252.254.34

To expose the API on a customer specific web address (e.g. api.acme.co.uk) one would register, using a preferred DNS provider, a CNAME record to alias the dedicated load-balancer DNS A record managed by CloudHub.  

For the purposes of this blog post I can simulate the DNS CNAME entry with a local hosts entry as follows:

34.253.47.169    api.acme.co.uk    #alias load-balancer static ip-address

This allows a client (e.g. a browser) to verify the server identity using a customer-specific hostname/domain and the public server certificate uploaded to the dedicated load-balancer.

So API consumers would issue a GET request to the following URL to retrieve the status of the deployed ACME demo API:

GET https://api.acme.co.uk/acme-demo/status

The dedicated load-balancer will authenticate the client based on the client certificate provided as part of the TLS handshake to establish the HTTPS connection.

To verify the infrastructure (environment, VPC and dedicated load-balancer) are provisioned successfully the API (Mule application) must be deployed to one or more workers in the Production environment.  Application deployment on Anypoint Platform can be achieved through a number of mechanisms: Anypoint Runtime Manager UI, mule-maven-plugin, Anypoint CLI and the platform APIs.  These are already well publicised, so not covered in detail here.

Provisioned Infrastructure

The diagram below shows the state of the end-to-end solution with infrastructure resources configured on the Anypoint Platform.

Provisioned Cloudhub platform architecture
Arguably I could now create a process API deployed on the Anypoint Platform to consume APIs provided by MuleSoft to automate repeatable common infrastructure provisioning.  How about that?

So, having explored the Anypoint Platform APIs and prototyped the process using Postman I hope readers feel empowered to codify their process and platform API interactions more formally.

Author:
Kevin Jervis

3 Comments for “Using MuleSoft’s Anypoint platform APIs to provision CloudHub Dedicated Load-Balancer”

  1. MuleStudent says:

    Awesome!

  2. Manish Yadav says:

    Wow !!! What a blog Kevin…As I said earlier all blog of Ricston are too Good.

  3. Amalendra says:

    Best explanation of LB and VPC in CloudHub. Great work Kevin

Comments

Contact Us

Ricston Ltd.
Triq G.F. Agius De Soldanis,
Birkirkara, BKR 4850,
Malta
MT: +356 2133 4457
UK: +44 (0)2071935107

Send our experts a message

Need Help?
Ask our Experts!