How to implement an API Gateway & JSON Web Token (JWT) Based Authentication for EdgeX Foundry
Guest post by EdgeX Foundry contributors Tingyu Zeng, Senior Principal Software Engineer and Security Lead for Dell IoT platform development, and David Ferriera, Senior Director – Cloud Technology, Office of the CTO for Forgerock
EdgeX Foundry is composed of a set of micro services running inside Docker containers to provide flexible RESTful APIs for interoperable communications.
Managing and securing RESTful APIs, however, can be a challenge. RESTful APIs expose a broad and diverse attack surface that needs to be protected. This challenge is not unique to EdgeX Foundry. It is an issue that must be addressed by any project with a RESTful interface.
A common approach to address this challenge is to utilize SSL/TLS and some sort of authentication/authorization/access control against each individual micro service’s REST APIs. This is essentially shifting the burden of security to the micro service developers. Given many developers and many micro services, it is likely to see mixed implementations of the security tightly coupled with each micro service.
A better approach for protecting a set of RESTful API resources is the API Gateway model. It presents a unified interface to the outside world. Additional authentication mechanisms like OAuth2, JWT, API Key, HMAC etc. can be applied as well.
In the EdgeX Foundry project, security is designed as a service, and runs just like other services that provide valuable capability to the IoT environment. A reverse proxy/API gateway service sits between external users and all EdgeX micro services. It serves as a single point of access to external users and helps protecting the EdgeX micro services from the “wild west” of the Internet. Some of the benefits we are gaining here are:
- As a centralized access point for all of the EdgeX micro services, it minimizes the attack surface even the number of EdgeX micro services increases in the future.
- As an independent service, the implementation can be replaced easily if needed.
- Code related to protecting each micro service does not have to be placed in each micro service, thereby reducing different or problematic implementations and reducing the number of code changes if the security strategy needs to be modified in the future.
Kong (http://www.konghq.com), a popular open-source micro service API gateway, is chosen to secure the EdgeX micro service APIs in the upcoming California Release (June 2018) due to its flexibilities on API namespace management and plugin supports. Combined with JWT, it provides the basic security feature of authentication for EdgeX. Other authentication methods such as basic authentication, key authentication could be used in a similar way if needed.
This set of instructions below show how to setup Kong to be used with EdgeX to secure the RESTful APIs. Once setup, those calling on the EdgeX APIs can skip to steps 15-18 to invoke the EdgeX APIs through the reverse proxy.
Step 1. Run the postgres sql database for Kong. The postgress database is provided in a Docker container. The database will hold the configuration/policy information.
Step 2. Run the Kong database Docker container. Notice we are using Kong version 0.13.0 here since we are taking the services/routes object approach which is a preferred way based on Kong’s latest document.
Step 3. Run the Kong container. Notice in production environment we may need to minimize the listening footprint by avoid using broad interface such as 0.0.0.0:8001 and 0.0.0.0:8444.
Step 4. Start the EdgeX micro service based on the steps in the wiki
At this point we should have several Docker containers running, which include a couple of EdgeX micro services as well as the Kong and postgress database.
With the EdgeX micro services running, the APIs can be exercised as usual. Here we are using ping to check the health of the core-data micro service (core-data operates on port 48080 by default).
Step 5. Now we need to set up Kong to run on the same user-defined network inside Docker as the rest of EdgeX containers. The name of the user-defined network can be obtained from “docker network ls”. In the testing environment it show the name as “composefiles_edgex-network”. This can be done by running the command below:
Step 6. Here comes a tricky part– we need to get the IP address of the host for the Docker container. A “ipconfig” command in the windows console shows it is 192.168.1.151 in our testing environment. The IP address is the value of host parameter in setting up the redirect path of the proxy when configuring services and routes for the EdgeX micro services.
Step 7. Create a service entry for each of the EdgeX micro services. Here is an example to create a service entry for core-data of EdgeX.
Step 8. Create routes for each of the services. Below, a route is created for core-data. Multiple routes can be associated with one service if needed.
Step 9. At this point we have finished mapping the core-data REST API with the Kong reverse proxy. In order to make the “ping” REST call to the core-data micro service of EdgeX (previously http://localhost:48080/api/v1/ping as show above) one would need to call on http://localhost:8000/coredata/api/v1/ping . With service and routes defined in Step 6 and 7, any core-data REST API is called on using the base URL of reverse proxy http://localhost:8000 as the entry point.
The hostname and port of the reverse proxy are configurable (see the Kong documentation https://getkong.org/docs/0.13.x/configuration/#admin_api_listen). With Kong and the service/route configuration complete, the only EdgeX port that need be exposed is that of Kong.
Step 10. Next, we enable JSON Web Token (JWT) authentication to protect the core-data micro service. After doing so, any HTTP request to core-data will be denied if no JWT is associated.
Step 11. we Invoking the curl HTTP request against core-data REST API now results in an unauthorized 404 error indicating authentication is required.
Step 12. Assume we will have a user “adam” that wants to consume the protected core-data REST API. The customer needs to be defined on our reverse proxy first using the command below.
Step 13. Then we need to create a JWT credential for “adam”.
Step 14. Note: any consumer like “adam” can be removed from the associated JWT credential store later with an HTTP DELETE call as shown. Note “id” placeholder below would be replaced with the token we got from previous step.
Step 15. In step 13, we have got the JWT credential for the consumer “adam”. We can use an HTTP GET request like below to retrieve or re-fetch that same information.
Step 16. After obtaining the needed JWT credential we will be able to create a JWT token that can be used for authenticating “adam”. Ordinarily, we would write code to create the JWT token. For the sake of demonstration, we will create the JWT token manually here.
Go to https://jwt.io/ and use information in the previous step to get a JWT token. In the Payload Data elements, make sure to use the key value obtained in the previous step when creating the JWT token as the value to the “iss” field value (which is required) along with the username (optional). Replace “secret” in the Verifying Signature section with the secret value obtained in the previous step when creating the JWT token.
Step 17. Now we have JWT token associated with the consumer “adam” and it can be used it to authenticate through the proxy and access the REST API resources of EdgeX and avoid 404 unauthorized errors!
Step 18. Optionally, the JWT token can be passed with a query string instead.
In conclusion, we have implemented the EdgeX reverse proxy/API gateway and JWT authentication using Kong. This is not the end of the EdgeX security story for sure – authorization, access control list (ACL), URL parameters filtering, URL white listing etc., can also be integrated with existing security mechanisms to provide an even better shield around the EdgeX micro service APIs down the road. For now, Kong and JWT help to provide EdgeX with its first line of defense against inappropriate micro service access and allows us to incorporate other security capabilities in the future. And it does so in a way that can be easily augmented or replaced in the future and it does not require implementing that security in each micro service.
For more technical details, visit the EdgeX Foundry wiki page.