OpenStack Neutron Security Group Audit
Driftmux can audit OpenStack Neutron security groups to detect public exposure of critical ports.
This mode does not scan virtual machines directly. Instead, it queries the OpenStack API and analyses Neutron security group rules.
It is useful when the cloud administrator does not have access to the guest operating systems but still wants to identify dangerous network exposure at the cloud layer.
What it detects
The OpenStack scanner detects ingress security group rules that expose critical ports to public CIDRs such as:
0.0.0.0/0
::/0
Examples of detected exposures:
SSH 22/tcp exposed to 0.0.0.0/0
RDP 3389/tcp exposed to 0.0.0.0/0
PostgreSQL 5432/tcp exposed to 0.0.0.0/0
Redis 6379/tcp exposed to 0.0.0.0/0
Elasticsearch/OpenSearch 9200/tcp exposed to 0.0.0.0/0
Docker API 2375/tcp exposed to 0.0.0.0/0
Kubernetes API 6443/tcp exposed to 0.0.0.0/0
All ports exposed to 0.0.0.0/0
By default, HTTP and HTTPS exposure on ports 80/tcp and 443/tcp is not
reported as a high-risk finding.
What it does not do
The OpenStack scanner does not log into virtual machines.
It does not inspect:
Guest operating systems
Installed packages inside VMs
Local users
Processes
Rootkits
Filesystem integrity
Application logs inside the VM
It only analyses the cloud networking configuration available through the OpenStack API.
Requirements
The scanner requires:
openstacksdk
Valid OpenStack credentials
Permissions to list Neutron security group rules
Permissions to list Neutron ports
Permissions to read projects and servers, if usage enrichment is enabled
Install the Python dependency:
pip install openstacksdk
If you installed Driftmux in editable mode:
pip install -e .
Authentication
The scanner supports the usual OpenStack authentication methods.
Using clouds.yaml
Example:
driftmux --openstack-sg-audit --os-cloud openstack
Using an OpenRC file
Example:
source admin-openrc.sh
driftmux --openstack-sg-audit --format markdown
Basic usage
Run the OpenStack security group audit:
driftmux --openstack-sg-audit --os-cloud openstack --format markdown
Generate JSON output:
driftmux --openstack-sg-audit --os-cloud openstack --format json
Generate CSV output:
driftmux --openstack-sg-audit --os-cloud openstack --format csv
Output files
The OpenStack audit generates a dedicated report name:
reports/driftmux-openstack-neutron-security-groups.md
reports/driftmux-openstack-neutron-security-groups.json
reports/driftmux-openstack-neutron-security-groups.csv
This avoids mixing OpenStack findings with host-based scan reports.
Console output
The console output summarizes findings grouped by project.
Example:
OpenStack Neutron Security Group Audit
Checked rules: 3254
Findings: 340
Errors: 0
[project-a] total=12 critical=1 high=11
- CRITICAL sg=default ports=all src=0.0.0.0/0 match=all_ports used_by=3
- HIGH sg=k8s-master ports=22 src=0.0.0.0/0 match=22/SSH used_by=2
[project-b] total=2 critical=0 high=2
- HIGH sg=elasticsearch ports=9200 src=0.0.0.0/0 match=9200/Elasticsearch/OpenSearch used_by=1
Markdown report
The Markdown report groups findings by project:
# Driftmux report
## Driftmux OpenStack Neutron Security Group Audit
- Checked rules: 3254
- Findings: 340
- Errors: 0
### Project: datalab-users `project-id`
- Total findings: 12
- Critical: 1
- High: 11
- Medium: 0
- Low: 0
- Info: 0
| Severity | Security group | Protocol | Ports | Source | Match | Used by ports | Used by servers | Rule ID |
|---|---|---|---|---|---|---:|---:|---|
| CRITICAL | default | any | all | 0.0.0.0/0 | all_ports | 3 | 2 | rule-id |
| HIGH | ssh-public | tcp | 22 | 0.0.0.0/0 | 22/SSH | 1 | 1 | rule-id |
JSON output
Each finding is represented using the common Driftmux finding model.
Example:
{
"scanner": "openstack",
"host": "openstack:neutron",
"title": "OpenStack security group exposure: datalab/default exposes all ports to 0.0.0.0/0 (all_ports) [active, servers=2]",
"severity": "critical",
"description": "OpenStack Neutron security group rule exposes a critical port or port range to a public CIDR.",
"evidence": "rule_id=... sg=default project=datalab direction=ingress protocol=None ports=None-None remote_ip_prefix=0.0.0.0/0 used_by_ports=3 used_by_servers=2 exposure_status=active",
"confidence": "high",
"port": null,
"service": "neutron-security-group",
"metadata": {
"event_type": "openstack_security_group_exposure",
"project_id": "project-id",
"project_name": "datalab",
"security_group_id": "security-group-id",
"security_group_name": "default",
"remote_ip_prefix": "0.0.0.0/0",
"matched": [
"all_ports"
],
"used_by_count": 3,
"used_by_server_count": 2,
"exposure_status": "active"
}
}
Active and unused exposures
The OpenStack scanner distinguishes between two states:
active
unused
Active exposure
A finding is marked as active when the security group is attached to at
least one Neutron port.
Example:
SSH 22/tcp exposed to 0.0.0.0/0 and applied to one or more VM ports
Unused exposure
A finding is marked as unused when the rule exists but the security group
is not currently attached to any Neutron port.
Example:
SSH 22/tcp exposed to 0.0.0.0/0 but the security group is not used by any port
Unused exposures are still reported because they may become dangerous if the security group is later attached to a server.
Severity model
The default severity model is:
Condition |
Active SG |
Unused SG |
|---|---|---|
All ports exposed to |
Critical |
High |
Critical service exposed to |
High |
Medium |
HTTP/HTTPS only |
Info or ignored |
Info or ignored |
Examples:
0.0.0.0/0 -> all ports -> active -> CRITICAL
0.0.0.0/0 -> 22/tcp -> active -> HIGH
0.0.0.0/0 -> 22/tcp -> unused -> MEDIUM
0.0.0.0/0 -> 80/tcp -> ignored by default
0.0.0.0/0 -> 443/tcp -> ignored by default
Critical ports
By default, Driftmux checks for common administrative, database and infrastructure ports.
Port |
Service |
|---|---|
22 |
SSH |
3389 |
RDP |
3306 |
MySQL/MariaDB |
5432 |
PostgreSQL |
6379 |
Redis |
27017 |
MongoDB |
9200 |
Elasticsearch/OpenSearch |
5601 |
Kibana/OpenSearch Dashboards |
8080 |
HTTP alternative/Tomcat |
8443 |
HTTPS alternative |
9092 |
Kafka |
11211 |
Memcached |
2049 |
NFS |
445 |
SMB |
389 |
LDAP |
636 |
LDAPS |
2375 |
Docker API without TLS |
2376 |
Docker API with TLS |
6443 |
Kubernetes API |
10250 |
Kubelet |
5900-5999 |
VNC |
Security group usage enrichment
Driftmux tries to determine whether each affected security group is currently used.
It builds the following relationship:
security_group_rule
|
v
security_group
|
v
neutron port.security_group_ids
|
v
port.device_id
|
v
nova server
The scanner enriches findings with:
used_by_count
used_by_server_count
used_by_ports
used_by_servers
exposure_status
Example metadata:
{
"used_by_count": 2,
"used_by_server_count": 1,
"used_by_servers": [
{
"server_id": "server-id",
"server_name": "vm-01",
"status": "ACTIVE",
"project_id": "project-id"
}
],
"exposure_status": "active"
}
Important limitation
exposure_status=active means that the security group is attached to at
least one Neutron port.
It does not prove that traffic has actually reached the VM.
To know whether the exposed rule is receiving traffic, you need network telemetry such as:
Neutron security group logging
OVN/OVS flow logs
Suricata
Zeek
Firewall logs
Router logs