In the world of DevOps, cloud providers offer services for developers to provision the infrastructure they need for their applications. DevOps teams become also the owner of any security concern that they may face. You would want to avoid cybersecurity breaches as much as possible to keep your business up and running. One of the core principles to achieve this is security by design. It aims to tackle security issues early on in the Software Development LifeCycle. The least privilege principle is one of the key aspects of security by design. In this article we will make it concrete: apply the least privilege principle for your cloud resources.
The foundation of the least privilege principle is to apply as few permissions as possible for a user or service to execute the tasks it needs. Compare it with a firewall: don’t allow all traffic and block specific ports and use so called “blacklists” to reject traffic. Instead, close down everything and open up only the ports and services which are really needed.
By dosing so you reduce the risks of a service to become compromised. Furthermore, it also reduces the blast radius significantly if things do go wrong. For example, an attacker can do much more damage if he misuses a service that uses an account with read/write/execute access to all other cloud resources in your account compared to using an account that only has read access to the services it needs to fetch data from.
Highlight the concepts with some practical examples help to understand what’s important. We use AWS as the cloud provider of choice. Most examples work similar but in a different shape or form for the other cloud providers as well.
- Secure your S3 buckets using bucket policies and Access Control Lists (ACL).
- Use IAM roles with only the permissions it needs and don’t reuse IAM roles for different (wider) use cases.
- Apply Pod Security Policies and/or the successor for your containerized workloads in Kubernetes.
- Restrict incoming traffic to your security groups and only for the services you really need.
- Use the right type of user for your non personal accounts.
This list of examples can never be complete, but it gives you a broad overview of the possibilities to apply the least privilege principle in reality.
Detect the culprits
To apply the examples in the “real world”, we use Cloudformation Templates to show you how things work. We avoid using the wizards and GUI to implement them since that does not give you the opportunity to automate these aspects.
First of all, how would you detect security issues. Lint is not enough since it focuses on syntax related problems and generic warnings like “this variable is declared, but not used”. I use CFN_NAG to scan Cloudformation templates to detect (potential) security issues up front. Once you start scanning your templates, you might see results similar to this:
| WARN W9 | | Resources: ["WebApplicationSecurityGroup"] | | Security Groups found with ingress cidr that is not /32 ------------------------------------------------------------
| WARN W11 | | Resources: ["ApplicationFunctionXIamRole"] | Line Numbers:  | | IAM role should not allow * resource on its permissions policy ------------------------------------------------------------
| WARN W51 | | Resources: ["ApplicationXBucket"] | Line Numbers:  | | S3 bucket should likely have a bucket policy ------------------------------------------------------------
Fixing the issues
Secure your S3 buckets
Securing your S3 buckets is not an easy task. Since you can use S3 Buckets for a number of use cases such as storing your application source code and public websites that host static concent, it’s difficult to find the right balance between those topics. At least you need to apply the following concepts.
Add the following element to your template:
PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true
This blocks public access for the bucket itself and the objects you store inside them. It also restricts public access to the bucket.
Add the following snippet to your template to apply a bucket policy to your existing bucket:
Type: AWS::S3::BucketPolicy Properties: Bucket: ApplicationXBucket PolicyDocument: [see below]
In the PolicyDocument you need to specify as few permissions as needed. Such as S3:GetObject and not S3:* which gives all possible permissions on the referencing bucket.
Use IAM roles
Identity Access Management in AWS is implemented using IAM roles, permissions and policies. In general, an IAM role should only contain permissions for one or more resources that it needs to have access to. And only the type of action that is really needed. In the previous example, the ApplicationFunctionXIamRole resource has access to all resources in the scope of the Cloud account. You need to restrict it, for example using the following snippet:
LambdaIamRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Principal: Service: - 'lambda.amazonaws.com' Action: - 'sts:AssumeRole' Policies: - PolicyName: read-dynamodb PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "dynamodb:GetItem" - "dynamodb:PutItem" - "dynamodb:Scan" Resource: 'arn:aws:dynamodb:eu-west-1:[accountIDHidden]:table/ApplicationXTable'
In here you see the AWS Lambda role only has Get (read), Put (write) and Scan (list) permissions for your ApplicationXTable DynamoDB database table. If you would include “*” here, your Lambda function could read, write and list all DynamoDB tables and this opens up the door for unwanted access. If someone misuses or misconfigures your IAM role, it could have a devastating effect on your databases.
Pod Security Polices
Kubernetes has a lot of components that require proper protection. Think of network related resources such as Network Policies, Service Accounts that use Roles and ClusterRoles which specify what a user or service is allowed to do on what resource and Pods that run the actual application containers.
The application that runs inside those containers might need to access other cloud resources in your cloud environment such as persistent storage or message queues. By default, the Pods in your cluster utilize the IAM role that is attached to your EC2 instance that hosts your containers. If you have not limited this IAM role, permissions are propagated to your containers. It’s best to apply Pod Security Policies to your containers that specify exactly what is allowed on which resources and nothing more.
In the future, Pod Security Policies will be replaced by PodSecurity Admission Controllers. This new feature handles the implementation of the least privilege principles much easier. It uses the following Pod Security Admission labels for namespaces (thus already applying a narrow scope here): enforce, audit and warn. This let’s you apply the predefined Pod Security Standard levels.
Drop capabilities to limit what a container can do
An example here. From Kubernetes version 1.22 onward, containers must drop ALL capabilities, and are only permitted to add back the NET_BIND_SERVICE capability. This leads to the following fields which are allowed c.q. restricted:
For more information, please consult the Pod Security Admission webpage.
Limit your security groups
Security groups are a network related resource to help you control which traffic to another service is allowed or blocked. In addition to that, you can control incoming and outgoing traffic.
The red flag of our example “WebApplicationSecurityGroup” translates to the following problem:
Security Groups found with ingress cidr that is not /32
In simple words, this means that your application accepts incoming traffic from a very broad range of networks and/or the entire internet. That means that anyone can access it while you might want to restrict it to only a specific application that is also hosted in your cloud environment.
Use the following snippet in your Cloudformation template to fix it.
WebApplicationSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: VpcId: !Ref "AppVPC" GroupDescription: "Web application Security Group" SecurityGroupIngress: - CidrIp: !Ref "WebAppLoadBalancer" FromPort: "22" IpProtocol: "tcp" ToPort: "22"
Here, you restrict incoming traffic ONLY from your own private LoadBalancer and not the entire internet. This makes your application much more secure and this is a must-have to secure your cloud infrastructure.
Non personal accounts
In general there are two types of accounts: personal accounts and non personal accounts (also known as technical accounts). Personal accounts are used by individuals like software developers or system operators. For example to login to the AWS console to view resources. Non personal accounts are used in technical systems that do other stuff such as provisioning cloud resources or deploy an application. AWS uses the same concepts.
In our example, we need a non personal account that connects our CI/CD pipeline (using an external tool) to AWS itself. You won’t use a personal account with all permissions here since that would make your AWS environment extremely vulnerable in case of an attack on your supply chain. In turn, it’s better to create a non personal account that only contain an ACCESS_KEY_ID and SECRET_ACCESS_KEY to programmatically access the AWS environment.
The policy for the role of this user might look like the following:
--- Version: '2012-10-17' Statement: - Sid: VisualEditor0 Effect: Allow Action: cloudformation:* Resource: "*" Condition: IpAddress: aws:SourceIp: 22.214.171.124
It tells you to only allow any action on Cloudformation templates, thus you can’t list, view or create any other resources. Besides, access in only allowed from the a restricted and fixed IP address (in our example it’s 126.96.36.199). Off course there is a problem here, that the IP address can (and probably will) change in the future, but it gives you much greater protection than just opening up all possibilities for this user.
Tools to help you
The above-mentioned issues can be detected with CFN_NAG. Besides these kind of checks, we should also validate the syntax and other static aspects of the Cloudformation templates.
AWS offers a simple way to validate the syntax of your template using: cloudformation validate-template <sample-template.yaml>. This would be your first check before anything else. Your next step would be to validate other static aspects such as unused variables or expensive instance types. For this matter, you can use CFN_LINT.
Once these checks help to pass or correct your templates, the next step would be to do the actual security checks with tools such as CFN_NAG. Other tools that can do this for you are Trivy or Cloudsploit.
It’s a good practice to include those checks into your CI/CD pipelines as well and to set quality gates that prevent you from deploying templates that are insecure. The (resource) costs to deploy and/or rollback in case of a security issue are much higher compared to fixing it while constructing those templates in your IDE.
After you fix those types of issues, you can use verify them using AWS trusted advisor to confirm your improvements. There are also other commercial tools like Prisma Cloud that offers an extensive Cloud Security Posture Management solution. One of the benefits of this tool is that it constantly scans your cloud environment(s). If needed it can block insecure cloud configurations or remediate them (automatically) for you. It also provides a visual graph to show you all of the security issues and where they originate from.
One of the key security aspects in the cloud is the least privilege principle. It’s extremely important to apply this principle to avoid misuse of your cloud resources or data theft. In this article I showed several examples of this principle and how you can fix them. There are several free tools available such as CFN_NAG to help you accomplish this. All examples show a practical way to implement them using efficient methods that utilize IaC. I hope this article helped you to get a better understanding of actually implement this principle and further secure your cloud deployments.
If you have questions related to this topic, feel free to book a meeting with one of our solutions experts, mail to firstname.lastname@example.org.