The ratio of development engineers to operations and security engineers in a typical technology organisation is 100:10:1.
This minimal presence of security engineers means that, without automation and the integration of information security into the daily work of Dev and Ops, Infosec can only perform some compliance checking. This is the opposite of security engineering.
A key aspect of DevSecOps is that security engineers build security decisions into the development lifecycle. By implementing security in applications from the beginning rather than applying it when issues arise, the team catches security vulnerabilities during the development process instead of finding problems after the app release. This explains why it is so important to implement as many security decisions as possible at the beginning of every project and every architecture.
In this post, we will explore how your Azure infrastructure and DevOps flow can be made more secure.
Setting up a secure cloud environment is not easy, and many things can go wrong. That is why all security features need to be implemented at the beginning of every project and why we need to put them in the architecture diagram.
The architecture diagram below shows a combination of several Azure resources supported by secure endpoints that are marked with an info flag.
Our backend application is running on Kubernetes, and we have a web app that is deployed as a static website on Azure Blob Storage.
The mobile app and the web app are publicly accessible, and the communication with the backend application is happening over an API Management gateway.
All users are controlled by the Azure AD B2C service.
The data is saved in Azure Postgres DB Server, and all services are monitored by the Azure Monitoring service.
On the other side, we are using the Azure DevOps service for our code repository and for the CI/CD flow:
This diagram contains several info symbols. All of them are related to security features described in this blog post:
This is probably the easiest advice to implement, but many developers overlook it.
We can avoid a data breach and sensitive information exposure by using git pre-commit hooks to prevent developers from leaking passwords and secrets when they commit and push code to a repository. Pre-commit checks are used to find common security issues before changes are committed into source code repositories.
Secrets in the working directory, such as the .env file, may need to be ignored to prevent them being committed to a repo or package registry. We need to control what we are going to push on the code repository. Here is an interesting repository with a list of useful hooks that can be implemented in your code.
We all know that the development teams put effort into reviewing Pull Requests. Furthermore, many automated stages in the pipelines are dedicated to checking the code. Sometimes those checks are time-consuming for the reviewers, which is why it is advisable to split the checks and make some of them run locally. Here is a good example of private keys detection and of how this is done with a Python script, but the same thing can be done directly in your hook directory with Bash code because it is the default hooks language.
Security implementation in Continuous Delivery is really important. The two most popular methods for security testing in a pipeline are:
SAST is a white-box testing methodology. White-box testing evaluates a range of static inputs, such as documentation and application source code.
During the scanning process, SAST refers to a predefined set of rules to detect vulnerabilities. The scanning process can be easily integrated into the CI/CD pipeline, and the SAST tools are activated once the code is pushed to a source code repository.
In contrast to SAST, DAST uses a black-box approach and assumes that testers have no knowledge of the inner workings of the software being tested. Black-box testing needs to be dynamic. This dynamic approach allows DAST to detect a wide range of real work vulnerabilities, including memory leaks, SQL injection and XSS attacks. Snyk is a popular tool that can be integrated into your Azure DevOps pipeline. It offers both free and paid plans and enables security across the Microsoft Azure ecosystem, including Azure Pipelines.
Snyk offers a set of application security tools, including open source vulnerability scanner, that help to automatically find, prioritise and fix vulnerabilities in the open source dependencies throughout your development process. Snyk automatically finds and fixes application and container vulnerabilities. Learn more about about implementing Snyk in your Azure Pipeline .
Azure role-based access control (Azure RBAC) provides access management to the Azure resources. Azure RBAC helps you manage access for your team members. It determines who has access to which Azure resources, what they can do with those resources and what scopes they have access to.
With Azure RBAC, you can enable different roles within your team, and you can also grant users the minimum access required to do their jobs.
Role is a collection of actions that the assigned user or application identity can perform. We can create different roles for a specific User, Group or Service Principal, and those roles can be assigned to different resources. Role assignment is a combination of the role definition, service principal and scope.
Another RBAC suggestion is to deny people access to Production. Only CI/CD should be able to make changes there.
Built-in roles are fixed, with a predefined set of permissions. These role definitions cannot be updated. Azure AD supports many built-in roles o round off the edges and create something specifically for your requirements. Azure AD also supports custom roles.
In Azure Active Directory > Roles and administrators > New custom role. Select a permission for your custom role, and it will be ready for assignment.
Whereas RBAC focuses on the user actions, security policies focus on the resource properties. Policy definitions describe resource compliance conditions and the action to take if a condition is met. A condition compares a resource property field or a value to a required value.
Here are two important facts about the policy definitions:
Based on the architecture that we showed in the diagram, we will add a policy definition for our Kubernetes cluster from the Azure Portal. To do this, go to Policy and then to Definitions .
Let’s search for Container Registry in the category field.
Disabling public network access improves security by ensuring that container registries are not exposed on the public internet. Creating private endpoints can limit exposure of container registry resources. Select the following policy from the listed definitions: Public network access should be disabled for Container registries. Click on the three dots to the right and select Assign .
The following screen will be displayed:
In the basic setup, simply select the Scope where this certain policy will be assigned. From the Scope dropdown, select your Resource Group where the Container Registry is launched.
The Azure Policies do not check the user permission. It assumes the user already has write permissions.
Azure Virtual Networks are similar to the Local Area Networks on your on-premises network.
The idea behind an Azure virtual network is that you create a network based on a single private IP address space, on which you can place all your Azure virtual machines. The private IP address spaces are available in the Class A (10.0.0.0/8), Class B (172.16.0.0/12) and Class C (192.168.0.0/16) ranges.
Subnets and VNets are not changeable and require resource recreation. Each subnet must have a unique address range within the address space of the virtual network.
The address range cannot overlap with other subnets in the virtual network. There are limits to the number of network interfaces and private IP addresses that you can have within a virtual network.
In our scenario, we have 2 different Class A subnets in the same virtual network. The first one is dedicated to the Kubernetes cluster and to the Postgres DB server. The second one is dedicated to API management. Cross-subnet communication is allowed and the API management is able to communicate with the ClusterIP type services in the Kubernetes cluster.
We already know that our first subnet will be used for communication between the Kubernetes cluster and the Postgres DB server, so we need to select a service endpoint.
Virtual Network (VNet) service endpoint provides secure and direct connectivity to Azure services over an optimised route via the Azure backbone network. Endpoints allow you to secure your critical Azure resources within your virtual networks only. Service endpoints enable private IP addresses in the VNet to reach the endpoint of an Azure service without needing a public IP address on the VNet.
Azure PostgreSQL leverages Azure storage encryption to encrypt data at-rest by default using managed keys. Data encryption with customer-managed keys for Azure PostgreSQL Database Server is configured at the server-level for a given server, where a customer-managed key called the key-encryption key (KEK) is used to encrypt the data encryption key (DEK) used by the service. The KEK is an asymmetric key stored in a customer-owned and customer-managed Azure Key Vault instance.
When the Database Server is configured to use a customer-managed key stored in the Azure Key Vault, the server sends the DEK to the Key Vault for encryption. In the encryption process, an encryption key (KEK) is used to encrypt the DEK. Use of a Key Encryption Key that never leaves Key Vault allows the data encryption keys themselves to be encrypted and controlled.
Key Vault returns the encrypted DEK, which is stored in the user database. For decryption, the server sends the protected DEK to the Key Vault, and then the value is used on the Database Server side. In the following image, you can see visually how the data encryption works on a PostgreSQL Database Server.
We already have a Key Vault deployed on Azure, so we can use the same one for enabling the data encryption on our PostgreSQL database server. Several requirements need to be satisfied for setting up a key that will protect the data on our PostgreSQL DB server such as:
Azure Security Center is a unified infrastructure security management system that strengthens the security posture of your data centres and provides advanced threat protection across your hybrid workloads in the cloud. You can use Azure Security Center for threat protection.
Azure Security Center focuses on three security challenges:
Security Center can be involved in the functionality of different services. In our case, we will scan for potential vulnerabilities in the images that we push to the Azure Container Registry (ACR).
Security Center pulls the image from the registry and runs it in an isolated sandbox with the Qualys scanner. The scanner extracts a list of known vulnerabilities.
To enable the Security Center for ACR, we need to enable the plan for Container Registries in the Security Center:
Azure Defender for container registries includes a vulnerability scanner to scan the images in your ACR and provide deeper visibility into your image’s vulnerabilities. The integrated scanner is powered by Qualys, the industry-leading vulnerability scanning vendor.
DevSecOps is an approach for integrating security practices within the DevOps process, which ensures the application is less vulnerable and ready for production use. While selecting the right security tools for the CI/CD process is important, organisations also need security teams to meet the required security standards. Security is easier to implement if the requirements are defined early , not as an after-thought. The requirements should be part of the architecture diagram and be included in your project estimates.
DevSecOps is well supported by Azure. The key role that enables this support is the way the Security Center controls the deployed resources. Azure suggests adding as many security solutions as possible in those resources.