Skip to main content

Restrict SSH Access from the Internet

Overview

This check identifies security groups that allow inbound SSH connections (TCP port 22) from anywhere on the internet (0.0.0.0/0 or ::/0). SSH access should be restricted to trusted IP addresses or accessed through more secure methods like AWS Systems Manager Session Manager.

Risk

Unrestricted SSH access creates significant security vulnerabilities:

  • Brute force attacks: Internet-exposed SSH invites automated credential-stuffing and password-guessing attacks from millions of bots scanning for open port 22
  • Remote shell compromise: Successful authentication gives attackers full command-line access to your server
  • Lateral movement: Once inside one server, attackers can pivot to other systems in your network
  • Data theft and ransomware: Attackers can steal sensitive data, install cryptominers, or deploy ransomware
  • Compliance violations: Most security frameworks (CIS, PCI-DSS, SOC 2) prohibit unrestricted SSH access

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to modify security groups
  • Knowledge of which IP addresses or ranges need SSH access (e.g., your office IP, VPN range)
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • ec2:DescribeSecurityGroups
  • ec2:DescribeSecurityGroupRules
  • ec2:RevokeSecurityGroupIngress
  • ec2:AuthorizeSecurityGroupIngress

AWS Console Method

  1. Find the offending security group

    • Go to EC2 Console in us-east-1
    • In the left sidebar, click Security Groups (under Network & Security)
    • Or go directly to Security Groups
  2. Identify the rule to fix

    • Select the security group flagged by Prowler
    • Click the Inbound rules tab
    • Find rules where:
      • Port range is 22 (or includes 22)
      • Source is 0.0.0.0/0 or ::/0
  3. Remove the unrestricted rule

    • Click Edit inbound rules
    • Find the SSH rule with source 0.0.0.0/0 or ::/0
    • Click Delete (trash icon) to remove it
    • Click Save rules
  4. Add a restricted SSH rule (if needed)

    • Click Edit inbound rules
    • Click Add rule
    • Configure:
      • Type: SSH
      • Port range: 22 (auto-filled)
      • Source: Select Custom and enter your trusted IP range (e.g., 203.0.113.0/24 for your office network, or 10.0.0.0/8 for internal VPN)
    • Click Save rules
  5. Repeat for IPv6 rules

    • If there's a rule with source ::/0, remove it and add a restricted IPv6 range if needed
AWS CLI (optional)

Identify security groups with unrestricted SSH

aws ec2 describe-security-groups \
--region us-east-1 \
--filters "Name=ip-permission.from-port,Values=22" \
"Name=ip-permission.to-port,Values=22" \
"Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[*].[GroupId,GroupName]' \
--output table

View the specific rules in a security group

aws ec2 describe-security-group-rules \
--region us-east-1 \
--filters Name="group-id",Values="sg-0123456789abcdef0" \
--query 'SecurityGroupRules[?FromPort==`22` && ToPort==`22`]' \
--output table

Replace sg-0123456789abcdef0 with your security group ID.

Remove the unrestricted SSH rule (IPv4)

aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0

Remove the unrestricted SSH rule (IPv6)

aws ec2 revoke-security-group-ingress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--ip-permissions IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges=[{CidrIpv6=::/0}]

Add a restricted SSH rule

aws ec2 authorize-security-group-ingress \
--region us-east-1 \
--group-id sg-0123456789abcdef0 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.0/24

Replace 203.0.113.0/24 with your trusted IP range.

CloudFormation (optional)

When defining security groups in CloudFormation, always restrict SSH to specific CIDR ranges:

AWSTemplateFormatVersion: '2010-09-09'
Description: Security group with restricted SSH access

Parameters:
TrustedSSHCidr:
Type: String
Description: CIDR range allowed to SSH (e.g., your office IP)
Default: 203.0.113.0/24
AllowedPattern: ^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$

VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where the security group will be created

Resources:
RestrictedSSHSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: restricted-ssh-access
GroupDescription: Security group with SSH restricted to trusted IPs only
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref TrustedSSHCidr
Description: SSH from trusted network only
Tags:
- Key: Name
Value: restricted-ssh-access

