Skip to content

Commit

Permalink
feat: sql server rules (#13)
Browse files Browse the repository at this point in the history
* feat: sql server rules

* fix copypaste

* linter
  • Loading branch information
pregress authored Nov 5, 2024
1 parent 35128c0 commit d235132
Show file tree
Hide file tree
Showing 10 changed files with 705 additions and 0 deletions.
14 changes: 14 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@
| --- | --- | --- | --- | --- |
|azurerm_mssql_database_transparent_data_encryption_enabled|Enforce transparant data encryption|WARNING|||

## azurerm_mssql_server
|Name|Description|Severity|Enabled|Link|
| --- | --- | --- | --- | --- |
|azurerm_mssql_server_azuread_authentication_only |Only user Azure AD authentication to SQL |WARNING|||
|azurerm_mssql_server_public_network_access_enabled|Consider disabling public network access on SQL servers. |NOTICE|||
|azurerm_mssql_server_minimum_tls_version|Enforce TLS 1.2 on SQL servers. |WARNING|||

## azurerm_mssql_firewall_rule

|Name|Description|Severity|Enabled|Link|
| --- | --- | --- | --- | --- |
|azurerm_mssql_firewall_rule_all_allowed|Remove a firewall rule that allows the any ip.|ERROR|||


## azurerm_storage_account
|Name|Description|Severity|Enabled|Link|
| --- | --- | --- | --- | --- |
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func createRuleSet() *tflint.BuiltinRuleSet {
rules.NewAzurermLinuxWebAppHTTPSOnly(),
rules.NewAzurermLinuxWebAppMinimumTLSVersion(),
rules.NewAzurermMssqlDatabaseEncryption(),
rules.NewAzurermMsSQLFirewallRuleAllAllowed(),
rules.NewAzurermMsSQLServerAdAuthOnly(),
rules.NewAzurermMsSQLServerPublicNetworkAccessEnabled(),
rules.NewAzurermMsSQLServerUnsecureTLS(),
rules.NewAzurermStorageAccountPublicNetworkAccessEnabled(),
rules.NewAzurermStorageAccountUnsecureTLS(),
rules.NewAzurermWindowsFunctionAppFtpsState(),
Expand Down
96 changes: 96 additions & 0 deletions rules/azurerm_mssql_firewall_rule_all_allowed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package rules

import (
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
)

// AzurermMsSQLFirewallRuleAllAllowed checks if the firewall rule allows all IP addresses
type AzurermMsSQLFirewallRuleAllAllowed struct {
tflint.DefaultRule

resourceType string
startIPAttr string
endIPAttr string
}

// NewAzurermMsSQLFirewallRuleAllAllowed returns a new rule instance
func NewAzurermMsSQLFirewallRuleAllAllowed() *AzurermMsSQLFirewallRuleAllAllowed {
return &AzurermMsSQLFirewallRuleAllAllowed{
resourceType: "azurerm_mssql_firewall_rule",
startIPAttr: "start_ip_address",
endIPAttr: "end_ip_address",
}
}

// Name returns the rule name
func (r *AzurermMsSQLFirewallRuleAllAllowed) Name() string {
return "azurerm_mssql_firewall_rule_all_allowed"
}

// Enabled returns whether the rule is enabled by default
func (r *AzurermMsSQLFirewallRuleAllAllowed) Enabled() bool {
return true
}

// Severity returns the rule severity
func (r *AzurermMsSQLFirewallRuleAllAllowed) Severity() tflint.Severity {
return tflint.ERROR
}

// Link returns the rule reference link
func (r *AzurermMsSQLFirewallRuleAllAllowed) Link() string {
return ""
}

// Check checks if the firewall rule allows all IP addresses
func (r *AzurermMsSQLFirewallRuleAllAllowed) Check(runner tflint.Runner) error {
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
Attributes: []hclext.AttributeSchema{
{Name: r.startIPAttr},
{Name: r.endIPAttr},
},
}, nil)
if err != nil {
return err
}

for _, resource := range resources.Blocks {
startIP, exists := resource.Body.Attributes[r.startIPAttr]
if !exists {
continue
}

endIP, exists := resource.Body.Attributes[r.endIPAttr]
if !exists {
continue
}

var startIPValue, endIPValue string
err := runner.EvaluateExpr(startIP.Expr, func(val string) error {
startIPValue = val
return nil
}, nil)
if err != nil {
return err
}

err = runner.EvaluateExpr(endIP.Expr, func(val string) error {
endIPValue = val
return nil
}, nil)
if err != nil {
return err
}

if startIPValue == "0.0.0.0" && endIPValue == "255.255.255.255" {
runner.EmitIssue(
r,
"Firewall rule allows access from all IP addresses (0.0.0.0-255.255.255.255). Consider restricting the IP range for better security.",
resource.DefRange,
)
}
}

return nil
}
66 changes: 66 additions & 0 deletions rules/azurerm_mssql_firewall_rule_all_allowed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package rules

