When it come to securing microservices, there are several aspects to consider:
- Securing the Development Lifecycle and Automated Testing: The key benefit of using a microservice architecture is the speed to production. We can change a service, test it and rapidly deploy it to production. To ensure we don’t introduce security vulnerabilities in the code, we need to utilise static code analysis and dynamic testing. These tests should be part of the continuous delivery (CD) process. Any vulnerabilities must identified early in the development lifecycle and have shorter feedback cycles.
- DevOps security: There are multiple microservices deployment patterns — but the most commonly used one is service-per-host model. The host does not necessarily mean a physical machine — many deployments use a container of some type (Azure App Service/Docker). So we need container-level security in place. We need a plan for how to isolate a container from other containers and what level of isolation we have between the container and the host operating system.
- Application level security: How we authenticate and control users access to microservices, and how we secure the communication channels between microservices.
Security becomes challenging in a microservices environment, where the services are scoped and deployed in multiple containers, in distributed hosts. The service interactions are not local, but remote, largely over HTTP.
API Gateways and Access Control
The two main techniques used to secure services is to limit access to the services via a new service layer which aggregates the services and provides APIs that are tailored to each client, and use of Role Based Access Control via an Identity service which passes signed, encrypted tokens to downstream services.
The aggregator service layer is also known as an API Gateway, and it is a common solution to the problem of controlling access to services.
All requests from clients first go through the API Gateway. It then routes requests to the appropriate microservice.
A typical API Gateway includes:
- Security (authentication and potentially authorisation)
- Managing access quotas and throttling
- API presentation
- Routing to “internal” APIs
- API health monitoring
- API performance monitoring
- API Versioning
API Gateway Advantages
- Implemented in a single place
- Simplifies the API source code itself, since these concerns are externalised
- Provides a centralised view of the API and therefore helps implement a consistent access policy
API Gateway Drawbacks
- Single point of failure
- Performance bottleneck
- Risk of complexity as all the API methods are in one place
- Risk of lock-in; migration may not be easy
Role Based Access Control
The challenge for RBAC become how we authenticate the user and pass the login context between microservices, in a symmetric manner, and then how each microservice authorises the user for specific functionality.
The most common accepted way to do this is via JSON Web Tokens (JWT). A JWT is an encypted container which transports User data between interested parties. It allows us to:
- Pass identity between interested parties.
- Pass user entitlements between interested parties.
- Transfer Role Based data securely between interested parties over an unsecured channel.
A signed JWT is known as a JWS (JSON Web Signature) and an encrypted JWT is known as a JWE (JSON Web Encryption). In fact a JWT does not exist itself — either it has to be a JWS or a JWE. It’s like an abstract class — the JWS and JWE are the concrete implementations.
The user context from one microservice can be passed to another with a JWT. Since the JWT is signed by a key known to the upstream microservice, the token will carry both the end user identity (as claims in the JWT) and the identity of the upstream microservice (via the signature). To accept the JWT, the downstream microservices first need to validate the signature of the JWT against the public key embedded in the token itself.
Cost of JWT Validation
Each microservice has to bear the cost of JWT validation, which includes a cryptographic operation to validate the token signature. Caching the JWT at the microservices level against the data extracted out of it reduces the impact of repetitive token validation. The cache expiration time must match the JWT token expiration time. The impact of caching is low if the JWT expiration time is low.
We have looked at how API Gateways can be of benefit to a microservices architecture, and how Access Control can be implemented using tokens. In a follow up to this article we will look at the use of JWT in .NET Core in more detail with code samples