Outputs:
SecurityGroupId:
Description: ID of the security group with restricted SSH
Value: !Ref RestrictedSSHSecurityGroup

Important: Never use 0.0.0.0/0 or ::/0 as the CidrIp or CidrIpv6 for SSH rules.

Terraform (optional)
variable "trusted_ssh_cidr" {
description = "CIDR range allowed to SSH (e.g., your office IP)"
type = string
default = "203.0.113.0/24"
}

variable "vpc_id" {
description = "VPC where the security group will be created"
type = string
}

resource "aws_security_group" "restricted_ssh" {
name = "restricted-ssh-access"
description = "Security group with SSH restricted to trusted IPs only"
vpc_id = var.vpc_id

# SSH from trusted network only - NEVER use 0.0.0.0/0
ingress {
description = "SSH from trusted network"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.trusted_ssh_cidr]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "restricted-ssh-access"
}
}

output "security_group_id" {
description = "ID of the security group with restricted SSH"
value = aws_security_group.restricted_ssh.id
}

Important: Never use 0.0.0.0/0 or ::/0 in cidr_blocks or ipv6_cidr_blocks for SSH ingress rules.

Better alternative: Use AWS Systems Manager Session Manager

Instead of exposing SSH at all, consider using AWS Systems Manager Session Manager for shell access. This eliminates the need for open inbound ports entirely.

Benefits:

  • No open inbound ports required
  • Centralized access control via IAM
  • Full audit logging in CloudTrail
  • No need to manage SSH keys or bastion hosts

Setup steps:

  1. Ensure instances have the SSM Agent installed (pre-installed on Amazon Linux 2, Amazon Linux 2023, and many other AMIs)

  2. Attach the AmazonSSMManagedInstanceCore policy to your instance role

  3. Connect via Session Manager:

    • Console: EC2 > Instances > Select instance > Connect > Session Manager
    • CLI: aws ssm start-session --target i-0123456789abcdef0

For more information, see AWS Systems Manager Session Manager.

Verification

After making changes, verify the fix:

  1. In the AWS Console:

    • Go to EC2 > Security Groups
    • Select the security group you modified
    • Check Inbound rules tab
    • Confirm there are no SSH rules with source 0.0.0.0/0 or ::/0
  2. Re-run Prowler:

    • Run the specific check: prowler aws --check ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22
    • The check should now pass
CLI verification commands

Check if any security groups still have unrestricted SSH access:

aws ec2 describe-security-groups \
--region us-east-1 \
--filters "Name=ip-permission.from-port,Values=22" \
"Name=ip-permission.to-port,Values=22" \
"Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[*].[GroupId,GroupName]' \
--output table

If the output is empty, no security groups have unrestricted SSH access.

Verify a specific security group's rules:

aws ec2 describe-security-group-rules \
--region us-east-1 \
--filters Name="group-id",Values="sg-0123456789abcdef0" \
--query 'SecurityGroupRules[?FromPort==`22`].[SecurityGroupRuleId,CidrIpv4,CidrIpv6]' \
--output table

Additional Resources

Notes

  • Identify who needs SSH access: Before restricting, ensure you know which IPs or ranges legitimately need SSH access (office networks, VPNs, bastion hosts)
  • Consider Session Manager: For most use cases, AWS Systems Manager Session Manager is a better alternative to SSH. It requires no open inbound ports and provides better auditing
  • Use bastion hosts: If you must use SSH, consider routing access through a bastion host in a public subnet, with your application servers in private subnets
  • Multiple security groups: If instances need SSH from different sources, consider using multiple security groups or updating rules dynamically
  • Test connectivity: After restricting SSH, verify you can still connect from your trusted IPs before logging out of any active sessions
  • Emergency access: Have a backup access method (like Session Manager or AWS EC2 Instance Connect) in case you accidentally lock yourself out
  • Default security groups: The default security group in each VPC may have overly permissive rules. Review and restrict it, or avoid using it altogether