Blog

Deploying Kong API gateway configurations with AWS CloudFormation

03 Nov, 2017
Xebia Background Header Wave

With AWS CloudFormation it is easy to deploy your applications with AWS Elastic Container Service. However, if you
want to provide access to your applications through the Kong API Gateway, you are left with one additional step in the
deployment process: configuring the Kong gateway. This complicates the deployment process for ECS applications.
With the Custom CloudFormation Resources for the Kong API Gateway we can deploy both the application
and the Kong API Gateway configuration in one go!
The Custom Resources allow you to configure Kong APIs, Plugins, Consumers, ACL and credentials. The resources also allow
you to specify a JWT token to be generated and passed with each request to Kong.

How do I add a Kong API?

It is quite easy: you specify a CloudFormation resource of type Custom::KongAPI:

  "SampleApi": {
    "Type": "Custom::KongAPI",
    "Properties": {
      "API" : {
    "name": "headers",
    "uris": [ "/headers" ],
    "upstream_url": "https://httpbin.org",
    "strip_uri": false
      },
      "AdminURL": "https://kong:8001",
      "ServiceToken": { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function:binxio-cfn-kong-provider" ]]}
    }
  }

The API object takes all of the properties as defined by add-api.
If you need to access the api-id in your cloudformation module, you can reference attribute id.

{ "Fn::GetAtt": [ "SampleApi", "id" ]}

How do I add a Kong Plugin?

You specify a CloudFormation resource of type Custom::KongPlugin, as follows:

  "KeyAuthPlugin": {
    "Type": "Custom::KongPlugin",
    "Properties": {
      "Plugin" : {
    "name": "key-auth",
    "api_id": { "Fn::GetAtt": [ "SampleApi", "id" ] },
      },
      "AdminURL": "https://kong:8001",
      "ServiceToken": { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function:binxio-cfn-kong-provider" ]]}
    }
  }

the Plugin object takes all properties as defined by add-plugin.
If you leave out the api_id, you will configure a global plugin. If you add a consumer_id you will configure
the plugin for the the specified consumer. If you add both, it will be for that consumer on that api.
If you need to access the plugin-id in your cloudformation module, you can reference attribute id.

{ "Fn::GetAtt": [ "KeyAuthPlugin", "id" ]}

How do I add a Kong Consumer?

You specify a CloudFormation resource of type Custom::KongConsumer, as follows:

  "KongConsumer": {
    "Type": "Custom::KongConsumer",
    "Properties": {
      "Consumer" : {
    "username": "kong-admin",
      },
      "AdminURL": "https://kong:8001",
      "ServiceToken": { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function:binxio-cfn-kong-provider" ]]}
    }
  }

The Consumer object takes all of the properties as defined by add-consumer.
If you need to access the consumer-id in your cloudformation module, you can reference attribute id.

{ "Fn::GetAtt": [ "KongConsumer", "id" ]}

How do I add a Consumer to a ACL group?

You specify a CloudFormation resource of type Custom::KongACL, as follows:

  "KongACL": {
    "Type": "Custom::KongACL",
    "Properties": {
      "ACL" : {
    "group": "admin",
    "consumer_id": {"Fn::GetAtt": [ "KongConsumer", "id" ]}
      },
      "AdminURL": "https://kong:8001",
      "ServiceToken": { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function:binxio-cfn-kong-provider" ]]}
    }
  }

The ACL object takes all of the properties as defined by the ACL plugin in associating-consumers

How do I add to Credentials to a Consumer?

You specify a CloudFormation resource of type Custom::KongCredential, as follows:

    "KongConsumerApiKey": {
      "Type": "Custom::KongCredential",
      "DependsOn": "KongConsumer",
      "Properties": {
        "PluginName": "key-auth",
        "Credential": {
          "consumer_id": { "Fn::GetAtt":  [ "KongConsumer", "id" ] },
          "key": "66471684-6BDF-4F11-868F-B513CC391520"
        },
      "AdminURL": "https://kong:8001",
      "ServiceToken": { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function:binxio-cfn-kong-provider" ]]}
    }
  }

The Credential object takes all of the properties as defined by plugin.

How do authenticate the Kong API requests?

You can add the object JWT to any of the custom resources, providing the issuer name and the Name of a Parameter in the Parameter Store. This parameter should contain the RSA private key with which the JWT token is to be signed. The generated token will be passed on the authentication header.

    "KongConsumerApiKey": {
      "Type": "Custom::Kong..."
      "Properties": {
    "JWT": {
       "Issuer": "admin",
        "PrivateKeyParameterName": "/kong/admin/private-key"
     }
         ....
    }
  }

Installation

To install these custom resources, clone cfn-kong-provider from git, and type:

cd cfn-kong-provider
aws cloudformation create-stack \
 --capabilities CAPABILITY_IAM \
 --stack-name cfn-kong-provider \
 --template-body file://cloudformation/cfn-resource-provider.json 

aws cloudformation wait stack-create-complete  --stack-name cfn-kong-provider 

If you want the provider to connect to a Kong API gateway which is only accessible from within a VPC, specify the parameters

AppVPC, PrivateSubnets and DefaultSecurityGroup.

    --parameters \
        ParameterKey=AppVPC,ParameterValue=vpid-id \
        ParameterKey=PrivateSubnets,ParameterValue=subnet-id1,subnet-id2 \
        ParameterKey=DefaultSecurityGroup,ParameterValue=sg-id

This CloudFormation template will use our pre-packaged provider from s3://binxio-public-{{region}}/lambdas/cfn-kong-provider-latest.zip.

Demo

For the demo to work, we need a deployed Kong API Gateway that is accessible from the Internet. If you do

not have one, type:

aws cloudformation create-stack --stack-name kong-environment \
 --template-body file://cloudformation/kong.json \
 --parameters ParameterKey=KongKeyName,ParameterValue=#insert-your-key-name-here#

aws cloudformation wait stack-create-complete  --stack-name kong-environment

ADMIN_URL=$(aws --output text --query 'Stacks[*].Outputs[?OutputKey==<code>AdminURL].OutputValue' \
     cloudformation describe-stacks --stack-name kong-environment)

Note that it will create an entire Kong setup, including a VPC, loadbalancers and a Postgres Database. Do not forget to clean up
afterwards.

aws cloudformation create-stack --stack-name cfn-kong-provider-demo \
 --template-body file://cloudformation/demo-stack.json \
 --parameters ParameterKey=AdminURL,ParameterValue=$ADMIN_URL

aws cloudformation wait stack-create-complete  --stack-name cfn-kong-provider-demo

To validate the result, set the environment variable ADMIN_URL to point to your Kong API admin port and type:

curl $ADMIN_URL/apis/header-api
curl $ADMIN_URL/apis/header-api/plugins
curl $ADMIN_URL/consumers/johndoe
curl $ADMIN_URL/consumers/johndoe/acls
curl $ADMIN_URL/consumers/johndoe/basic-auth
curl $ADMIN_URL/consumers/johndoe/key-auth

Conclusion

By using the Kong Custom CloudFormation providers you can deploy both the application and the Kong API configuration for that
application in a single CloudFormation template, simplifying the deployment process.
If you have any questions, do not hesitate to contact me.

Mark van Holsteijn
Mark van Holsteijn is a senior software systems architect at Xebia Cloud-native solutions. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts