Understanding Elastic Beanstalk Security
AWS Elastic Beanstalk is a Platform as a Service (PaaS) that abstracts away much of the infrastructure management. However, the underlying resources—EC2 instances, load balancers, S3 buckets, and more—still require careful security configuration. Two foundational pillars of Elastic Beanstalk security are IAM policies (who and what can access and manage your environment) and network security (how traffic reaches your application and flows between components). This tutorial walks you through both, with practical code examples you can apply directly to your own environments.
IAM Policies and Roles for Elastic Beanstalk
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Elastic Beanstalk relies on AWS Identity and Access Management (IAM) to grant permissions to both the service itself and the resources it creates. There are two primary IAM roles every environment uses:
- Service role – assumed by the Elastic Beanstalk service to call other AWS APIs on your behalf (e.g., creating EC2 instances, scaling, updating load balancers).
- Instance profile role – attached to the EC2 instances that run your application, allowing them to access AWS services (e.g., pulling from S3, writing logs, reading secrets).
The Elastic Beanstalk Service Role
When you create an environment, Elastic Beanstalk needs a service role to perform management operations. The default role is aws-elasticbeanstalk-service-role. You can create it automatically via the console or manually with the policy below. Without it, the environment cannot scale, update, or even be created correctly.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:Describe*",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ConfigureHealthCheck",
"ec2:Describe*",
"ec2:CreateSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:DeleteSecurityGroup",
"autoscaling:Describe*",
"autoscaling:CreateAutoScalingGroup",
"autoscaling:UpdateAutoScalingGroup",
"autoscaling:DeleteAutoScalingGroup",
"autoscaling:PutScalingPolicy",
"autoscaling:DeletePolicy",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"cloudformation:DescribeStack*",
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"sqs:CreateQueue",
"sqs:DeleteQueue"
],
"Resource": "*"
}
]
}
To create the service role using the AWS CLI:
aws iam create-role --role-name aws-elasticbeanstalk-service-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "elasticbeanstalk.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {"StringEquals": {"sts:ExternalId": "elasticbeanstalk"}}
}]
}'
aws iam put-role-policy --role-name aws-elasticbeanstalk-service-role \
--policy-name ElasticBeanstalkServiceRolePolicy \
--policy-document file://service-role-policy.json
The ExternalId condition is a best practice to prevent the confused deputy problem, ensuring only Elastic Beanstalk can assume this role.
The Instance Profile Role
Every EC2 instance in your environment must have an instance profile that grants it permissions to interact with other AWS services. The default is aws-elasticbeanstalk-ec2-role. A typical policy allows access to S3 (for application versions and logs), CloudWatch Logs, and optionally RDS, DynamoDB, or Secrets Manager.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:s3:::elasticbeanstalk-*",
"arn:aws:s3:::elasticbeanstalk-*/*",
"arn:aws:logs:*:*:log-group:/aws/elasticbeanstalk/*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"autoscaling:Describe*",
"elasticloadbalancing:Describe*",
"sqs:GetQueueUrl",
"sqs:ReceiveMessage",
"sqs:DeleteMessage"
],
"Resource": "*"
}
]
}
You attach the policy to the role and then specify the instance profile when creating the environment. The CLI command for attaching the managed policy AWS-ElasticBeanstalk-WebTier (which covers many common needs) is:
aws iam attach-role-policy --role-name aws-elasticbeanstalk-ec2-role \
--policy-arn arn:aws:iam::aws:policy/AWS-ElasticBeanstalk-WebTier
Important: Never use overly broad policies like AdministratorAccess for the instance role. Follow least privilege and only grant what your application code truly needs.
IAM Policies for Developers and CI/CD
Your team members or CI/CD pipelines need permissions to interact with Elastic Beanstalk itself—creating applications, deploying versions, and managing environments. The AWS managed policy AWSElasticBeanstalkFullAccess is a good starting point, but you should tailor it to your organization's boundaries.
A custom policy for a developer to deploy to a specific environment might look like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"elasticbeanstalk:CreateApplicationVersion",
"elasticbeanstalk:UpdateEnvironment",
"elasticbeanstalk:DescribeEnvironments",
"elasticbeanstalk:DescribeEnvironmentResources",
"elasticbeanstalk:DescribeEvents",
"elasticbeanstalk:RequestEnvironmentInfo",
"elasticbeanstalk:RetrieveEnvironmentInfo",
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:elasticbeanstalk:us-east-1:123456789012:application/my-app",
"arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/my-app/*",
"arn:aws:s3:::elasticbeanstalk-us-east-1-123456789012",
"arn:aws:s3:::elasticbeanstalk-us-east-1-123456789012/*"
]
}
]
}
This scopes the user to only one application and its environments, plus the necessary S3 bucket for storing application versions. You can further restrict with conditions on elasticbeanstalk:EnvironmentName or tags.
Using IAM Conditions with Elastic Beanstalk
Elastic Beanstalk supports IAM condition keys such as elasticbeanstalk:EnvironmentName, elasticbeanstalk:ApplicationName, and aws:SourceArn. For example, to restrict environment termination to a specific VPC, you could combine an IAM policy with a condition that checks the VPC ID from the environment's configuration. However, the most common use is restricting access to specific applications:
{
"Effect": "Allow",
"Action": "elasticbeanstalk:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"elasticbeanstalk:ApplicationName": "my-app"
}
}
}
This ensures the user can only act on the application my-app, regardless of the environment. For network-oriented restrictions, you'd typically use VPC and subnet conditions in the EC2 or load balancer actions, but those are enforced when the service role tries to create resources, so it's often better to use service control policies (SCPs) in AWS Organizations.
Network Security for Elastic Beanstalk
Elastic Beanstalk environments run inside a Virtual Private Cloud (VPC) that you define. Proper network design is critical to protect your application from unauthorized access and to comply with internal security policies. The key components are subnets, security groups, and network ACLs.
Choosing and Designing the VPC
Always launch your Elastic Beanstalk environment inside a VPC—never in the EC2-Classic (legacy) mode. A well-architected VPC includes:
- Public subnets – for load balancers that must accept traffic from the internet.
- Private subnets – for EC2 instances that run your application, with no direct internet exposure.
- NAT gateway or NAT instance – in a public subnet so that instances in private subnets can access the internet for updates or external APIs.
When you create the environment, specify the VPC ID, and choose the subnets for the load balancer and instances. This is done via the console or CLI options:
aws elasticbeanstalk create-environment --application-name my-app \
--environment-name my-env \
--solution-stack-name "64bit Amazon Linux 2023 v5.8.0 running Node.js 18" \
--option-settings \
Namespace=aws:ec2:vpc,OptionName=VPCId,Value=vpc-0abcd1234 \
Namespace=aws:ec2:vpc,OptionName=Subnets,Value=subnet-1111,subnet-2222 \
Namespace=aws:ec2:vpc,OptionName=ELBSubnets,Value=subnet-3333,subnet-4444 \
Namespace=aws:ec2:vpc,OptionName=AssociatePublicIpAddress,Value=false
The AssociatePublicIpAddress option set to false ensures instances in private subnets don't get public IPs, forcing all inbound traffic through the load balancer.
Security Groups Configuration
Elastic Beanstalk automatically creates security groups for the load balancer and the EC2 instances, but you can and should refine them. The default behavior allows the load balancer to accept HTTP on port 80 from anywhere (0.0.0.0/0). For production, restrict the source CIDR to your expected user base, or use HTTPS only.
You can customize security groups using .ebextensions configuration files. Here's an example that adds a security group for an internal RDS database and allows the EC2 instances to communicate with it:
# .ebextensions/network-security.config
option_settings:
aws:ec2:vpc:
VPCId: vpc-0abcd1234
Subnets: subnet-1111,subnet-2222
ELBSubnets: subnet-3333,subnet-4444
SecurityGroups: sg-0myinstancesg
aws:elbv2:loadbalancer:
SecurityGroups: sg-0myloadbalancersg
Resources:
instanceSecurityGroupIngressToRDS:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: sg-0myinstancesg
SourceSecurityGroupId: sg-0myrdssg
IpProtocol: tcp
FromPort: 3306
ToPort: 3306
Description: Allow MySQL from RDS security group
Note the use of option_settings to set the security groups for the EC2 instances and the load balancer. The custom resource adds an ingress rule. You can also modify the auto-generated security group's ingress rules via the same mechanism, but specifying your own groups gives you full control.
Network ACLs as a Second Layer
Security groups are stateful; network ACLs are stateless and provide an additional boundary at the subnet level. For a private subnet hosting your application instances, you might configure inbound rules that only allow traffic from the load balancer security group's subnet and outbound to the internet via the NAT. However, NACLs are optional and can become complex. A common pattern is:
- Public subnets: NACL allows inbound HTTP/HTTPS from 0.0.0.0/0 and ephemeral ports for return traffic.
- Private subnets: NACL allows inbound only from the load balancer subnet CIDR on the application port (e.g., 8080) and outbound to 0.0.0.0/0 for updates (if a NAT is present).
NACLs are configured at the subnet level outside of Elastic Beanstalk, but they affect all resources in those subnets, so plan accordingly.
Enforcing HTTPS and TLS
Network security isn't only about IP filtering; encrypting data in transit is mandatory. Elastic Beanstalk supports HTTPS termination at the Application Load Balancer or Classic Load Balancer. You can upload an SSL certificate via ACM (AWS Certificate Manager) and configure the load balancer listener:
option_settings:
aws:elbv2:listener:443:
Protocol: HTTPS
SSLCertificateArns: arn:aws:acm:us-east-1:123456789012:certificate/abc123-...
DefaultProcess: default
ListenerEnabled: true
aws:elbv2:listener:80:
Protocol: HTTP
DefaultProcess: redirect
ListenerEnabled: true
To redirect HTTP to HTTPS, define a listener action in the load balancer rules. You can set the DefaultProcess to redirect and provide a custom rule in .ebextensions:
Resources:
redirectHttpToHttps:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: {Ref: AWSEBV2LoadBalancerListener80}
Priority: 1
Conditions:
- Field: host-header
Values: ['*']
Actions:
- Type: redirect
RedirectConfig:
Protocol: HTTPS
Port: 443
Host: '#{host}'
Path: '/#{path}'
Query: '#{query}'
StatusCode: HTTP_301
This ensures all plain HTTP requests are redirected to HTTPS, protecting data in transit.
Restricting Inbound Traffic with Source IPs or Security Groups
For internal applications, you may want to limit access to a specific IP range or a set of security groups. Using the aws:elbv2:loadbalancer:SecurityGroups option, you can assign a security group that only allows ingress from trusted CIDRs. Alternatively, if you're using an internal load balancer (in private subnets), you can restrict access via security group references. This is ideal for microservices where only other VPC resources should call your API.
# Set the load balancer scheme to internal
option_settings:
aws:ec2:vpc:
ELBSubnets: subnet-private1,subnet-private2
aws:elbv2:loadbalancer:
SecurityGroups: sg-internal-lb
Scheme: internal
Now the load balancer has no public IP and is only reachable from within the VPC, providing strong network isolation.
Best Practices for Elastic Beanstalk Security
- Least privilege IAM roles – Avoid wildcard permissions. Tailor policies to the exact S3 buckets, log groups, and resources your application and environment need.
- Separate service and instance roles – Never use the service role as your instance profile. They serve different purposes and combining them violates separation of duties.
- Use
ExternalIdin service roles – Protects against the confused deputy problem when a service assumes a role. - Deploy into private subnets – Keep EC2 instances away from direct internet access. Only load balancers (and possibly bastion hosts) should be in public subnets.
- Restrict security group ingress – Don't leave
0.0.0.0/0on application ports. Limit to specific CIDRs or security group references. - Enforce HTTPS – Redirect all HTTP to HTTPS and use strong TLS policies. Consider using
AWS::ElasticLoadBalancingV2::Listenerwith a security policy likeELBSecurityPolicy-TLS-1-2-2017-01. - Monitor and audit with CloudTrail – Track IAM and Elastic Beanstalk API calls to detect misconfigurations or unauthorized actions.
- Use infrastructure as code – Define your environments, IAM roles, and network settings with CloudFormation or
.ebextensionsto ensure reproducibility and avoid manual drift. - Regularly rotate instance credentials – If you store long-lived credentials on instances, replace them with IAM roles or temporary credentials via AWS STS.
Conclusion
Security in Elastic Beanstalk is a shared responsibility. While AWS manages the underlying platform, you must correctly configure IAM policies to control access and network boundaries to protect your data. By crafting precise IAM roles with least privilege, placing your instances in private subnets, enforcing encrypted traffic, and using security groups and NACLs as layered defenses, you build a robust environment that can safely run production workloads. The code examples and patterns provided here give you a practical starting point—adapt them to your own context, and always test changes in a staging environment before rolling to production. With these foundations, your Elastic Beanstalk applications will be both scalable and secure.