Security groups do not allow all ingress traffic from the Internet
OpenStack security groups are evaluated to verify that no rule allows all ingress traffic (any protocol, any port) from the Internet (0.0.0.0/0 or ::/0). A rule with no protocol and no port restriction is effectively a "permit any" firewall rule, completely bypassing network-level access controls. This is the most permissive possible configuration and should never be used in production.
Risk
Allowing all inbound traffic from the Internet exposes every service on the instance to unauthorized access. Attackers can exploit any listening service including databases, management interfaces, and internal APIs. This bypasses defense-in-depth and enables initial access, lateral movement, data exfiltration, and infrastructure compromise.
Run this check with Prowler
Run in Prowler Cloudprowler openstack --checks networking_security_group_allows_all_ingress_from_internetRecommendation
Remove rules that allow all ingress traffic from the Internet. Follow the principle of least privilege by creating specific rules for only the required protocols and ports. Use security group references (remote_group_id) instead of CIDRs where possible to restrict access to known infrastructure components.
Remediation
Terraform
# Terraform: create specific ingress rules instead of allowing all traffic
resource "openstack_networking_secgroup_v2" "web_servers" {
name = "web-servers-sg"
description = "Security group for web servers"
}
# GOOD: Allow only HTTPS from the Internet
resource "openstack_networking_secgroup_rule_v2" "https_ingress" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.web_servers.id
}
# GOOD: Allow SSH only from bastion security group
resource "openstack_networking_secgroup_rule_v2" "ssh_from_bastion" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_group_id = openstack_networking_secgroup_v2.bastion.id
security_group_id = openstack_networking_secgroup_v2.web_servers.id
}
# BAD: Never allow all traffic from the Internet
# resource "openstack_networking_secgroup_rule_v2" "allow_all" {
# direction = "ingress"
# ethertype = "IPv4"
# remote_ip_prefix = "0.0.0.0/0" # DANGEROUS - no protocol/port restriction
# security_group_id = openstack_networking_secgroup_v2.web_servers.id
# }Other
Check Code
from typing import List
from prowler.lib.check.models import Check, CheckReportOpenStack
from prowler.providers.openstack.lib.security_groups import is_cidr_public
from prowler.providers.openstack.services.networking.networking_client import (
networking_client,
)
class networking_security_group_allows_all_ingress_from_internet(Check):
"""Ensure security groups do not allow all ingress traffic from the Internet."""
def execute(self) -> List[CheckReportOpenStack]:
findings: List[CheckReportOpenStack] = []
for sg in networking_client.security_groups:
report = CheckReportOpenStack(metadata=self.metadata(), resource=sg)
report.resource_id = sg.id
report.resource_name = sg.name
report.region = sg.region
all_ingress_exposed = False
exposed_rules = []
for rule in sg.security_group_rules:
# Only match rules that allow ALL protocols AND ALL ports
if rule.direction != "ingress":
continue
if rule.protocol is not None:
continue
if rule.port_range_min is not None or rule.port_range_max is not None:
continue
# Check if from internet (0.0.0.0/0, ::/0, or None with no group)
if rule.remote_group_id:
continue
if rule.remote_ip_prefix:
if not is_cidr_public(rule.remote_ip_prefix, any_address=True):
continue
# else: no prefix and no group means all IPs
all_ingress_exposed = True
cidr = rule.remote_ip_prefix or "0.0.0.0/0"
exposed_rules.append(f"rule {rule.id} ({cidr})")
if all_ingress_exposed:
report.status = "FAIL"
rules_str = ", ".join(exposed_rules)
report.status_extended = f"Security group {sg.name} ({sg.id}) allows all ingress traffic (any protocol, any port) from the Internet via {rules_str}."
else:
report.status = "PASS"
report.status_extended = f"Security group {sg.name} ({sg.id}) does not allow all ingress traffic from the Internet."
findings.append(report)
return findings