This post was originally published on TechUK on 20 April 2021 and was featured on the publication's #Cyber2021 Week.
The microservices architecture expands the attack surface since a microservice communicates with other microservices remotely over the network. It's a common principle in security that the strength of a given system is only as strong as its weakest link. In a microservice architecture the number of links to be protected can grow from a few to thousands. Thus, microservices architecture requires stricter security controls to mitigate security risks.
Many microservices deployments today worry about the edge security by exposing the microservices via APIs and protecting those with an API gateway at the edge. Once a request passes through the API gateway, the communications assume a trusted network and no further security measures exist to secure access. Securing a single entry point is inadequate since it exposes endless possibilities for an attacker to gain access to sensitive services and data stores. Thus, a defense in depth approach with multiple security layers is the way to go for microservices.
In this blog, we focus on securing service-to-service communications that occur in microservices to mitigate security challenges associated with trust.
What is a Zero-trust Environment?
As per John Kindervag, the creator of the zero-trust model in 2010, rust is a human emotion that we have injected into the digital systems for no reason at all and individuals confuse human trust with digital trust all the time.. A zero-trust environment is built to eliminate the trust between components in a digital system that will expose them to potential security vulnerabilities. When we apply this into a microservices deployment, a microservice should eliminate the trust with other components and the underlying network it interacts with or at least try to minimize trust.
Securing Service-to-service Communications
Microservices security for service-to-service communications is about securing interactions among microservices once a request from a client application passes through the security at the edge. Within a microservices deployment, interactions between microservices can happen over HTTP or gRPC synchronously or over a messaging protocol like Kafka asynchronously.
Irrespective of the communication protocol, in a service-to-service security design, we need to focus on authentication and authorization in service-to-service interactions and passing user context among microservices in a cryptographically safe manner. This is shown in the image below.
Authentication and Authorization in Service-to-service Interactions
The most common way of implementing authentication for service-to-service interactions is to use Mutual Transport Layer Security (mTLS). Following the zero-trust principle, each microservice must validate the requests it gets at the edge of the microservice and not at the edge of the network. Each microservice must validate the certificate of the calling microservice to see whether either the certificate itself or the issuer of the certificate are trusted.
Once the identity of the caller is established, the microservice has to perform an authorization based on the authentication context and the access control policies to see whether the calling microservice is eligible to do what it wants to do. Typically, a proxy component deployed along with the microservice, which intercepts all the requests coming in and going out of a microservice, does these checks. In a service mesh deployment, for example, the envoy proxy carries out these checks.
Passing User Context Among Microservices in A Cryptographically Safe Manner
Typically, an API gateway at the edge of the microservices deployment validates the user context corresponding to the calling application. The calling application can invoke an API just by being itself, or on-behalf of another user. In either case, the API gateway has to pass the user context to the upstream microservices in a cryptographically safe manner.
The best way to carry the user context in a cryptographically safe manner in a microservices deployment is to use a JSON Web Token (JWT). Each microservice at its edge will validate the JWT to make sure it is issued from an issuer it trusts. When one microservice talks to another microservice, the calling microservice can pass the same JWT or exchange it to a new JWT by talking to a security token service that the recipient microservice trusts, and passes the new JWT.
The recipient microservice can authorize the end user based on the claims the JWT carries. As in the case of service-to-service authentication/authorization, a proxy component deployed along with the microservice, which intercepts all the requests coming in and going out of a microservice, does these checks.
The technology around securing service-to-service communications following zero-trust security principles have evolved in the last few years. Kubernetes has become the de-facto deployment for microservices and service mesh implementations like Istio and Linkerd have increased their support for securing service-to-service communications with mTLS and JWT. Additionally, open-source projects like SPIFFE and OPA are becoming mainstream to address key concerns in securing microservices.
Prabath Siriwardena is an identity evangelist, an author, a blogger, and the Deputy CTO (Security) at WSO2 with more than 13 years of industry experience in designing and building critical Identity and Access Management infrastructure for global enterprises, including many Fortune 100/500 companies.
As a technology evangelist, Prabath has published eight books, including Microservices Security in Action (Manning), OpenID Connect in Action (Manning), Advanced API Security (Apress) and Microservices for the Enterprise (Apress). He blogs on various topics from blockchain, PSD2, GDPR, IAM to microservices security. He also runs a YouTube channel.