# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # E2E Test: Network/NetworkSecurityGroup_SSHAccess_Audit # Real Azure Policy: "[Deprecated]: SSH access from the Internet should be blocked" # Source: regolator/policyDefinitions/Network/NetworkSecurityGroup_SSHAccess_Audit.json # # Features exercised: # - Deeply nested template expressions: if(and(not(empty(...)), contains(...))) # - Arithmetic in templates: int(), split(), first(), last() # - Port range parsing: lessOrEquals/greaterOrEquals on split results # - count with where clause + template expression # - Double negation pattern: not { field notEquals "x" } # - Parameterized effect with defaultValue # - Multiple anyOf branches (destination port + source address) aliases: test_aliases.json policy_definition: | { "properties": { "displayName": "[Deprecated]: SSH access from the Internet should be blocked", "policyType": "BuiltIn", "mode": "All", "parameters": { "effect": { "type": "string", "defaultValue": "Audit", "allowedValues": ["Audit", "Disabled"] } }, "policyRule": { "if": { "allOf": [ { "field": "type", "equals": "Microsoft.Network/networkSecurityGroups/securityRules" }, { "allOf": [ { "field": "Microsoft.Network/networkSecurityGroups/securityRules/access", "equals": "Allow" }, { "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction", "equals": "Inbound" }, { "anyOf": [ { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange", "equals": "*" }, { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange", "equals": "22" }, { "value": "[if(and(not(empty(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'))), contains(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'),'-')), and(lessOrEquals(int(first(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22),greaterOrEquals(int(last(split(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange'), '-'))),22)), 'false')]", "equals": "true" }, { "count": { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]", "where": { "value": "[if(and(not(empty(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')))), contains(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')),'-')), and(lessOrEquals(int(first(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22),greaterOrEquals(int(last(split(first(field('Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]')), '-'))),22)) , 'false')]", "equals": "true" } }, "greater": 0 }, { "not": { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]", "notEquals": "*" } }, { "not": { "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRanges[*]", "notEquals": "22" } } ] }, { "anyOf": [ { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix", "equals": "*" }, { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix", "equals": "Internet" }, { "not": { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]", "notEquals": "*" } }, { "not": { "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefixes[*]", "notEquals": "Internet" } } ] } ] } ] }, "then": { "effect": "[parameters('effect')]" } } } } cases: # ========================================================================= # Exact port 22 match # ========================================================================= - note: audit_exact_port_22_from_internet resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "allow-ssh" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" sourceAddressPrefix: "Internet" want_effect: "Audit" - note: audit_exact_port_22_from_wildcard resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "allow-ssh-any" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" sourceAddressPrefix: "*" want_effect: "Audit" # ========================================================================= # Wildcard port (*) # ========================================================================= - note: audit_wildcard_port_from_internet resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "allow-all" properties: access: "Allow" direction: "Inbound" destinationPortRange: "*" sourceAddressPrefix: "Internet" want_effect: "Audit" # ========================================================================= # Port range containing 22 # ========================================================================= - note: audit_port_range_includes_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "allow-ssh-range" properties: access: "Allow" direction: "Inbound" destinationPortRange: "20-25" sourceAddressPrefix: "*" want_effect: "Audit" - note: audit_port_range_exact_22_to_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "exactly-22" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22-22" sourceAddressPrefix: "Internet" want_effect: "Audit" - note: audit_port_range_1_to_1024 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "low-ports" properties: access: "Allow" direction: "Inbound" destinationPortRange: "1-1024" sourceAddressPrefix: "*" want_effect: "Audit" # ========================================================================= # Port range NOT containing 22 # ========================================================================= - note: pass_port_range_excludes_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "http-only" properties: access: "Allow" direction: "Inbound" destinationPortRange: "80-443" destinationPortRanges: [] sourceAddressPrefix: "Internet" sourceAddressPrefixes: [] want_undefined: true - note: pass_port_23_only resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "port-23" properties: access: "Allow" direction: "Inbound" destinationPortRange: "23" destinationPortRanges: [] sourceAddressPrefix: "*" sourceAddressPrefixes: [] want_undefined: true # ========================================================================= # destinationPortRanges[*] array — double negation pattern # ========================================================================= - note: audit_port_ranges_array_contains_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "multi-port-ssh" properties: access: "Allow" direction: "Inbound" destinationPortRanges: - "80" - "22" - "443" sourceAddressPrefix: "Internet" want_effect: "Audit" - note: audit_port_ranges_array_contains_wildcard resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "multi-port-wildcard" properties: access: "Allow" direction: "Inbound" destinationPortRanges: - "*" sourceAddressPrefix: "*" want_effect: "Audit" - note: audit_port_ranges_array_with_range_containing_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "multi-port-range" properties: access: "Allow" direction: "Inbound" destinationPortRanges: - "80-443" - "10-30" sourceAddressPrefix: "Internet" want_effect: "Audit" - note: pass_port_ranges_array_no_22 resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "non-ssh-ports" properties: access: "Allow" direction: "Inbound" destinationPortRanges: - "80" - "443" - "8080" sourceAddressPrefix: "Internet" want_undefined: true # ========================================================================= # Source address variations # ========================================================================= - note: audit_source_prefixes_array_wildcard resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "src-wild-array" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" sourceAddressPrefixes: - "*" want_effect: "Audit" - note: audit_source_prefixes_array_internet resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "src-inet-array" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" sourceAddressPrefixes: - "Internet" want_effect: "Audit" - note: pass_source_is_private_subnet resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "private-ssh" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" destinationPortRanges: [] sourceAddressPrefix: "10.0.0.0/8" sourceAddressPrefixes: [] want_undefined: true - note: pass_source_prefixes_all_private resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "private-array" properties: access: "Allow" direction: "Inbound" destinationPortRange: "22" destinationPortRanges: [] sourceAddressPrefixes: - "10.0.0.0/8" - "172.16.0.0/12" want_undefined: true # ========================================================================= # Non-matching access / direction # ========================================================================= - note: pass_deny_rule resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "deny-ssh" properties: access: "Deny" direction: "Inbound" destinationPortRange: "22" sourceAddressPrefix: "*" want_undefined: true - note: pass_outbound_rule resource: type: "Microsoft.Network/networkSecurityGroups/securityRules" name: "outbound-ssh" properties: access: "Allow" direction: "Outbound" destinationPortRange: "22" sourceAddressPrefix: "*" want_undefined: true # ========================================================================= # Wrong resource type # ========================================================================= - note: pass_wrong_type resource: type: "Microsoft.Storage/storageAccounts" name: "not-nsg" properties: access: "Allow" want_undefined: true