Imagine you're going through immigration at the airport. The immigration officer says, "I don't need your passport because I trust that you are who you claim to be."
Wait, what? That would never happen, right? That's because trust is exploitable. Sooner or later, somebody will try to lie about who they are, and thus a criminal could enter the country. That’s why countries must enforce some form of identity, like a passport, to certify travelers are who they claim. By checking your passport, the immigration officer can quickly determine your true identity.
Similarly, your services should have a virtual passport to certify that they are who they claim to be.
In this post, and the below recording from our recent Destination: Zero-Trust virtual event, I'll go over the importance of zero-trust security for modern applications and how a service mesh, like Kuma or Kong Mesh, can enforce zero-trust security in one click.
Zero-trust security becomes even more critical as you transition to a microservice architecture. Microservices can be a chance to make your systems more secure than they were with monolithic applications. This may sound counterintuitive, but keep reading, and I'll explain.
As you increase the number of services across your organization, you'll want to be able to scale and deploy more efficiently. That way, you'll be able to ship features and back fixes to customers every day. Over time, you'll become more distributed and decoupled to become highly available. As you decouple into microservices, you'll need to add more service connectivity.
Trust Is Exploitable
You cannot rely on the concept of trust to enforce security across so many different services. Trust can be taken advantage of at companies with a high number of services running across the organization. The more services your company makes, the less you can rely on the concept of trust. To be completely secure, you must remove trust from your systems entirely.
Marketplace App Stores
Let's dive further into microservices vs. monolith security with a more practical example. A marketplace app, like Amazon or eBay, allows users to search for and then purchase items.
Monolith: Trust Allowed
If this app is a monolith, every resource you create—users, items, invoices, etc.—is accessible to any other object within the same code base. Typically, the monolith app encapsulates all of these resources into objects.
Object-oriented programming exposes initializers and functions. This means any other object can change the state of these resources. There are ways that you can restrict access to resources. Think of public, private or protected access modifiers or package-level visibility. It’s still not going to be very secure. Everything that you do in a monolith is going to be one comment away from being disruptive. Systems can work around those limitations in a large codebase, where many people work on the same application. This is not a model that you can trust to enforce strict security.
Microservices: Trust Still Allowed
Instead of having every resource in the same code base, microservices decouple resources away from the large monolith into separate services. Now, each of these services is exposing an API. It can be a RESTful API, gRPC or anything that the network can consume to allow resources to change another service's state. Microservices architecture replaces the monolith function calls with calls between all the services.
By default, this doesn’t make your security situation much better. Anything can still make a network request to the API of another service and then change the state of its resources. Without there being any security in place, the problem still exists. It's the same situation as the monolith. The exception is that the security problem runs over the network instead of inside the same codebase. The gotcha here is that you can adopt software to operate over the network layer to block or restrict access to particular operations. These requests are now going over the network.
Microservices Need Virtual Passports
To determine the security rules among your services, you must do two things:
Enforce those permissions
Back to the marketplace application example - you may want to allow the invoice service to consume the item service but not vice versa. Your services or anything on your requests' execution path can make these checks themselves, but it has to happen somewhere. You need to have a reliable way to identify the identity of the service making the request. You must have that virtual passport that identifies all the services in your infrastructure.
Identity is essential in any microservices-based application and any contemporary architecture. Without identity, there is no security. You would be reintroducing the concept of trust, which is exploitable. To implement zero-trust, you must assign an identity to identify every service instance used for every request that happens within your systems. This identity will become that virtual passport that you need for the request, confirming that the originating service is what it claims to be.
How to Implement Zero-Trust Security
To implement zero-trust, you can use a mutual TLS to assign the identifier that provides both the identity and the encryption to every request your services are making. For example, when you go to Google, you know that the identity is being certified by the certificate associated with the domain. In the case of microservices, you can use subject alternative names (SANs) to associate a name to any certificate assigned to any outgoing requests from every service.
SANs can identify the service name so that when you implement the checks on the receiving end of that request, you can determine that the service name matches the service name in the certificate. If it doesn't, it won't be allowed to execute these sets of permissions. It sounds complicated, but in reality, there will be tooling in patterns, like service mesh, that can help us do all of this in one click. Once you have the service name, you can finally perform the checks you want to apply. A lot of infrastructure goes into this because you must create those certificates, associate the SANs to each certificate, make the permissions and check those permissions. You must be able to reliably check this across all the technologies, platforms and programming languages used by your app teams.
One way to build this would be to generate a library that your teams can leverage to create the identity every time they request another service. However, that would depend on the programming language and the framework that the application teams are using. Plus, service connectivity isn't their job.
The application teams' job is to build the microservices application themselves, not secure the network or the infrastructure.
Architects should provide the application teams with the underlying infrastructure that will generate those certificates out of the box, assign those identities and verify the permissions across each service communication.
Service Mesh Simplifies Zero-Trust Security
Service mesh allows infrastructure architects to provide all of this as a service to the application teams. They just make the requests across their services. By default, those requests will be secure, encrypted and enforced with the traffic permission checks you put in place.
Kuma Service Mesh
Kong built an open source service mesh technology called Kuma that we have donated to the CNCF foundation. Kuma is a neutral technology that you can use to create a service mesh that, among many things, can also enforce zero-trust security in one click. Kuma also has observability and routing components to it. It’s a full service mesh.
Kuma leverages the Envoy proxy to enforce the identities and permissions that you set up in the control plane. Kuma effectively is a control plane that implements the XDS API of Envoy. The data plane proxies can run alongside your services on Kubernetes deployments, virtual machines or hybrid. The Kuma control plane allows you to determine how you want certificates to be issued and rotated and how permission checks will be set up and enforced.
Kuma is one service mesh that you can deploy once across any zone, cloud or cluster, including a Kubernetes cluster. Then, on top of this one service mesh implementation, you can create as many virtual service meshes as you want—each backed by its certificate authority.
Kuma uses the certificate authority to generate the data plane proxy certificates for each service's replica so that every request you're making within the mesh is secure. The data plane proxy runs on top of Envoy and creates the virtual passport. It’s orchestrated by the control plane that connects all of the data plane proxies running on top of Envoy. This entire lifecycle of generating the certificates and replacing them, and even the provisioning of the certificate authority itself, can be automated.
With Kuma, you don’t have to worry about implementing zero-trust security.
Kuma does that for you by supporting the multi-zone deployment. That way, you can propagate the service mesh authorization policies and security configuration across each zone that you want to support and make it part of the service mesh by adding the global and remote control plane separation. It automatically connects the services from one zone to another, from one cloud to another and from one region to another, without you having to worry about service discovery in the first place. Every time there is a service that belongs to Kuma in any zone, that service, if zero-trust is enabled, will have that certificate that allows us to create an encrypted infrastructure with identities in place for us to enforce permission checks on every request.
Among the policies you have, there are a few security policies like mutual TLS and traffic permissions. These are the two policies that you're going to be using to enforce zero-trust security across your systems. With mTLS, you can:
Update mesh objects with the mutual policy
Determine how you want to generate the data plane proxy certificates
Determine how often you should rotate certificates
That's all you need to generate a built-in certificate authority that will provision the root certificate and key. You'll then use this to automatically issue certificates to each data plane proxy you have running across any zone or environment. Kuma will be in charge of rotating those data plane proxy certificates every day using the rotation property in this example.
It doesn’t matter if you have one data plane proxy or a thousand data plane proxies. Kuma will take it from there and automatically issue and rotate those certificates.
If you don't want Kuma to generate the certificate authority by itself, you can provide the system with your existing certificate authority. You can then apply traffic permissions that would allow you to determine what you want to enforce and which services will consume other services.
Here's an example of a traffic permission policy that allows us to execute all traffic control across any service to any service. Now, of course, you can restrict that in such a way that only allows specific traffic paths. Doing so is very powerful because all you need to do is install the mesh, enable mTLS and create the traffic permission. And just like that, you have zero-trust security implemented across the board.
With mTLS and traffic permissions, you can create those simple rules to determine what is allowed to consume other services. With OPA, you can also decide what operations within those services to apply in a more fine-grained way. You can access all of this on a native interface that runs on Kubernetes and virtual machines.