Back to Checks
openstack

Security groups do not allow all ingress traffic from the Internet

networking_security_group_allows_all_ingress_from_internet
Service: networkingSeverity: critical
OfficialBuilt-inVerified
Categories:internet-exposedtrust-boundaries
Resource Type:OS::Neutron::SecurityGroup

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 Cloud
Run
prowler openstack --checks networking_security_group_allows_all_ingress_from_internet

Recommendation

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

1Navigate to Network > Security Groups
2Locate the security group with unrestricted ingress
3Click Manage Rules
4Delete rules allowing all traffic from 0.0.0.0/0 or ::/0 with no protocol/port restriction
5Create specific rules for only the protocols and ports required (e.g., TCP 443 for HTTPS)
6Restrict source CIDRs to known IP ranges where possible
7Save changes and verify connectivity

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