Containers have made microservices popular among developers, and now many companies are embracing them due to advantages like fast deployment and increased independence of services. However, migrating from a monolithic architecture to a microservices architecture raises many issues, among which security concerns are the most important.
Traditional security tools used for monolithic applications aren’t acceptable for securing microservices and containers. Applications based on microservice architectures contain thousands of containers, significantly enlarging the attack surface. Treating containers as a unit of service greatly reduces the transparency and auditability of applications.
These security issues raise heated debates over whether this new approach to software development actually solves more problems than it creates. So how can we introduce security to our applications without losing the benefits of a microservice approach?
In this article, we observe the security challenges and provide a list of industry best practices for ensuring security in microservices and containers. This article will be useful for both microservice practitioners and those who are new to this technique.
The National Institute of Standards and Technology (NIST) defines microservices as loosely coupled self-contained services that appear as the result of the architectural decomposition of an application’s components. These services can communicate with each other using a standard communication protocol and a set of well-defined APIs. This definition implies that developers need to decompose application services into separate units that can connect to each other and be deployed in any infrastructure. To achieve a microservice deployment on any device, developers put microservices into containers.
Containers are convenient for packaging microservices and securely running an application within a virtualized environment. Containers are isolated from the host operating system and only share its resources to run the application.
Security concerns regarding microservices typically stem from the following:
- Many moving parts
Microservice-based applications are more complex than monolithic applications as they consist of many moving parts. One application can include hundreds of microservices that are deployed in thousands of containers. For developers, this means that monolithic code with 1,000 DLLs should be decomposed into the same number of microservices. And while that makes the code more secure and maintainable, it also makes the microservice-based application much more vulnerable to cyber attacks.
- Communication risks
Additionally, the interface-driven approach to development requires well-defined REST APIs to ensure that microservices can establish consistent communication with each other. In contrast to monolithic applications where components communicate internally, the components of microservice-based applications communicate in both external and internal environments, which creates speed and security challenges. Developers have to be much more conscious of security, as they have to ensure the protection of many more things than in monolithic applications, guarantee the security of communications, and protect a much larger attack surface.
As for container-related security challenges, there’s a wide range of issues as security should be maintained across all operations.
- Vulnerabilities of container technology
The core components of container technology — containers, images, registers, orchestrators, and the host operating system — can also be targeted by cyber criminals. For instance, attackers can compromise an image and get access to application or data files. Moreover, hackers can infect a container with malicious code and use it to attack other containers, the host operating system, or other hosts.
- More people have access to code
Though DevSecOps is intended to break down the barriers between teams and ensure continuous integration and continuous deployment (CI/CD), it also leads to an increased risk that someone can alter code in the distributed working environment.
When choosing appropriate security measures, remember that they shouldn’t counter the advantages that make this approach so attractive to developers: lightweight services, simplified code building and packaging, instant startup abilities, and agile scaling on demand. Security measures are likely to be ignored or rejected if they make the approach less useful.
Our list of best practices for microservices and containers security is based on the expertise of the Apriorit team, security recommendations provided by leading microservices practitioners, and official industry standards, which are reflected in the following documents:
- NIST Special Publication 800-180, NIST Definition of Microservices, Application Containers and System Virtual Machines
- NIST Special Publication 800-190, Application Container Security Guide
- NISTIR 8176, Security Assurance Requirements for Linux Application Container Deployments
- DWP Security Policies and Standards, Security Standard - Microservices Architecture (SS-028)
Now let’s look closer at how you can secure microservices deployed in containers.
Developers tend to leave shell access to images so they can fix them in production. However, attackers often exploit this access to inject malicious code. To avoid this, create immutable containers. In case of any defects or vulnerabilities, developers can rebuild and redeploy containers. Remote management is done through runtime APIs or by creating remote shell sessions to the host on which the microservices are running. The immutable nature of containers also affects data persistence. Developers should store data outside containers so that when some of them are replaced all data is still available to their new versions.
There are many open source packages for developers with readily available containers, including Node.js., Apache Web Server, and the Linux operating system. However, for security purposes, you need to know where containers originate, when they were updated, and if they’re free of any known vulnerabilities and malicious code. It’s best to establish a trusted image repository and run images only from that trusted source.
In addition, developers should check application signatures in their scripts before putting containers into production. If you run across multiple cloud environments, it’s acceptable to have several image repositories. If you want to use images from other sources, it’s recommended to scan their contents with scanning tools.
Registries like Docker Hub, Amazon EC2 Container Registry, and Quay Container Registry help developers store and manage images they’ve created. These registries can also be used to provide role-based access controls, accept containers only from trusted sources, constantly update the list of known vulnerabilities, and flag vulnerable images.
Role-based access controls are important, as you need to control who can introduce changes into containers. It’s better to limit access to specific administrative accounts: one with responsibility for system administration and one for operating and orchestrating containers.
In addition, you should ensure that your registry verifies the signature of each container and accepts only those that come from trusted sources. Your registry should also include features that help you constantly check image content for known vulnerabilities and inform about security issues.
A microservice-based application can be deployed either on the same host or across multiple hosts. So this deployment model leads to management complexities. Consider which containers should be deployed to which hosts, which containers need access to each other, and how to automatically scale software capacities. A best practice is to group containers of a certain microservice on a single host operating system kernel. This approach provides additional defense in depth, so attackers face difficulties in compromising different groups. Automate this process if you have larger environments with many hosts.
While most recommendations refer to the security of microservices and containers, it’s also necessary to ensure the security of the host operating system. First of all, NIST recommends using container-specific host operating systems, as they’re free of unnecessary functionality and thus have a much smaller attack surface than general-purpose hosts. It’s also preferable to use a platform that allows controlling egress traffic with a router or firewall.
Secondly, CIS Docker Benchmark can offer a list of checks that provide you with a good baseline on how to harden your system. Most probably, it will recommend that you do the following:
- Establish user authentication
- Set access roles
- Specify permissions for binary file access
- Enable logging of audit data
In order to avoid data theft, limit container access to underlying operating system resources and isolate containers from each other. A best practice is to run the container engine in kernel mode while running containers in user mode. In addition, Linux provides multiple layers of security that limit the capabilities of containers. Security in Linux can be achieved by using kernel security features and modules such as Linux namespaces, Seccomp, Cgroups, SELinux, and Linux capabilities.
This approach is one of the most important principles of microservices security as it creates multiple layers of security to prevent attacks. It includes such security measures as filtering communication flows, authenticating and authorizing access to microservices, and using encryption technologies. Securing your internal environment from any external connections is the first layer of defense. If you use images from a public repository, this may pose a security risk to your application. Administer the host via a known private network so there will be no public attack surface.
There are various tools that can automatically test containers during the build or CI processes. For instance, HP Fortify and IBM AppScan offer dynamic and static application security testing. You can also use scanners like JFog Xray and Black Duck Hub to check containers for known vulnerabilities in real time. These tools flag builds with discovered issues and allow you to take appropriate action.
When we deal with Docker, we use Docker Security Scanner or other specially designed tools to detect any potential threats to our application. While security scans find malware and known vulnerabilities, monitoring tools detect issues that you aren’t expecting. Monitoring tools first collect events and then examine them against security policies. A deterministic policy can define what services can be run and which containers are allowed to make external HTTP requests. A dynamic policy can create a baseline of normal communication activities and notify about traffic spikes or unusual traffic flows.
Microservice orchestration can be implemented in two ways:
- By using the API gateway as an orchestration layer
- By coding orchestration as a separate microservice
Orchestrators pull images from registries, deploy those images to containers, and manage their running. The abstraction provided by an orchestrator allows you to specify how many containers are necessary to run a given image and what host resources need to be allocated to them.
Using an orchestration manager, you can not only automate the deployment of microservices but also ensure a certain level of security. For instance, orchestrators allow you to manage clusters of containers, segregate workloads, limit access to metadata, and collect logs. Many orchestration managers also have built-in secret management tools that allow developers to securely store and share secret data like API and SSL certificates, encryption keys, identity tokens, and passwords. There are many orchestration managers such as Kubernetes, Swarm, and Mesos as well as cloud-native management systems offered as part of Azure, GCP, and AWS.
An API is a key to applications that consist of microservices. Software based on this technology has multiple independent API services that require additional tools to manage. Thus, API access control that ensures secure authentication and authorization is crucial for microservice security. An OAuth/OAuth2 server is commonly used by developers and administrators to obtain tokens for authentication with the API. For security reasons, you should also ensure that all client-server communications are encrypted in transit with transport layer security (TLS).
The shift to microservices allows developers to improve their applications and infrastructure. However, these technologies require a radically different approach to security. A comprehensive security program for microservice-based software should address the entire application lifecycle. Using the described best practices, you can ensure the secure development and deployment of containers and microservices. Apriorit has expertise in cloud computing and cybersecurity projects, so we would be glad to assist you with any security issues.