Skip to content

Commit

Permalink
feat(new): Added Azure.ACR.Firewall (#2434)
Browse files Browse the repository at this point in the history
* feat(new): Added Azure.ACR.Firewall

* Fixes

* Added feedback from code review

* Added feedback from code review

---------

Co-authored-by: Bernie White <[email protected]>
  • Loading branch information
BenjaminEngeset and BernieWhite authored Sep 27, 2023
1 parent d0d45cd commit 0c4ab11
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 20 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ What's changed since pre-release v1.30.0-B0080:

- New rules:
- Azure Container Registry:
- Check that Container Registries restricts network access by @BenjaminEngeset.
[#2423](https://github.com/Azure/PSRule.Rules.Azure/issues/2423)
- Check that Container Registries disables anonymous pull access by @BenjaminEngeset.
[#2422](https://github.com/Azure/PSRule.Rules.Azure/issues/2422)
- Engineering:
Expand Down
112 changes: 112 additions & 0 deletions docs/en/rules/Azure.ACR.Firewall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
severity: Important
pillar: Security
category: Application endpoints
resource: Container Registry
online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.ACR.Firewall/
---

# Restrict network access to container registries

## SYNOPSIS

Limit network access of container registries to only trusted clients.

## DESCRIPTION

Azure Container Registry (ACR) allows you to restrict network access to trusted clients and networks instead of any client.

Container registries using the Premium SKU can limit network access by setting firewall rules or using private endpoints.
Firewall and private endpoints are not supported when using the Basic or Standard SKU.

In general, network access should be restricted to harden against unauthorized access or exfiltration attempts.
However may not be required when publishing and distributing public container images to external parties.

## RECOMMENDATION

Consider restricting network access to trusted clients to harden against unauthorized access or exfiltration attempts.

## EXAMPLES

### Configure with Azure template

To deploy Azure Container Registries that pass this rule:

- Set the `properties.publicNetworkAccess` property to `Disabled`. OR
- Set the `properties.networkRuleSet.defaultAction` property to `Deny`.

For example:

```json
{
"type": "Microsoft.ContainerRegistry/registries",
"apiVersion": "2023-01-01-preview",
"name": "[parameters('registryName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Premium"
},
"properties": {
"publicNetworkAccess": "Enabled",
"networkRuleBypassOptions": "AzureServices",
"networkRuleSet": {
"defaultAction": "Deny",
"ipRules": [
{
"action": "Allow",
"value": "_PublicIPv4Address_"
}
]
}
}
}
```

### Configure with Bicep

To deploy Azure Container Registries that pass this rule:

- Set the `properties.publicNetworkAccess` property to `Disabled`. OR
- Set the `properties.networkRuleSet.defaultAction` property to `Deny`.

For example:

```bicep
resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
name: registryName
location: location
sku: {
name: 'Premium'
}
properties: {
publicNetworkAccess: 'Enabled'
networkRuleBypassOptions: 'AzureServices'
networkRuleSet: {
defaultAction: 'Deny'
ipRules: [
{
action: 'Allow'
value: '_PublicIPv4Address_'
}
]
}
}
}
```

## NOTES

Configuring firewall rules or using private endpoints is only available for the Premium SKU.

When used with Microsoft Defender for Containers, you must enable trusted Microsoft services for the vulnerability assessment feature to be able to scan the registry.

## LINKS

- [Best practices for endpoint security on Azure](https://learn.microsoft.com/azure/well-architected/security/design-network-endpoints)
- [Restrict access using private endpoint](https://learn.microsoft.com/azure/container-registry/container-registry-private-link)
- [Restrict access using firewall rules](https://learn.microsoft.com/azure/container-registry/container-registry-access-selected-networks)
- [Allow trusted services to securely access a network-restricted container registry](https://learn.microsoft.com/azure/container-registry/allow-access-trusted-services)
- [Vulnerability assessments for Azure with Microsoft Defender Vulnerability Management](https://learn.microsoft.com/azure/defender-for-cloud/agentless-container-registry-vulnerability-assessment)
- [Azure security baseline for Container Registry](https://learn.microsoft.com/security/benchmark/azure/baselines/container-registry-security-baseline)
- [NS-2: Secure cloud services with network controls](https://learn.microsoft.com/security/benchmark/azure/baselines/container-registry-security-baseline#ns-2-secure-cloud-services-with-network-controls)
- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.containerregistry/registries#registryproperties)
39 changes: 31 additions & 8 deletions src/PSRule.Rules.Azure/rules/Azure.ACR.Rule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,42 +152,65 @@ spec:
field: properties.anonymousPullEnabled
hasDefault: false

---
# Synopsis: Limit network access of container registries to only trusted clients.
apiVersion: github.com/microsoft/PSRule/v1
kind: Rule
metadata:
name: Azure.ACR.Firewall
ref: AZR-000402
tags:
release: GA
ruleSet: 2023_09
Azure.WAF/pillar: Security
labels:
Azure.MCSB.v1/control: 'NS-2'
spec:
with:
- Azure.ACR.IsPremiumSKU
condition:
anyOf:
- field: properties.publicNetworkAccess
equals: Disabled
- field: properties.networkRuleSet.defaultAction
equals: Deny

#endregion Rules

#region Selectors

---
# Synopsis: Azure Container Registries using a Premium SKU.
# Synopsis: Azure Container Registries using a Standard SKU.
apiVersion: github.com/microsoft/PSRule/v1
kind: Selector
metadata:
name: Azure.ACR.IsPremiumSKU
name: Azure.ACR.IsStandardSKU
spec:
if:
allOf:
- type: '.'
equals: Microsoft.ContainerRegistry/registries
- anyOf:
- field: sku.name
equals: Premium
equals: Standard
- field: sku.tier
equals: Premium
equals: Standard

---
# Synopsis: Azure Container Registries using a Standard SKU.
# Synopsis: Azure Container Registries using a Premium SKU.
apiVersion: github.com/microsoft/PSRule/v1
kind: Selector
metadata:
name: Azure.ACR.IsStandardSKU
name: Azure.ACR.IsPremiumSKU
spec:
if:
allOf:
- type: '.'
equals: Microsoft.ContainerRegistry/registries
- anyOf:
- field: sku.name
equals: Standard
equals: Premium
- field: sku.tier
equals: Standard
equals: Premium

#endregion Selectors
36 changes: 28 additions & 8 deletions tests/PSRule.Rules.Azure.Tests/Azure.ACR.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ Describe 'Azure.ACR' -Tag 'ACR' {
# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -BeIn 'registry-E';
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -BeIn 'registry-E', 'registry-I';

# None
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'None' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 8;
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-C', 'registry-F', 'registry-G', 'registry-H', 'registry-I', 'registry-J';
$ruleResult.Length | Should -Be 7;
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-C', 'registry-F', 'registry-G', 'registry-H', 'registry-J';
}

It 'Azure.ACR.Retention' {
Expand All @@ -117,8 +117,8 @@ Describe 'Azure.ACR' -Tag 'ACR' {
# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -BeIn 'registry-D', 'registry-J';
$ruleResult.Length | Should -Be 3;
$ruleResult.TargetName | Should -BeIn 'registry-D', 'registry-J', 'registry-I';
$ruleResult.Detail.Reason.Path | Should -BeIn 'Properties.policies.retentionPolicy.status';

# Pass
Expand All @@ -130,8 +130,8 @@ Describe 'Azure.ACR' -Tag 'ACR' {
# None
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'None' });
$ruleResult | Should -Not -BeNullOrEmpty;
$ruleResult.Length | Should -Be 7;
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-C', 'registry-F', 'registry-G', 'registry-H', 'registry-I';
$ruleResult.Length | Should -Be 6;
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-C', 'registry-F', 'registry-G', 'registry-H';
}

It 'Azure.ACR.Usage' {
Expand Down Expand Up @@ -258,6 +258,26 @@ Describe 'Azure.ACR' -Tag 'ACR' {
$ruleResult.Length | Should -Be 1;
$ruleResult.TargetName | Should -BeIn 'registry-A';
}

It 'Azure.ACR.Firewall' {
$filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.ACR.Firewall' };

# Fail
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' });
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -BeIn 'registry-D', 'registry-E';
$ruleResult.Detail.Reason.Path | Should -BeIn 'properties.publicNetworkAccess', 'properties.networkRuleSet.defaultAction';

# Pass
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' });
$ruleResult.Length | Should -Be 2;
$ruleResult.TargetName | Should -BeIn 'registry-I', 'registry-J';

# None
$ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'None' });
$ruleResult.Length | Should -Be 6;
$ruleResult.TargetName | Should -BeIn 'registry-A', 'registry-B', 'registry-C', 'registry-F', 'registry-G', 'registry-H';
}
}

Context 'Resource name' {
Expand Down
32 changes: 28 additions & 4 deletions tests/PSRule.Rules.Azure.Tests/Resources.ACR.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@
"Name": "registry-D",
"Properties": {
"loginServer": "registry-D.azurecr.io",
"adminUserEnabled": true
"adminUserEnabled": true,
"publicNetworkAccess": "Enabled"
},
"ResourceGroupName": "test-rg",
"Type": "Microsoft.ContainerRegistry/registries",
Expand Down Expand Up @@ -379,6 +380,17 @@
"days": 180,
"status": "enabled"
}
},
"publicNetworkAccess": "Enabled",
"networkRuleBypassOptions": "AzureServices",
"networkRuleSet": {
"defaultAction": "Allow",
"ipRules": [
{
"action": "Allow",
"value": "_IPv4Address_"
}
]
}
},
"ResourceGroupName": "test-rg",
Expand Down Expand Up @@ -655,14 +667,15 @@
"retentionDays": 90,
"status": "enabled"
}
}
},
"publicNetworkAccess": "Disabled"
},
"ResourceGroupName": "test-rg",
"Type": "Microsoft.ContainerRegistry/registries",
"ResourceType": "Microsoft.ContainerRegistry/registries",
"Sku": {
"Name": "Standard",
"Tier": "Standard",
"Name": "Premium",
"Tier": "Premium",
"Size": null,
"Family": null,
"Model": null,
Expand Down Expand Up @@ -699,6 +712,17 @@
"retentionDays": 90,
"status": "enabled"
}
},
"publicNetworkAccess": "Enabled",
"networkRuleBypassOptions": "AzureServices",
"networkRuleSet": {
"defaultAction": "Deny",
"ipRules": [
{
"action": "Allow",
"value": "_IPv4Address_"
}
]
}
},
"ResourceGroupName": "test-rg",
Expand Down

0 comments on commit 0c4ab11

Please sign in to comment.