azure-postgres-mysql-public-firewall
PostgreSQL/MySQL Flexible Server with publicNetworkAccess Enabled and an internet-wide firewall rule exposes the DB endpoint to the public internet.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
[{'server': None}]
where
node_class(?server) == Data and node_type(?server) ==
RelationalDatabase
node_type(?server) matched provider_type in ['Microsoft.DBforPostgreSQL/flexibleServers', 'Microsoft.DBforMySQL/flexibleServers']
?server.properties.publicNetworkAccess == 'Enabled'
?server.properties.firewallRules contains at least one rule where startIpAddress is not in RFC1918/link-local space (i.e., startIpAddress is publicly routable) AND endIpAddress == '255.255.255.255' (full internet range) OR equivalent (e.g., rule covering 0.0.0.0–255.255.255.255)
emit
| source type | RelationalDatabase |
|---|---|
| target type | AnonymousIdentity |
| source | ?server |
| target | <AnonymousIdentity> |
| state logic | if ?server.properties.publicNetworkAccess == 'Disabled': DO NOT EMIT — private-access topology has no public endpoint regardless of firewallRules records (the service ignores firewall rules in that topology); elif firewallRules[] contains a rule with startIpAddress == '0.0.0.0' AND endIpAddress == '0.0.0.0' (Allow Azure services shorthand, not a public internet range): DO NOT EMIT as ExposedToInternet — only Azure backbone IPs are admitted, not arbitrary internet hosts; elif firewallRules[] contains a rule covering true public internet (startIpAddress outside RFC1918/link-local, endIpAddress == '255.255.255.255'): ACTIVE — any internet host can reach the DB port; else if firewallRules[] contains a rule with a broad public range (e.g., a /24 that includes significant public space): ACTIVE — meaningful public exposure. |
Narrative
{server.name} has publicNetworkAccess=Enabled and a firewall rule admitting the full internet (0.0.0.0–255.255.255.255 or equivalent public range). Any internet-connected host can reach the database engine endpoint (PostgreSQL port 5432 or MySQL port 3306). Valid credentials are still required for data access, but the network gate is absent - enabling credential spray, brute-force, or exploitation of DB engine vulnerabilities from the internet.
Raw rule rules/derived/azure/postgres-mysql.yaml
id: azure-postgres-mysql-public-firewall
emits: ExposedToInternet
description: PostgreSQL/MySQL Flexible Server with publicNetworkAccess Enabled and an internet-wide firewall
rule exposes the DB endpoint to the public internet.
match:
- - server: null
where:
- node_class(?server) == Data and node_type(?server) == RelationalDatabase
- node_type(?server) matched provider_type in ['Microsoft.DBforPostgreSQL/flexibleServers', 'Microsoft.DBforMySQL/flexibleServers']
- ?server.properties.publicNetworkAccess == 'Enabled'
- "?server.properties.firewallRules contains at least one rule where startIpAddress is not in RFC1918/link-local\
\ space (i.e., startIpAddress is publicly routable) AND endIpAddress == '255.255.255.255' (full internet\
\ range) OR equivalent (e.g., rule covering 0.0.0.0\u2013255.255.255.255)"
emit:
source_type: RelationalDatabase
target_type: AnonymousIdentity
source: ?server
target: <AnonymousIdentity>
permissions: []
conditions: []
state_logic: "if ?server.properties.publicNetworkAccess == 'Disabled': DO NOT EMIT \u2014 private-access\
\ topology has no public endpoint regardless of firewallRules records (the service ignores firewall\
\ rules in that topology); elif firewallRules[] contains a rule with startIpAddress == '0.0.0.0' AND\
\ endIpAddress == '0.0.0.0' (Allow Azure services shorthand, not a public internet range): DO NOT\
\ EMIT as ExposedToInternet \u2014 only Azure backbone IPs are admitted, not arbitrary internet hosts;\
\ elif firewallRules[] contains a rule covering true public internet (startIpAddress outside RFC1918/link-local,\
\ endIpAddress == '255.255.255.255'): ACTIVE \u2014 any internet host can reach the DB port; else\
\ if firewallRules[] contains a rule with a broad public range (e.g., a /24 that includes significant\
\ public space): ACTIVE \u2014 meaningful public exposure."
confidence: '0.93'
derived_from:
- 'collected ARM properties: publicNetworkAccess, firewallRules[]'
false_positive_note: "CRITICAL: publicNetworkAccess == Disabled means the server is VNet-injected with\
\ no public endpoint \u2014 do NOT emit regardless of firewallRules records (which are meaningless\
\ in that topology). The \"Allow Azure services\" shorthand (startIpAddress=0.0.0.0, endIpAddress=0.0.0.0)\
\ is frequently confused with an open internet rule \u2014 it admits only Azure backbone IPs and MUST\
\ NOT be emitted as ExposedToInternet. Only emit ACTIVE when the rule covers a genuine public internet\
\ range: endIpAddress == 255.255.255.255 combined with a public starting address is the canonical\
\ indicator. Do NOT emit if endIpAddress is a private range or a non-internet-routable block. ExposedToInternet\
\ here means the DB port is network-reachable from the internet \u2014 valid credentials are still\
\ required for data access. This edge resolves the CONDITIONAL(network_reachability) on azure-postgres-mysql-admin-reset-readdata:\
\ if both this edge and CanModifyConfiguration are ACTIVE, CanReadData upgrades to ACTIVE."
narrative: "{server.name} has publicNetworkAccess=Enabled and a firewall rule admitting the full internet\
\ (0.0.0.0\u2013255.255.255.255 or equivalent public range). Any internet-connected host can reach\
\ the database engine endpoint (PostgreSQL port 5432 or MySQL port 3306). Valid credentials are still\
\ required for data access, but the network gate is absent \u2014 enabling credential spray, brute-force,\
\ or exploitation of DB engine vulnerabilities from the internet."