Skip to content

Commit fe84d55

Browse files
Added Azure.VM.PublicIPAttached (Azure#3012)
* Added Azure.VM.PublicIPAttached * Update changelog * Update docs/en/rules/Azure.VM.PublicIPAttached.md Co-authored-by: Bernie White <[email protected]> * Update docs/en/rules/Azure.VM.PublicIPAttached.md Co-authored-by: Bernie White <[email protected]> * Update docs/en/rules/Azure.VM.PublicIPAttached.md Co-authored-by: Bernie White <[email protected]> * Update docs/en/rules/Azure.VM.PublicIPAttached.md Co-authored-by: Bernie White <[email protected]> * Update docs/en/rules/Azure.VM.PublicIPAttached.md Co-authored-by: Bernie White <[email protected]> --------- Co-authored-by: Bernie White <[email protected]>
1 parent e4dedd8 commit fe84d55

File tree

5 files changed

+142
-0
lines changed

5 files changed

+142
-0
lines changed

docs/CHANGELOG-v1.md

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
2929

3030
## Unreleased
3131

32+
- New rules:
33+
- Virtual Machine:
34+
- Verify that virtual machines does not have public IPs attached by @BenjaminEngeset.
35+
[#11](https://github.com/Azure/PSRule.Rules.Azure/issues/11)
36+
3237
## v1.39.0-B0029 (pre-release)
3338

3439
What's changed since pre-release v1.39.0-B0009:
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
severity: Critical
3+
pillar: Security
4+
category: SE:06 Network controls
5+
resource: Virtual Machine
6+
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.VM.PublicIPAttached/
7+
---
8+
9+
# Public IPs attached
10+
11+
## SYNOPSIS
12+
13+
Avoid attaching public IPs directly to virtual machines.
14+
15+
## DESCRIPTION
16+
17+
Attaching a public IP address to a virtual machine network interface (NIC) exposes it directly to the Internet.
18+
This exposure can make the VM vulnerable to unauthorized inbound access and security compromise.
19+
Minimize the number of Internet ingress/ egress points to enhance security and reduces potential attack surfaces.
20+
21+
For enhanced security, consider one or more of the following options:
22+
23+
- **Secure remote access** &mdash; by RDP or SSH to virtual machines can be configured through Azure Bastion.
24+
- Azure Bastion provides a secure encrypted connection without exposing a public IP.
25+
- **Exposing web services** &mdash; by HTTP/S can be configured by App Gateway or Azure Front Door (AFD).
26+
- App Gateway and AFD provide a secure reverse proxy that supports web application firewall (WAF) filtering.
27+
- **Internet connectivity** &mdash; should be managed through a security hardened device such as Azure Firewall.
28+
- This option also allows additional controls to be applied for east/ west and north/ south traffic filtering.
29+
- Alternatively a Network Virtual Appliance (NVA) can used.
30+
31+
## RECOMMENDATION
32+
33+
Evaluate alternative methods for inbound access to virtual machines to enhance security and minimize risk.
34+
35+
### Configure with Azure template
36+
37+
To deploy VM network interfaces that pass this rule:
38+
39+
- For each IP configuration specified in the `properties.ipConfigurations` property:
40+
- Ensure that the `properties.publicIPAddress.id` property does not reference a Public IP resource.
41+
42+
For example:
43+
44+
```json
45+
{
46+
"type": "Microsoft.Network/networkInterfaces",
47+
"apiVersion": "2023-11-01",
48+
"name": "[parameters('nicName')]",
49+
"location": "[parameters('location')]",
50+
"properties": {
51+
"ipConfigurations": [
52+
{
53+
"name": "[parameters('ipConfig')]",
54+
"properties": {
55+
"privateIPAllocationMethod": "Dynamic",
56+
"subnet": {
57+
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]"
58+
}
59+
}
60+
}
61+
]
62+
}
63+
}
64+
```
65+
66+
### Configure with Bicep
67+
68+
To deploy VM network interfaces that pass this rule:
69+
70+
- For each IP configuration specified in the `properties.ipConfigurations` property:
71+
- Ensure that the `properties.publicIPAddress.id` property does not reference a Public IP resource.
72+
73+
For example:
74+
75+
```bicep
76+
resource nic 'Microsoft.Network/networkInterfaces@2023-11-01' = {
77+
name: nicName
78+
location: location
79+
properties: {
80+
ipConfigurations: [
81+
{
82+
name: ipconfig
83+
properties: {
84+
privateIPAllocationMethod: 'Dynamic'
85+
subnet: {
86+
id: resourceId('Microsoft.Network/virtualNetworks/subnets', virtualNetworkName, subnetName)
87+
}
88+
}
89+
}
90+
]
91+
}
92+
}
93+
```
94+
95+
## LINKS
96+
97+
- [SE:06 Network controls](https://learn.microsoft.com/azure/well-architected/security/networking)
98+
- [Plan for inbound and outbound internet connectivity](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/plan-for-inbound-and-outbound-internet-connectivity)
99+
- [Dissociate public IP address from a VM](https://learn.microsoft.com/azure/virtual-network/ip-services/remove-public-ip-address-vm)
100+
- [Azure Bastion](https://learn.microsoft.com/azure/bastion/bastion-overview)
101+
- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.network/networkinterfaces)

src/PSRule.Rules.Azure/en/PSRule-rules.psd1

+1
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,5 @@
118118
AppServiceAvailabilityZoneSKU = "The app service plan ({0}) is not deployed with a SKU that supports zone-redundancy."
119119
FirewallSubnetNAT = "The firewall should have a NAT gateway associated."
120120
PrivateSubnet = "The subnet ({0}) should disable default outbound access."
121+
VMPublicIPAttached = "The virtual machine with the NIC ({0}) should not have a public IP address attached."
121122
}

src/PSRule.Rules.Azure/rules/Azure.VM.Rule.ps1

+17
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,23 @@ Rule 'Azure.VM.MaintenanceConfig' -Ref 'AZR-000375' -Type 'Microsoft.Compute/vir
286286

287287
#endregion Maintenance Configuration
288288

289+
#region Public IP
290+
291+
# Synopsis: Avoid attaching public IPs directly to virtual machines.
292+
Rule 'Azure.VM.PublicIPAttached' -Ref 'AZR-000449' -Type 'Microsoft.Network/networkInterfaces' -Tag @{ release = 'GA'; ruleSet = '2024_09'; 'Azure.WAF/pillar' = 'Security'; } {
293+
$configurations = @($TargetObject.properties.ipConfigurations)
294+
295+
if ($configurations.Count -eq 0) {
296+
return $Assert.Pass()
297+
}
298+
299+
foreach ($config in $configurations) {
300+
$Assert.HasDefaultValue($config, 'properties.publicIPAddress.id', $null).Reason($LocalizedData.VMPublicIPAttached, $PSRule.TargetName)
301+
}
302+
}
303+
304+
#endregion Public IP
305+
289306
#region Helper functions
290307

291308
function global:HasPublisherMicrosoftSQLServer {

tests/PSRule.Rules.Azure.Tests/Azure.VM.Tests.ps1

+18
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ Describe 'Azure.VM' -Tag 'VM' {
588588
$ruleResult.Length | Should -Be 1;
589589
$ruleResult.TargetName | Should -BeIn 'vm-C';
590590
}
591+
592+
It 'Azure.VM.PublicIPAttached' {
593+
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.VM.PublicIPAttached' };
594+
595+
# Fail
596+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
597+
$ruleResult.Length | Should -Be 3;
598+
$ruleResult.TargetName | Should -Be 'nic-A', 'nic-B', 'nic-C';
599+
600+
$ruleResult[0].Reason | Should -BeExactly "The virtual machine with the NIC (nic-A) should not have a public IP address attached.";
601+
$ruleResult[1].Reason | Should -BeExactly "The virtual machine with the NIC (nic-B) should not have a public IP address attached.";
602+
$ruleResult[2].Reason | Should -BeExactly "The virtual machine with the NIC (nic-C) should not have a public IP address attached.";
603+
604+
# Pass
605+
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
606+
$ruleResult.Length | Should -Be 5;
607+
$ruleResult.TargetName | Should -Be 'aks-agentpool-00000000-nic-1', 'aks-agentpool-00000000-nic-2', 'aks-agentpool-00000000-nic-3', 'pe-001', 'private-link.nic.00000000-0000-0000-0000-000000000000';
608+
}
591609
}
592610

593611
Context 'Resource name - Azure.VM.Name' {

0 commit comments

Comments
 (0)