Microservices architecture has become the default way of building modern applications. But to get the most benefits from microservices, you need to have a clear understanding of the dark side of microservices. Good microservices design is not only about writing your application in small pieces instead of one monolith. The great benefits of microservices come at a price. That price is more complicated networking and more effort into securing your microservices. In this post, you’ll learn about the most important microservices security principles and some best practices.
Traditional Security in a World of Microservices?
So, you may have a few questions in mind. Why do microservices require different security approaches? What’s wrong with traditional security tools and patterns? Why can’t you just reuse what you already have for microservices? I’m glad you asked because there’s a fundamental difference between securing a monolith and microservices.
You see, when you have a monolith application, security is always something that’s separated. Your developers should try to write secure code. But in reality, all the security (usually for both infrastructure and application) is happening in a separated, dedicated security team. This also applies to the architecture of the application itself. The application usually needs to validate the identity of the clients and their own components, the database should trust only one source, etc. And traditionally, that validation and access control are happening only once at the beginning of the application life cycle. From then, the application trusts its peers.
And if you try to apply that model to microservices, you’ll end up in a very vulnerable environment. Microservices change often. The container orchestrator tool may restart them at any time. They also scale on demand and are easily and independently upgradable. And for all these reasons, you can’t establish trust between your microservices only once. Client validation and access control should be a continuous process in a microservices-based environment.
Another very important aspect of microservices security is monitoring and observability. Since you may have dozens or even hundreds of microservices, it’s really important to have a good overview of what’s happening in your clusters. When running on the Kubernetes cluster, you’ll see many pods that are part of the Kubernetes itself or are deployed by your cloud provider.
All of this means that it’s very easy to miss malicious pods on your cluster. Good and continuous security analysis of your running pods and cluster should be proactive and provide you with the important information up front.
Here are some best practices to keep in mind when it comes to microservices security.
In a traditional monolithic application, you usually restrict access to the database by specifying the allowed IP address. If you were to translate that to the microservices, it would mean that all microservices would have access to the database. But in reality, probably only a few of them should be able to talk to that database. The same applies to other parts of your application. If you have specific microservices that handle very sensitive data, you should make sure that access to these microservices is restricted.
If you have a large number of back-end microservices, it’s also good to consider using an API gateway. This is a concept where you have a proxy service that sits between all your API consumers (either clients or other microservices) and works as a reverse proxy for all your back-end services. From a security perspective, it’s the same concept as, for example, having a single SSH bastion host exposed to the internet instead of each virtual machine on its own.
The point is, of course, to reduce attack vectors. It’s way easier to make sure that one API gateway is secure instead of dozens of back-end services separately. Imagine that you are onboarding a new API microservice for your application from a new team. And, for whatever reason, that team is not fully aware of all security checks they should perform. With an API gateway, this wouldn’t be a problem because the API gateway itself is secured.
Layer-7 Container-Aware Security
It’s no surprise that traditional IP:PORT:PROTO-based firewalls are not really useful in a containerized environment. Containers, and so their IP addresses, are ephemeral. Moreover, in most environments, their IP addresses are NATed to the underlying host machines. So, on the firewall, you’d have to allow or decline the IP address of the node. But on that node, you could have more than 50 containers. This gives very little security.
With microservices, you should invest in some sort of Layer-7 firewall. Or even better, use cloud-native solutions that also understand Kubernetes and containers. With Cilium, for example, you can apply very detailed policies like “container A can only send HTTP GET request to containers B and C on endpoint/orders.”
Kubernetes itself also comes with a simple pod and/or namespace-based network policies. They are easy to implement and are much more efficient than a simple old-school firewall. You can, for example, deploy a database into a specific database namespace and only allow access to that namespace to specific back-end pods.
I mentioned the importance of trust management at the beginning of this post. But let’s take it one step further. The ultimate security posture of microservices-based environments should be simple: trust no one. Validate each request. And this applies to both users accessing your clusters and communication between microservices. You see, there is no point in spending thousands of dollars on the code security of your application if anyone has admin access to the cluster.
Remember that these days, hackers don’t try to hack your application in the same way as they used to in the monolith world. Now they simply search for the weakest point of your infrastructure. And usually, they find access to the cluster through some forgotten, less important microservice or via other, not properly secured cloud services. Once in the network, your trusted approach that was common for monolith applications does not work for microservices. Of course, this gives you a little bit more work, but hey, it’s worth it.
Microservices are really interesting. On the one hand, they bring many benefits and more flexibility to developers. On the other, microservices also force developers to think more about security. It’s no longer a single task at the end of the development cycle. It’s no longer mainly a security team job.
In traditional software development, developers should also try to write secure code from the beginning. If they don’t, the security team will try to secure everything around them. With microservices, it doesn’t work that way. To secure microservices, you need to embed security into your architecture. Trust is no longer something granted. But also, a lot of security has shifted from the actual application code to infrastructure.
Of course, it doesn’t mean that you can pay less attention to your code. But hackers sometimes don’t even need to hack your application to achieve what they came for. Even without properly secured cluster access, they can find other methods of stealing your data. That said, you can drastically decrease the chances of getting hacked with things like zero-trust security and network isolation. If you want to learn more about microservices and security, check out our other posts. And if you want to start with securing your API, take a look at our API Security Solution.
This post was written by Dawid Ziolkowski. Dawid has 10 years of experience as a Network/System Engineer at the beginning, DevOps in between, Cloud Native Engineer recently. He’s worked for an IT outsourcing company, a research institute, telco, a hosting company, and a consultancy company, so he’s gathered a lot of knowledge from different perspectives. Nowadays he’s helping companies move to cloud and/or redesign their infrastructure for a more Cloud Native approach.