Skip to content

Commit 4a1f54c

Browse files
committed
fix: disallow 0.0.0.0/0 in egress firewall cidr_list
The CloudStack API does not reject 0.0.0.0/0 as an egress firewall CIDR but silently replaces it with the network's own CIDR. This causes a permanent drift between the Terraform state (which stores 0.0.0.0/0) and the actual API state (which returns the network CIDR), resulting in Terraform perpetually planning to recreate the resource. Validate early in verifyEgressFirewallRuleParams to surface a clear error before the API call is made.
1 parent cd071af commit 4a1f54c

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

cloudstack/resource_cloudstack_egress_firewall.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ func deleteEgressFirewallRules(d *schema.ResourceData, meta interface{}, rules *
542542
return errs.ErrorOrNil()
543543
}
544544

545-
func deleteEgressFirewallRule(d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
545+
func deleteEgressFirewallRule(_ *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
546546
cs := meta.(*cloudstack.CloudStackClient)
547547
uuids := rule["uuids"].(map[string]interface{})
548548

@@ -589,7 +589,13 @@ func verifyEgressFirewallParams(d *schema.ResourceData) error {
589589
return nil
590590
}
591591

592-
func verifyEgressFirewallRuleParams(d *schema.ResourceData, rule map[string]interface{}) error {
592+
func verifyEgressFirewallRuleParams(_ *schema.ResourceData, rule map[string]interface{}) error {
593+
if cidrList, ok := rule["cidr_list"].(*schema.Set); ok {
594+
if cidrList.Contains("0.0.0.0/0") {
595+
return fmt.Errorf("CIDR 0.0.0.0/0 is not allowed in egress firewall rules. cidr_list must be within the network subnet")
596+
}
597+
}
598+
593599
protocol := rule["protocol"].(string)
594600
if strings.ToLower(protocol) != "all" && protocol != "tcp" && protocol != "udp" && protocol != "icmp" {
595601
return fmt.Errorf(

cloudstack/resource_cloudstack_egress_firewall_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,44 @@ import (
2525
"testing"
2626

2727
"github.com/apache/cloudstack-go/v2/cloudstack"
28+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2829
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
2930
"github.com/hashicorp/terraform-plugin-testing/terraform"
3031
)
3132

33+
func TestVerifyEgressFirewallRuleParams_disallowAnyIP(t *testing.T) {
34+
cidrSet := schema.NewSet(schema.HashString, []interface{}{"0.0.0.0/0"})
35+
rule := map[string]interface{}{
36+
"cidr_list": cidrSet,
37+
"protocol": "tcp",
38+
"ports": schema.NewSet(schema.HashString, []interface{}{"80"}),
39+
"uuids": map[string]interface{}{},
40+
}
41+
42+
err := verifyEgressFirewallRuleParams(nil, rule)
43+
if err == nil {
44+
t.Fatal("expected error for cidr 0.0.0.0/0, got nil")
45+
}
46+
if !strings.Contains(err.Error(), "0.0.0.0/0") {
47+
t.Fatalf("expected error message to mention 0.0.0.0/0, got: %s", err.Error())
48+
}
49+
}
50+
51+
func TestVerifyEgressFirewallRuleParams_allowValidCIDR(t *testing.T) {
52+
cidrSet := schema.NewSet(schema.HashString, []interface{}{"10.1.1.0/24"})
53+
rule := map[string]interface{}{
54+
"cidr_list": cidrSet,
55+
"protocol": "tcp",
56+
"ports": schema.NewSet(schema.HashString, []interface{}{"80"}),
57+
"uuids": map[string]interface{}{},
58+
}
59+
60+
err := verifyEgressFirewallRuleParams(nil, rule)
61+
if err != nil {
62+
t.Fatalf("expected no error for valid cidr, got: %s", err.Error())
63+
}
64+
}
65+
3266
func TestAccCloudStackEgressFirewall_basic(t *testing.T) {
3367
resource.Test(t, resource.TestCase{
3468
PreCheck: func() { testAccPreCheck(t) },

0 commit comments

Comments
 (0)