From 02f0cac3ef2255b5a25dcf528d80c579afe17caa Mon Sep 17 00:00:00 2001 From: Preben Huybrechts <1079843+pregress@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:55:12 +0200 Subject: [PATCH] Feat: storage network rules --- README.md | 2 +- ...e_account_public_network_access_enabled.md | 18 +++++++- project/main.go | 2 +- ..._account_public_network_access_enabled.go} | 43 +++++++++++++++--- ...ount_public_network_access_enabled_test.go | 44 +++++++++++++++++-- 5 files changed, 98 insertions(+), 11 deletions(-) rename rules/{azurerm_storage_account_public_network_access_enabled .go => azurerm_storage_account_public_network_access_enabled.go} (64%) diff --git a/README.md b/README.md index e2557b2..9ef75e8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ You can install the plugin with `tflint --init`. Declare a config in `.tflint.hc plugin "azurerm-security" { enabled = true - version = "0.1.9" + version = "0.1.10" source = "github.com/pregress/tflint-ruleset-azurerm-security" } ``` diff --git a/docs/rules/azurerm_storage_account_public_network_access_enabled.md b/docs/rules/azurerm_storage_account_public_network_access_enabled.md index e591009..bf73fd0 100644 --- a/docs/rules/azurerm_storage_account_public_network_access_enabled.md +++ b/docs/rules/azurerm_storage_account_public_network_access_enabled.md @@ -13,16 +13,32 @@ resource "azurerm_storage_account" "example" { ## Why -Disabling public_network_access_enabled ensures the Storage Account is not accessible from the public internet, reducing exposure to potential security threats and limiting access to trusted, private networks only. +Storage accounts with unrestricted public network access expose your data to potential security threats. By either disabling public network access altogether or implementing network rules with "Deny" as the default action, you can significantly reduce your storage account's attack surface. ## How to Fix +Option 1: Disable public network access completely: + ```hcl resource "azurerm_storage_account" "example" { public_network_access_enabled = false } ``` +Option 2: Implement network rules with default action set to "Deny": + +```hcl +resource "azurerm_storage_account" "example" { + network_rules { + default_action = "Deny" + bypass = ["AzureServices"] + # Add specific IP rules or virtual network subnet IDs as needed + ip_rules = ["203.0.113.0/24"] + } +} +``` + +This configuration enables fine-grained access control, allowing connectivity only from specified IP addresses or virtual networks while blocking all other traffic. ## How to disable diff --git a/project/main.go b/project/main.go index dbfef4e..ae2913a 100644 --- a/project/main.go +++ b/project/main.go @@ -3,7 +3,7 @@ package project import "fmt" // Version is ruleset version -const Version string = "0.1.9" +const Version string = "0.1.10" // ReferenceLink returns the rule reference link func ReferenceLink(name string) string { diff --git a/rules/azurerm_storage_account_public_network_access_enabled .go b/rules/azurerm_storage_account_public_network_access_enabled.go similarity index 64% rename from rules/azurerm_storage_account_public_network_access_enabled .go rename to rules/azurerm_storage_account_public_network_access_enabled.go index 2e0a9af..2ab857c 100644 --- a/rules/azurerm_storage_account_public_network_access_enabled .go +++ b/rules/azurerm_storage_account_public_network_access_enabled.go @@ -3,7 +3,7 @@ package rules import ( "github.com/terraform-linters/tflint-plugin-sdk/hclext" "github.com/terraform-linters/tflint-plugin-sdk/tflint" - + "github.com/terraform-linters/tflint-ruleset-azurerm-security/project" ) @@ -49,18 +49,51 @@ func (r *AzurermStorageAccountPublicNetworkAccessEnabled) Check(runner tflint.Ru Attributes: []hclext.AttributeSchema{ {Name: r.attributeName}, }, + Blocks: []hclext.BlockSchema{ + { + Type: "network_rules", + Body: &hclext.BodySchema{ + Attributes: []hclext.AttributeSchema{ + {Name: "default_action"}, + }, + }, + }, + }, }, nil) if err != nil { return err } for _, resource := range resources.Blocks { + // Check for network_rules block with default_action = "Deny" + hasSecureNetworkRulesWithDeny := false + hasSecureNetworkRules := false + for _, block := range resource.Body.Blocks { + if block.Type == "network_rules" { + hasSecureNetworkRules = true + if defaultActionAttr, exists := block.Body.Attributes["default_action"]; exists { + var defaultAction string + if err := runner.EvaluateExpr(defaultActionAttr.Expr, &defaultAction, nil); err == nil { + if defaultAction == "Deny" { + hasSecureNetworkRulesWithDeny = true + break + } + } + } + } + } + + // If network rules with default_action = "Deny" exist, the configuration is secure + if hasSecureNetworkRulesWithDeny { + continue + } + attribute, exists := resource.Body.Attributes[r.attributeName] - if !exists { - // Emit an issue if the attribute does not exist + if !exists && !hasSecureNetworkRules { + // If the attribute does not exist and there are no secure network rules, emit an issue runner.EmitIssue( r, - "public_network_access_enabled is not defined and defaults to true, consider disabling it", + "public_network_access_enabled is not defined and defaults to true, consider disabling it or adding network_rules with default_action = \"Deny\"", resource.DefRange, ) continue @@ -70,7 +103,7 @@ func (r *AzurermStorageAccountPublicNetworkAccessEnabled) Check(runner tflint.Ru if val { runner.EmitIssue( r, - "Consider changing public_network_access_enabled to false", + "Consider changing public_network_access_enabled to false or add network_rules with default_action = \"Deny\"", attribute.Expr.Range(), ) } diff --git a/rules/azurerm_storage_account_public_network_access_enabled_test.go b/rules/azurerm_storage_account_public_network_access_enabled_test.go index 062a024..d7ccf4c 100644 --- a/rules/azurerm_storage_account_public_network_access_enabled_test.go +++ b/rules/azurerm_storage_account_public_network_access_enabled_test.go @@ -14,7 +14,7 @@ func Test_AzurermStorageAccountPublicNetworkAccessEnabled(t *testing.T) { Expected helper.Issues }{ { - Name: "public network access disabled", + Name: "public network access enabled", Content: ` resource "azurerm_storage_account" "example" { public_network_access_enabled = true @@ -22,7 +22,7 @@ resource "azurerm_storage_account" "example" { Expected: helper.Issues{ { Rule: NewAzurermStorageAccountPublicNetworkAccessEnabled(), - Message: "Consider changing public_network_access_enabled to false", + Message: "Consider changing public_network_access_enabled to false or add network_rules with default_action = \"Deny\"", Range: hcl.Range{ Filename: "resource.tf", Start: hcl.Pos{Line: 3, Column: 37}, @@ -39,7 +39,7 @@ resource "azurerm_storage_account" "example" { Expected: helper.Issues{ { Rule: NewAzurermStorageAccountPublicNetworkAccessEnabled(), - Message: "public_network_access_enabled is not defined and defaults to true, consider disabling it", + Message: "public_network_access_enabled is not defined and defaults to true, consider disabling it or adding network_rules with default_action = \"Deny\"", Range: hcl.Range{ Filename: "resource.tf", Start: hcl.Pos{Line: 2, Column: 1}, @@ -56,6 +56,44 @@ resource "azurerm_storage_account" "example" { }`, Expected: helper.Issues{}, }, + { + Name: "public network access enabled netork rules with default_action = Deny", + Content: ` +resource "azurerm_storage_account" "example" { + public_network_access_enabled = true + + network_rules { + default_action = "Deny" + bypass = ["AzureServices"] + ip_rules = ["1.1.1.1"] + } +}`, + Expected: helper.Issues{}, + }, + { + Name: "public network access enbled network rules with default_action = Allow", + Content: ` +resource "azurerm_storage_account" "example" { + public_network_access_enabled = true + + network_rules { + default_action = "Allow" + bypass = ["AzureServices"] + ip_rules = ["1.1.1.1"] + } +}`, + Expected: helper.Issues{ + { + Rule: NewAzurermStorageAccountPublicNetworkAccessEnabled(), + Message: "Consider changing public_network_access_enabled to false or add network_rules with default_action = \"Deny\"", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 3, Column: 37}, + End: hcl.Pos{Line: 3, Column: 41}, + }, + }, + }, + }, } rule := NewAzurermStorageAccountPublicNetworkAccessEnabled()