Back to Checks
oraclecloud

Network security group restricts ingress from 0.0.0.0/0 to port 22 (SSH)

network_security_group_ingress_from_internet_to_ssh_port
Service: networkSeverity: high
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:NetworkSecurityGroup

Network security groups with ingress from 0.0.0.0/0 exposing SSH are identified. This includes rules that explicitly permit TCP 22, use all protocols, or define TCP port ranges that encompass 22.

Risk

Public SSH access enables Internet-wide probing, brute force, and credential stuffing that can grant shell access. Compromise allows lateral movement, data exfiltration (confidentiality), unauthorized changes or malware (integrity), and service disruption or ransomware-driven lockouts (availability).

Run this check with Prowler

Run in Prowler Cloud
Run
prowler oraclecloud --checks network_security_group_ingress_from_internet_to_ssh_port

Recommendation

Enforce least privilege: limit SSH in NSG rules to trusted CIDRs or private networks. Prefer bastion access (e.g., OCI Bastion) or VPN/private connectivity over direct Internet exposure. Use key-based auth, disable PasswordAuthentication, log and monitor access, and segment workloads for defense in depth.

Remediation

CLI

oci network nsg rules remove --nsg-id <nsg-ocid> --security-rule-ids '["<rule-ocid>"]'

Terraform

# Restrict SSH so it's not from 0.0.0.0/0
resource "oci_core_network_security_group_security_rule" "<example_resource_name>" {
  network_security_group_id = "<example_resource_id>"
  direction                 = "INGRESS"
  protocol                  = "6"                         # TCP
  source                    = "10.0.0.0/8"               # CRITICAL: not 0.0.0.0/0; limits SSH to internal range

  tcp_options {
    destination_port_range {
      min = 22  # CRITICAL: explicitly defines SSH port
      max = 22
    }
  }
}

Other

1In the OCI Console, go to Networking > Network Security Groups
2Open the target NSG and go to the Rules tab
3Find the INGRESS rule with Source 0.0.0.0/0 that includes TCP port 22
4Click Remove to delete that rule
5(If SSH is required) Add a new INGRESS rule for TCP port 22 with Source set to a specific trusted CIDR (not 0.0.0.0/0), then Save

Check Code

"""Check if network security groups allow ingress from 0.0.0.0/0 to port 22."""

from prowler.lib.check.models import Check, Check_Report_OCI
from prowler.providers.oraclecloud.services.network.network_client import network_client


class network_security_group_ingress_from_internet_to_ssh_port(Check):
    """Check if network security groups allow ingress from 0.0.0.0/0 to port 22."""

    def execute(self) -> Check_Report_OCI:
        """Execute the network_security_group_ingress_from_internet_to_ssh_port check.

        Returns:
            List of Check_Report_OCI objects with findings
        """
        findings = []

        for nsg in network_client.network_security_groups:
            has_public_ssh_access = False

            # Check security rules for public SSH access
            for rule in nsg.security_rules:
                if (
                    rule.get("direction") == "INGRESS"
                    and rule.get("source") == "0.0.0.0/0"
                ):
                    protocol = rule.get("protocol")
                    # Protocol 6 is TCP
                    if protocol == "6":
                        tcp_options = rule.get("tcp_options")
                        # If tcp_options is None, all TCP ports are allowed
                        if not tcp_options:
                            has_public_ssh_access = True
                            break
                        # If tcp_options exists, check destination port range
                        dst_port = tcp_options.get("destination_port_range")
                        if dst_port:
                            port_min = dst_port.get("min", 0)
                            port_max = dst_port.get("max", 0)
                            if port_min <= 22 <= port_max:
                                has_public_ssh_access = True
                                break
                        # If no destination port range specified, all ports are allowed
                        else:
                            has_public_ssh_access = True
                            break
                    # Protocol "all" or if protocol is not specified
                    elif protocol == "all" or not protocol:
                        has_public_ssh_access = True
                        break

            report = Check_Report_OCI(
                metadata=self.metadata(),
                resource=nsg,
                region=nsg.region,
                resource_name=nsg.display_name,
                resource_id=nsg.id,
                compartment_id=nsg.compartment_id,
            )

            if has_public_ssh_access:
                report.status = "FAIL"
                report.status_extended = f"Network security group {nsg.display_name} allows ingress from 0.0.0.0/0 to port 22 (SSH)."
            else:
                report.status = "PASS"
                report.status_extended = f"Network security group {nsg.display_name} does not allow ingress from 0.0.0.0/0 to port 22 (SSH)."

            findings.append(report)

        return findings