import (
"testing"

hcl "github.com/hashicorp/hcl/v2"
"github.com/terraform-linters/tflint-plugin-sdk/helper"
)

func Test_AzurermMsSQLFirewallRuleAllAllowed(t *testing.T) {
tests := []struct {
Name string
Content string
Expected helper.Issues
}{
{
Name: "all IPs allowed",
Content: `
resource "azurerm_mssql_firewall_rule" "example" {
start_ip_address = "0.0.0.0"
end_ip_address = "255.255.255.255"
}`,
Expected: helper.Issues{
{
Rule: NewAzurermMsSQLFirewallRuleAllAllowed(),
Message: "Firewall rule allows access from all IP addresses (0.0.0.0-255.255.255.255). Consider restricting the IP range for better security.",
Range: hcl.Range{
Filename: "resource.tf",
Start: hcl.Pos{Line: 2, Column: 1},
End: hcl.Pos{Line: 2, Column: 49},
},
},
},
},
{
Name: "specific IP range",
Content: `
resource "azurerm_mssql_firewall_rule" "example" {
start_ip_address = "10.0.0.0"
end_ip_address = "10.0.0.255"
}`,
Expected: helper.Issues{},
},
{
Name: "missing IP addresses",
Content: `
resource "azurerm_mssql_firewall_rule" "example" {
}`,
Expected: helper.Issues{},
},
}

rule := NewAzurermMsSQLFirewallRuleAllAllowed()

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
runner := helper.TestRunner(t, map[string]string{"resource.tf": test.Content})

if err := rule.Check(runner); err != nil {
t.Fatalf("Unexpected error occurred: %s", err)
}

helper.AssertIssues(t, test.Expected, runner.Issues)
})
}
}
105 changes: 105 additions & 0 deletions rules/azurerm_mssql_server_azuread_authentication_only.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package rules

import (
"fmt"
"strings"

"github.com/terraform-linters/tflint-plugin-sdk/hclext"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
)

// AzurermMsSQLServerAdAuthOnly checks that azuread_authentication_only is set to true
type AzurermMsSQLServerAdAuthOnly struct {
tflint.DefaultRule

resourceType string
attributePath []string
expectedValue string
}

// NewAzurermMsSQLServerAdAuthOnly returns a new rule instance
func NewAzurermMsSQLServerAdAuthOnly() *AzurermMsSQLServerAdAuthOnly {
return &AzurermMsSQLServerAdAuthOnly{
resourceType: "azurerm_mssql_server",
attributePath: []string{"azuread_administrator", "azuread_authentication_only"},
expectedValue: "true",
}
}

// Name returns the rule name
func (r *AzurermMsSQLServerAdAuthOnly) Name() string {
return "azurerm_mssql_server_azuread_authentication_only"
}

// Enabled returns whether the rule is enabled by default
func (r *AzurermMsSQLServerAdAuthOnly) Enabled() bool {
return true
}

// Severity returns the rule severity
func (r *AzurermMsSQLServerAdAuthOnly) Severity() tflint.Severity {
return tflint.WARNING
}

// Link returns the rule reference link
func (r *AzurermMsSQLServerAdAuthOnly) Link() string {
return ""
}

// Check verifies that azuread_authentication_only is set to "Disabled"
func (r *AzurermMsSQLServerAdAuthOnly) Check(runner tflint.Runner) error {
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
Blocks: []hclext.BlockSchema{
{
Type: "azuread_administrator",
Body: &hclext.BodySchema{
Attributes: []hclext.AttributeSchema{
{Name: "azuread_authentication_only"},
},
},
},
},
}, nil)
if err != nil {
return err
}

for _, resource := range resources.Blocks {
siteConfigBlocks := resource.Body.Blocks.OfType("azuread_administrator")
if len(siteConfigBlocks) == 0 {
runner.EmitIssue(
r,
"azuread_administrator block is missing, azuread_authentication_only should be set to true",
resource.DefRange,
)
continue
}

siteConfig := siteConfigBlocks[0]
attribute, exists := siteConfig.Body.Attributes["azuread_authentication_only"]
if !exists {
runner.EmitIssue(
r,
"azuread_authentication_only is missing in azuread_administrator, should be set to true",
siteConfig.DefRange,
)
continue
}

err := runner.EvaluateExpr(attribute.Expr, func(val string) error {
if !strings.EqualFold(val, r.expectedValue) {
runner.EmitIssue(
r,
fmt.Sprintf("azuread_authentication_only is set to %s, should be set to true", val),
attribute.Expr.Range(),
)
}
return nil
}, nil)
if err != nil {
return err
}
}

return nil
}
Loading

0 comments on commit d235132

Please sign in to comment.