Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions compliance/frameworks/cis_azure_benchmark.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
"control_id": "6.4",
"control_name": "Ensure that Azure Firewall is enabled on Virtual Networks",
"description": "VNet peering connections with allowGatewayTransit or useRemoteGateways enabled allow traffic to route between network segments through shared gateways. This can break network segmentation and enable lateral movement between zones that should remain isolated. Peering connections should be reviewed and gateway transit disabled unless explicitly required and documented."
}
}
},
"AZ-NET-016": {
"control_id": "9.1",
"control_name": "Ensure that unassociated public IP addresses are removed",
"description": "Load balancers with no backend pool configured are either misconfigured or leftover resources from decommissioned workloads. They represent unnecessary cost and poor resource hygiene and should be removed or configured correctly."
}
}
}
5 changes: 5 additions & 0 deletions compliance/frameworks/iso27001.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
"control_id": "A.13.1.1",
"control_name": "Network controls",
"description": "VNet peering connections with gateway transit enabled allow traffic to flow between network segments through shared gateways, potentially bypassing network controls. Networks should be managed and controlled to protect information in systems and applications. Gateway transit on peering connections should be disabled unless explicitly required."
},
"AZ-NET-016": {
"control_id": "A.13.1.1",
"control_name": "Network controls",
"description": "Load balancers with no backend pool are unused resources. Unused network resources should be removed as part of regular network hygiene to maintain an accurate and minimal network topology."
}
}
}
5 changes: 5 additions & 0 deletions compliance/frameworks/nist_csf.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
"control_id": "PR.AC-5",
"control_name": "Network integrity is protected",
"description": "VNet peering with gateway transit enabled allows traffic to cross network boundaries through shared gateways, undermining network segmentation. PR.AC-5 requires that network integrity is protected. Disabling gateway transit on peering connections enforces boundary integrity between network zones."
},
"AZ-NET-016": {
"control_id": "CM-7",
"control_name": "Least Functionality",
"description": "Load balancers with no backend pool configured serve no function and represent unnecessary resources. Unused resources should be removed to maintain least functionality and reduce cost."
}
}
}
5 changes: 5 additions & 0 deletions compliance/frameworks/soc2.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@
"control_id": "CC6.6",
"control_name": "Restricts Access from Outside the Network Boundary",
"description": "VNet peering with allowGatewayTransit or useRemoteGateways enabled allows traffic to cross network boundaries through shared gateways, weakening the logical separation between network zones. CC6.6 requires that logical access from outside the network boundary is restricted and controlled. Gateway transit on peering connections should be disabled to enforce boundary separation."
},
"AZ-NET-016": {
"control_id": "CC8.1",
"control_name": "Change Management",
"description": "A load balancer with no backend pool configured is either misconfigured or a leftover resource from a decommissioned workload that was not properly cleaned up. CC8.1 requires that infrastructure is managed through formal change management and resource lifecycle procedures."
}
}
}
25 changes: 25 additions & 0 deletions playbooks/cli/fix_az_net_016.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
set -euo pipefail

RESOURCE_GROUP=$1
LB_NAME=$2

if [ -z "$RESOURCE_GROUP" ] || [ -z "$LB_NAME" ]; then
echo "Usage: $0 <resource-group> <lb-name>"
exit 1
fi

echo "Reviewing load balancer: $LB_NAME"
echo "This load balancer has no backend pool configured."
echo ""
echo "Option 1: Delete the load balancer if it is no longer required."
echo " az network lb delete --resource-group \"$RESOURCE_GROUP\" --name \"$LB_NAME\""
echo ""
echo "Option 2: Configure a backend pool if the load balancer is still needed."
az network lb address-pool create \
--resource-group "$RESOURCE_GROUP" \
--lb-name "$LB_NAME" \
--name "backendPool"

echo "Done. Backend pool created for load balancer: $LB_NAME"
echo "Note: Add virtual machine NICs or IP addresses to the backend pool to complete configuration."
8 changes: 8 additions & 0 deletions scanner/azure_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ def get_vnet_peerings(self, resource_group: str, vnet_name: str) -> List[Any]:
except Exception as exc:
logger.error("get_vnet_peerings(%s) failed: %s", vnet_name, exc)
return []
def get_load_balancers(self) -> List[Any]:
"""List all load balancers in the subscription."""
try:
client = NetworkManagementClient(self.credential, self.subscription_id)
return list(client.load_balancers.list_all())
except Exception as exc:
logger.error("get_load_balancers failed: %s", exc)
return []

# ------------------------------------------------------------------ #
# Compute #
Expand Down
53 changes: 53 additions & 0 deletions scanner/rules/az_net_016.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""AZ-NET-016: Load balancer with no backend pool configured."""
from typing import Any, Dict, List

RULE_ID = "AZ-NET-016"
RULE_NAME = "Load Balancer Has No Backend Pool Configured"
SEVERITY = "LOW"
CATEGORY = "Network"
FRAMEWORKS = {
"CIS": "9.1",
"NIST": "CM-7",
"ISO27001": "A.13.1.1",
"SOC2": "CC8.1"
}
DESCRIPTION = (
"The load balancer has no backend pool configured, meaning it is "
"either misconfigured or a leftover resource from a decommissioned "
"workload that was not properly cleaned up. Unused load balancers "
"represent unnecessary cost and poor resource hygiene, and may "
"indicate gaps in the organisation's change management process."
)
REMEDIATION = (
"Review the load balancer and either configure a backend pool with "
"the appropriate virtual machine instances, or delete the resource "
"if it is no longer required. Ensure decommissioning procedures "
"include removal of all associated networking resources."
)
PLAYBOOK = "playbooks/cli/fix_az_net_016.sh"


def scan(azure_client: Any, subscription_id: str) -> List[Dict[str, Any]]:
findings: List[Dict[str, Any]] = []
for lb in azure_client.get_load_balancers():
parsed = azure_client.parse_resource_id(lb.id)
resource_group = parsed["resource_group"]
backend_pools = getattr(lb, "backend_address_pools", []) or []
if not backend_pools:
findings.append({
"rule_id": RULE_ID,
"rule_name": RULE_NAME,
"severity": SEVERITY,
"category": CATEGORY,
"resource_id": lb.id,
"resource_name": lb.name,
"resource_type": "Microsoft.Network/loadBalancers",
"description": DESCRIPTION,
"remediation": REMEDIATION,
"playbook": PLAYBOOK,
"frameworks": FRAMEWORKS,
"metadata": {
"resource_group": resource_group
}
})
return findings
Loading