API Gateway
Introduction
EdgeX 3.0
This content is completely new for EdgeX 3.0. EdgeX 3.0 uses a brand new API gateway solution based on NGINX and Hashicorp Vault instead of Kong and Postgres. The new solution means that EdgeX 3.0 will be able to run in security enabled mode on more resource-constrained devices.
API gateways are used in microservice architectures that expose HTTP-accessible APIs to create a security layer that separates internal and external callers. An API gateway accepts client requests, authenticates the client, forwards the request to a backend microservice, and relays the results back to the client.
Although authentication is done at the microservice layer in EdgeX 3.0, EdgeX Foundry as elected to continue to use an API gateway for the following reasons:
-
It provides a convenient choke point and policy enforcement point for external HTTP requests and enables EdgeX adopters to easily replace the default authentication logic.
-
It defers the urgency of implementing fine-grained authorization at the microservice layer.
-
It provides defense-in-depth against microservice authentication bugs and other technical debt that might otherwise put EdgeX microservices at risk.
The API gateway listens on two ports:
-
8000: This is an unencrypted HTTP port exposed to localhost-only (also exposed to the edgex-network Docker network). When EdgeX is running in security-enabled mode, the EdgeX UI uses port 8000 for authenticated local microservice calls.
-
8443: This is a TLS 1.3 encrypted HTTP port exposed via the host's network interface to external clients. The default TLS certificate on this port is untrusted by default and should be replaced with a trusted certificate for production usage.
EdgeX 3.0 uses NGINX as the API gateway implementation and delegates to EdgeX's secret store (powered by Hashicorp Vault) for user and JWT authentication.
Start the API Gateway
The API gateway is started by default in the Docker-based EdgeX deployment using the Docker Compose files found at https://github.com/edgexfoundry/edgex-compose/.
In Docker, the command to start EdgeX inclusive of API gateway related services is (where "somerelease" denotes the EdgeX release, such as "jakarta" or "minnesota"):
git clone -b somerelease https://github.com/edgexfoundry/edgex-compose
cd edgex-compose
make run
or
git clone -b somerelease https://github.com/edgexfoundry/edgex-compose
cd edgex-compose
make run arm64
The API gateway is not started if EdgeX is started with security
features disabled by appending no-secty
to the previous make
commands.
This disables all EdgeX security features, not just the API gateway.
Configuring API Gateway
Using a bring-your-own external TLS certificate for API gateway
The API gateway will generate a default self-signed TLS certificate that is used for external communication. Since this certificate is not trusted by client software, it is commonplace to replace this auto-generated certificate with one generated from a known certificate authority, such as an enterprise PKI, or a commercial certificate authority.
The process for obtaining a certificate is out-of-scope
for this document. For purposes of the example,
the X.509 PEM-encoded certificate is assumed to be called cert.pem
and the unencrypted PEM-encoded private key is called key.pem
.
Do not use an encrypted private key as the API gateway
will hang on startup in order to prompt for a password.
Run the following command to install a custom certificate using the assumptions above:
docker compose -p edgex -f docker-compose.yml run --rm -v `pwd`:/host:ro --entrypoint /edgex/secrets-config proxy-setup proxy tls --inCert /host/cert.pem --inKey /host/key.pem
The following command can verify the certificate installation was successful.
echo "GET /" | openssl s_client -showcerts -servername edge001.example.com -connect 127.0.0.1:8443
(where edgex001.example.com
is the hostname by which the client is externally reachable)
The TLS certificate installed in the previous step should be among the output of the openssl
command.
Configuration of Adding Microservices Routes for API Gateway
A standard set of routes are configured statically via the security-proxy-setup
microservice.
Additional routes can be added via the EDGEX_ADD_PROXY_ROUTE
environment variable.
Here is an example:
security-proxy-setup:
...
environment:
...
EDGEX_ADD_PROXY_ROUTE: "app-myservice.http://edgex-app-myservice:56789"
...
...
app-myservice:
...
container_name: app-myservice-container
hostname: edgex-app-myservice
...
The value of EDGEX_ADD_PROXY_ROUTE
takes a comma-separated list
of one or more paired additional prefix and URL
for which to create proxy routes.
The paired specification is given as the following:
<RoutePrefix>.<TargetRouteURL>
where RoutePrefix is the base path that will be created off of the root of the API gateway to route traffic to the target. This should typically be the service key that the app uses to register with the EdgeX secret store and configuration provider, as the name of the service in the docker-compose file has security implications when using delayed-start services.
TargetRouteURL is the fullly qualified URL for the target service,
like http://edgex-app-myservice:56789
as it is known on on the network on which the API gateway is running.
For Docker, the hostname should match the hostname specified in the docker-compose.yml
file.
For example, using the above docker-compose.yml
:
EDGEX_ADD_PROXY_ROUTE: "app-myservice.http://edgex-app-myservice:56789"
When a request to the API gateway is received,
such as GET https://localhost:8443/app-myservice/api/v3/ping
,
the API gateway will reissue the request as
GET http://edgex-app-myservice:56789/api/v3/ping
.
Note that the route prefix is stripped
from the re-issued request.
Using API Gateway
Resource Mapping between EdgeX Microservice and API gateway
If the EdgeX API gateway is not in use, a client can access and use any REST API provided by the EdgeX microservices by sending an HTTP request to the service endpoint. E.g., a client can consume the ping endpoint of the Core Data microservice with curl command like this:
curl http://<core-data-microservice-ip>:59880/api/v3/ping
Where <core-data-microservice-ip>
is the Docker IP address of
the container running the core-data microservice (if using Docker),
or additionally localhost
in the default configuration for Docker.
This means that in the default configuration,
EdgeX microservices are only accessible to local host processes.
The API gateway serves as single external endpoint for all the REST APIs. The curl command to ping the endpoint of the same Core Data service, as shown above, needs to change to:
curl https://<api-gateway-host>:8443/core-data/api/v3/ping
Comparing these two curl commands you may notice several differences.
http
is switched tohttps
as we enable the SSL/TLS for secure communication. This applies to any client side request. (If the certificate is not trusted, the-k
option tocurl
may also be required.)- The EdgeX microservice IP address where the request is sent changed to the host/IP address of API gateway service (recall the API gateway becomes the single entry point for all the EdgeX micro services). The API gateway will eventually relay the request to the Core Data service if the client is authorized.
- The port of the request is switched from 59880 to 8443, which is the default SSL/TLS port for API gateway (versus the micro service port). This applies to any client side request.
- The
/core-data/
path in the URL is used to identify which EdgeX micro service the request is routed to. As each EdgeX micro service has a dedicated service port open that accepts incoming requests, there is a mapping table kept by the API gateway that maps paths to micro service ports. A partial listing of the map between ports and URL paths is shown in the table below.
Note that any such request issued will be met with an
401 Not Authorized
response to the lack of an authentication token on the request. Authentication will be explained later.
The EdgeX documentation maintains an up-to-date list of default service ports.
Microservice Host Name | Port number | Partial URL |
---|---|---|
edgex-core-data | 59880 | core-data |
edgex-core-metadata | 59881 | core-metadata |
edgex-core-command | 59882 | core-command |
edgex-support-notifications | 59860 | support-notifications |
edgex-support-scheduler | 59861 | support-scheduler |
edgex-kuiper | 59720 | rules-engine |
device-virtual | 59900 | device-virtual |
Creating Access Token for API Gateway Authentication
Authentication is more fully explained in the authentication chapter.
The authentication chapter goes into detail on:
- How to create user accounts in the EdgeX secret store
- How to authenticate to the EdgeX secret store remotely and obtain a JWT token that will be accepted by the gateway.
The TL;DR version to get an API gateway token, for development and test purposes, is
make get-token
(in the edgex-compose repository, if using Docker).
The get-token
target will return a JWT in the form
eyJ.... "." base64chars "." base64chars
As a bearer token, it has a limited lifetime for security reasons.
The get-token
process should be repeated to obtain fresh tokens periodically.
In the long form process described in the
authentication chapter,
this means re-authenticating to the EdgeX secret store and requesting a fresh JWT.
EdgeX versions prior to 3.0 used to support
registering a public key with the API gateway,
and allowing clients to self-generate their JWT
for API gateway authentication.
Regrettably, this "raw key JWT" authentication method is no longer supported.
As consolation, the EdgeX secret store backend, Hashicorp Vault,
supports many other authentication backends.
EdgeX only enables the userpass
auth engine by default,
and only passes the userpass
auth endpoints through the API gateway by default.
Customizing an EdgeX implementation to use alternative authentication methods
is left as an exercise for the adopter.
Using API Gateway to Proxy Existing EdgeX Microservices
Once the resource mapping and access token to API gateway are in place, a client can use the access token to use the protected EdgeX REST API resources behind the API gateway. Again, without the API Gateway in place, here is the sample request to hit the ping endpoint of the EdgeX Core Data microservice using curl:
curl http://<core-data-microservice-ip>:59880/api/v3/ping
With the security service and JWT authentication is enabled, the command changes to:
curl -k -H 'Authorization: Bearer <JWT>' https://myhostname:8443/core-data/api/v3/ping
In summary the difference between the two commands are listed below:
-k
tells curl to ignore certificate errors. This is for demonstration purposes. In production, a known certificate that the client trusts be installed on the proxy and this parameter omitted.- Use the https versus http protocol identifier for SSL/TLS secure communication.
- The service port 8443 is the default TLS service port of API gateway
- Use the URL path "core-data" to indicate which EdgeX microservice the request is routed to
- Use header of
-H "Authorization: Bearer <JWT>"
to pass the authentication token as part of the request.