github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/firewall/rule_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewall
     5  
     6  import (
     7  	"github.com/juju/testing"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/core/network"
    12  )
    13  
    14  var _ = gc.Suite(&IngressRuleSuite{})
    15  
    16  type IngressRuleSuite struct {
    17  	testing.IsolationSuite
    18  }
    19  
    20  func (IngressRuleSuite) TestRuleFormatting(c *gc.C) {
    21  	pr := network.MustParsePortRange("8080-9090/tcp")
    22  	r1 := NewIngressRule(pr)
    23  	c.Assert(r1.PortRange, gc.Equals, pr)
    24  	c.Assert(r1.SourceCIDRs, gc.HasLen, 0)
    25  	c.Assert(r1.String(), gc.Equals, "8080-9090/tcp")
    26  
    27  	r2 := NewIngressRule(pr, "10.0.0.0/24", "0.0.0.0/0", "0.0.0.0/0")
    28  	c.Assert(r2.PortRange, gc.Equals, pr)
    29  	c.Assert(r2.SourceCIDRs, gc.HasLen, 2, gc.Commentf("expected ingress rule not to contain duplicate CIDRs"))
    30  	c.Assert(r2.String(), gc.Equals, "8080-9090/tcp from 0.0.0.0/0,10.0.0.0/24")
    31  }
    32  
    33  func (IngressRuleSuite) TestRuleValidation(c *gc.C) {
    34  	bogus := network.PortRange{
    35  		Protocol: "gopher",
    36  		FromPort: 1,
    37  		ToPort:   1,
    38  	}
    39  	r1 := NewIngressRule(bogus)
    40  	c.Assert(r1.Validate(), gc.ErrorMatches, `.*invalid protocol "gopher", expected "tcp", "udp", or "icmp"`)
    41  
    42  	pr := network.MustParsePortRange("8080-9090/tcp")
    43  	r2 := NewIngressRule(pr, "bogus")
    44  	c.Assert(r2.Validate(), gc.ErrorMatches, ".*invalid CIDR address: bogus")
    45  
    46  	r3 := NewIngressRule(pr, "100.0.0.0/8")
    47  	c.Assert(r3.Validate(), jc.ErrorIsNil)
    48  }
    49  
    50  func (IngressRuleSuite) TestRuleEquality(c *gc.C) {
    51  	specs := []struct {
    52  		descr        string
    53  		ruleA, ruleB IngressRule
    54  		exp          bool
    55  	}{
    56  		{
    57  			descr: "same port and CIDRs",
    58  			ruleA: NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
    59  			ruleB: NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
    60  			exp:   true,
    61  		},
    62  		{
    63  			descr: "same port different CIDRs",
    64  			ruleA: NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.42.0/24"),
    65  			ruleB: NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
    66  			exp:   false,
    67  		},
    68  		{
    69  			descr: "different port same CIDRs",
    70  			ruleA: NewIngressRule(network.MustParsePortRange("90/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
    71  			ruleB: NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
    72  			exp:   false,
    73  		},
    74  	}
    75  
    76  	for specIndex, spec := range specs {
    77  		c.Logf("%d) %s", specIndex, spec.descr)
    78  		got := spec.ruleA.EqualTo(spec.ruleB)
    79  		c.Assert(got, gc.Equals, spec.exp)
    80  
    81  		got = spec.ruleB.EqualTo(spec.ruleA)
    82  		c.Assert(got, gc.Equals, spec.exp)
    83  	}
    84  }
    85  
    86  func (IngressRuleSuite) TestRuleSorting(c *gc.C) {
    87  	rules := IngressRules{
    88  		NewIngressRule(network.MustParsePortRange("10-100/udp"), "0.0.0.0/0", "192.168.1.0/24"),
    89  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "0.0.0.0/0", "192.168.1.0/24"),
    90  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0", "192.168.1.0/24"),
    91  		NewIngressRule(network.MustParsePortRange("80/tcp"), "0.0.0.0/0", "192.168.1.0/24"),
    92  		NewIngressRule(network.MustParsePortRange("80/tcp"), "0.0.0.0/0"),
    93  	}
    94  	rules.Sort()
    95  
    96  	exp := IngressRules{
    97  		NewIngressRule(network.MustParsePortRange("80/tcp"), "0.0.0.0/0"),
    98  		NewIngressRule(network.MustParsePortRange("80/tcp"), "0.0.0.0/0", "192.168.1.0/24"),
    99  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "0.0.0.0/0", "192.168.1.0/24"),
   100  		NewIngressRule(network.MustParsePortRange("10-100/udp"), "0.0.0.0/0", "192.168.1.0/24"),
   101  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0", "192.168.1.0/24"),
   102  	}
   103  
   104  	c.Assert(rules, gc.DeepEquals, exp)
   105  }
   106  
   107  func (IngressRuleSuite) TestRulesEquality(c *gc.C) {
   108  	setA := IngressRules{
   109  		NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   110  		NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
   111  	}
   112  	setB := IngressRules{
   113  		NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
   114  		NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   115  	}
   116  	setC := IngressRules{
   117  		NewIngressRule(network.MustParsePortRange("80/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
   118  		NewIngressRule(network.MustParsePortRange("90/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   119  	}
   120  
   121  	c.Assert(setA.EqualTo(setB), jc.IsTrue)
   122  	c.Assert(setA.EqualTo(setC), jc.IsFalse)
   123  	c.Assert(setB.EqualTo(setC), jc.IsFalse)
   124  }
   125  
   126  func (IngressRuleSuite) TestUniqueRules(c *gc.C) {
   127  	in := IngressRules{
   128  		NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   129  		NewIngressRule(network.MustParsePortRange("123/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
   130  		NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   131  	}
   132  
   133  	exp := IngressRules{
   134  		NewIngressRule(network.MustParsePortRange("80/tcp"), "10.0.0.0/24", "192.168.0.0/24"),
   135  		NewIngressRule(network.MustParsePortRange("123/tcp"), "192.168.0.0/24", "10.0.0.0/24"),
   136  	}
   137  
   138  	c.Assert(in.UniqueRules(), gc.DeepEquals, exp)
   139  }
   140  
   141  func (IngressRuleSuite) TestDiffOpenAll(c *gc.C) {
   142  	wanted := IngressRules{
   143  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "0.0.0.0/0"),
   144  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   145  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   146  	}
   147  	wanted.Sort()
   148  
   149  	toOpen, toClose := IngressRules{}.Diff(wanted)
   150  	c.Assert(toClose, gc.HasLen, 0)
   151  	c.Assert(toOpen, jc.DeepEquals, wanted)
   152  }
   153  
   154  func (IngressRuleSuite) TestDiffCloseAll(c *gc.C) {
   155  	current := IngressRules{
   156  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "0.0.0.0/0"),
   157  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   158  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   159  	}
   160  	current.Sort()
   161  
   162  	toOpen, toClose := current.Diff(nil)
   163  	c.Assert(toOpen, gc.HasLen, 0)
   164  	c.Assert(toClose, jc.DeepEquals, current)
   165  }
   166  
   167  func (IngressRuleSuite) TestDiffNoPortRangeOverlap(c *gc.C) {
   168  	current := IngressRules{
   169  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "0.0.0.0/0"),
   170  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   171  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   172  	}
   173  	extra := IngressRules{
   174  		NewIngressRule(network.MustParsePortRange("100-110/tcp"), "0.0.0.0/0"),
   175  		NewIngressRule(network.MustParsePortRange("8080/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   176  		NewIngressRule(network.MustParsePortRange("67/udp"), "0.0.0.0/0"),
   177  	}
   178  
   179  	wanted := append(current, extra...)
   180  	toOpen, toClose := current.Diff(wanted)
   181  	c.Assert(toClose, gc.HasLen, 0)
   182  
   183  	extra.Sort()
   184  	c.Assert(toOpen, jc.DeepEquals, extra)
   185  }
   186  
   187  func (IngressRuleSuite) TestPortRangeOverlapToOpen(c *gc.C) {
   188  	current := IngressRules{
   189  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "10.0.0.0/24"),
   190  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   191  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   192  	}
   193  	extra := IngressRules{
   194  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "192.168.1.0/24", "10.0.0.0/24"),
   195  		NewIngressRule(network.MustParsePortRange("8080/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   196  		NewIngressRule(network.MustParsePortRange("67/udp"), "0.0.0.0/0"),
   197  	}
   198  	wanted := append(current, extra...)
   199  	toOpen, toClose := current.Diff(wanted)
   200  	c.Assert(toClose, gc.HasLen, 0)
   201  
   202  	c.Assert(toOpen, jc.DeepEquals, IngressRules{
   203  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "192.168.1.0/24"),
   204  		NewIngressRule(network.MustParsePortRange("8080/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   205  		NewIngressRule(network.MustParsePortRange("67/udp"), "0.0.0.0/0"),
   206  	})
   207  }
   208  
   209  func (IngressRuleSuite) TestPortRangeOverlapToClose(c *gc.C) {
   210  	current := IngressRules{
   211  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   212  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   213  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   214  	}
   215  	wanted := IngressRules{
   216  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "10.0.0.0/24"),
   217  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   218  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   219  	}
   220  
   221  	toOpen, toClose := current.Diff(wanted)
   222  	c.Assert(toOpen, gc.HasLen, 0)
   223  
   224  	c.Assert(toClose, jc.DeepEquals, IngressRules{
   225  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "192.168.1.0/24"),
   226  	})
   227  }
   228  
   229  func (IngressRuleSuite) TestPortRangeOverlap(c *gc.C) {
   230  	current := IngressRules{
   231  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   232  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   233  	}
   234  	wanted := IngressRules{
   235  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "10.0.0.0/24"),
   236  		NewIngressRule(network.MustParsePortRange("443/tcp"), "10.0.0.0/24", "192.168.1.0/24"),
   237  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   238  	}
   239  
   240  	toOpen, toClose := current.Diff(wanted)
   241  	c.Assert(toOpen, jc.DeepEquals, IngressRules{
   242  		NewIngressRule(network.MustParsePortRange("80-90/udp"), "0.0.0.0/0"),
   243  	})
   244  	c.Assert(toClose, jc.DeepEquals, IngressRules{
   245  		NewIngressRule(network.MustParsePortRange("80-90/tcp"), "192.168.1.0/24"),
   246  	})
   247  }
   248  
   249  func (IngressRuleSuite) TestDiffRangesClosesPortsIfRulesAreDisjoint(c *gc.C) {
   250  	current := IngressRules{
   251  		NewIngressRule(network.MustParsePortRange("3306/tcp"), "35.187.158.35/32"),
   252  	}
   253  	wanted := IngressRules{
   254  		NewIngressRule(network.MustParsePortRange("3306/tcp"), "35.187.152.241/32"),
   255  	}
   256  
   257  	toOpen, toClose := current.Diff(wanted)
   258  	c.Assert(toOpen, gc.DeepEquals, wanted)
   259  	c.Assert(toClose, gc.DeepEquals, current)
   260  }
   261  
   262  func (IngressRuleSuite) TestRemoveCIDRsMatchingAddressType(c *gc.C) {
   263  	in := IngressRules{
   264  		NewIngressRule(network.MustParsePortRange("80/tcp"), "35.187.158.35/32"),
   265  		// We expect these rules to be de-dupped once the IPV6 CIDRs get removed
   266  		NewIngressRule(network.MustParsePortRange("81/tcp"), "35.187.1.35/32", "::/0"),
   267  		NewIngressRule(network.MustParsePortRange("81/tcp"), "35.187.1.35/32", "2002::1234:abcd:ffff:c0a8:101/64"),
   268  	}
   269  
   270  	out := in.RemoveCIDRsMatchingAddressType(network.IPv6Address)
   271  	c.Assert(out, gc.DeepEquals, IngressRules{
   272  		NewIngressRule(network.MustParsePortRange("80/tcp"), "35.187.158.35/32"),
   273  		NewIngressRule(network.MustParsePortRange("81/tcp"), "35.187.1.35/32"),
   274  	})
   275  }