Skip to content

Commit 801faf8

Browse files
Extend SG API to have tiers, larger priority range, and localaddress and port matching
1 parent 830362a commit 801faf8

File tree

8 files changed

+158
-70
lines changed

8 files changed

+158
-70
lines changed

charts/kube-ovn/templates/kube-ovn-crd.yaml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,7 +3507,13 @@ spec:
35073507
max: 65535
35083508
policy:
35093509
type: string
3510-
description: Policy action (allow or deny)
3510+
description: Policy action (allow, deny, or pass)
3511+
localAddress:
3512+
type: string
3513+
localPortRangeMin:
3514+
type: integer
3515+
localPortRangeMax:
3516+
type: integer
35113517
egressRules:
35123518
type: array
35133519
description: Egress traffic rules for the security group
@@ -3546,10 +3552,20 @@ spec:
35463552
max: 65535
35473553
policy:
35483554
type: string
3549-
description: Policy action (allow or deny)
3555+
description: Policy action (allow, deny, or pass)
3556+
localAddress:
3557+
type: string
3558+
localPortRangeMin:
3559+
type: integer
3560+
localPortRangeMax:
3561+
type: integer
35503562
allowSameGroupTraffic:
35513563
type: boolean
3552-
description: Allow traffic between pods in the same security group
3564+
securityGroupTier:
3565+
type: integer
3566+
default: 2
3567+
minimum: 2
3568+
maximum: 3
35533569
status:
35543570
type: object
35553571
properties:

mocks/pkg/ovs/interface.go

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/kubeovn/v1/security-group.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type SgPolicy string
3131
var (
3232
SgPolicyAllow = SgPolicy(ovnnb.ACLActionAllow)
3333
SgPolicyDrop = SgPolicy(ovnnb.ACLActionDrop)
34+
// Pass ACL processing to next tier
35+
SgPolicyPass = SgPolicy(ovnnb.ACLActionPass)
3436
)
3537

3638
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -57,6 +59,7 @@ type SecurityGroupSpec struct {
5759
IngressRules []SecurityGroupRule `json:"ingressRules,omitempty"`
5860
EgressRules []SecurityGroupRule `json:"egressRules,omitempty"`
5961
AllowSameGroupTraffic bool `json:"allowSameGroupTraffic,omitempty"`
62+
SecurityGroupTier int `json:"securityGroupTier,omitempty"`
6063
}
6164

6265
type SecurityGroupRule struct {
@@ -68,6 +71,9 @@ type SecurityGroupRule struct {
6871
RemoteSecurityGroup string `json:"remoteSecurityGroup,omitempty"`
6972
PortRangeMin int `json:"portRangeMin,omitempty"`
7073
PortRangeMax int `json:"portRangeMax,omitempty"`
74+
LocalAddress string `json:"localAddress,omitempty"`
75+
LocalPortRangeMin int `json:"localPortRangeMin,omitempty"`
76+
LocalPortRangeMax int `json:"localPortRangeMax,omitempty"`
7177
Policy SgPolicy `json:"policy"`
7278
}
7379

pkg/controller/security_group.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,14 @@ func (c *Controller) initDefaultDenyAllSecurityGroup() error {
7272
return err
7373
}
7474

75-
if err := c.OVNNbClient.CreateSgDenyAllACL(util.DenyAllSecurityGroup); err != nil {
76-
klog.Errorf("create deny all acl for sg %s: %v", util.DenyAllSecurityGroup, err)
77-
return err
75+
// Add default deny rules for all the tiers. This is to ensure that if a packet
76+
// is moved between tiers during acl evaluation, it is always dropped if no explicit
77+
// allow or drop rule is not hit.
78+
for tier := util.SecurityGroupTierMinimum; tier <= util.SecurityGroupTierMaximum; tier++ {
79+
if err := c.OVNNbClient.CreateSgDenyAllACL(util.DenyAllSecurityGroup, tier); err != nil {
80+
klog.Errorf("create deny all acl for sg %s tier %d: %v", util.DenyAllSecurityGroup, tier, err)
81+
return err
82+
}
7883
}
7984

8085
c.addOrUpdateSgQueue.Add(util.DenyAllSecurityGroup)
@@ -229,7 +234,7 @@ func (c *Controller) handleAddOrUpdateSg(key string, force bool) error {
229234
return err
230235
}
231236

232-
if err := c.OVNNbClient.CreateSgBaseACL(sg.Name, ovnnb.ACLDirectionToLport); err != nil {
237+
if err := c.OVNNbClient.CreateSgBaseACL(sg.Name, ovnnb.ACLDirectionToLport, sg.Spec.SecurityGroupTier); err != nil {
233238
klog.Error(err)
234239
return err
235240
}
@@ -245,7 +250,7 @@ func (c *Controller) handleAddOrUpdateSg(key string, force bool) error {
245250
return err
246251
}
247252

248-
if err := c.OVNNbClient.CreateSgBaseACL(sg.Name, ovnnb.ACLDirectionFromLport); err != nil {
253+
if err := c.OVNNbClient.CreateSgBaseACL(sg.Name, ovnnb.ACLDirectionFromLport, sg.Spec.SecurityGroupTier); err != nil {
249254
klog.Error(err)
250255
return err
251256
}
@@ -266,13 +271,21 @@ func (c *Controller) handleAddOrUpdateSg(key string, force bool) error {
266271
func (c *Controller) validateSgRule(sg *kubeovnv1.SecurityGroup) error {
267272
// check sg rules
268273
allRules := append(sg.Spec.IngressRules, sg.Spec.EgressRules...)
274+
if sg.Spec.SecurityGroupTier < util.SecurityGroupTierMinimum || sg.Spec.SecurityGroupTier > util.SecurityGroupTierMaximum {
275+
return fmt.Errorf("tier '%d' is not in the range [%d,%d]", sg.Spec.SecurityGroupTier, util.SecurityGroupTierMinimum, util.SecurityGroupTierMaximum)
276+
}
277+
269278
for _, rule := range allRules {
270279
if rule.IPVersion != "ipv4" && rule.IPVersion != "ipv6" {
271280
return errors.New("IPVersion should be 'ipv4' or 'ipv6'")
272281
}
273282

274-
if rule.Priority < 1 || rule.Priority > 200 {
275-
return fmt.Errorf("priority '%d' is not in the range of 1 to 200", rule.Priority)
283+
if rule.Priority < util.SecurityGroupPriorityMin || rule.Priority > util.SecurityGroupPriorityMax {
284+
return fmt.Errorf("priority '%d' is not in the range of %d to %d", rule.Priority, util.SecurityGroupPriorityMin, util.SecurityGroupPriorityMax)
285+
}
286+
287+
if sg.Spec.SecurityGroupTier == util.SecurityGroupTierMaximum && rule.Policy == kubeovnv1.SgPolicyPass {
288+
return fmt.Errorf("policy pass not valid when the security group tier is maximum [%d]", util.SecurityGroupTierMaximum)
276289
}
277290

278291
switch rule.RemoteType {
@@ -295,13 +308,33 @@ func (c *Controller) validateSgRule(sg *kubeovnv1.SecurityGroup) error {
295308
return fmt.Errorf("not support sgRemoteType '%s'", rule.RemoteType)
296309
}
297310

311+
if rule.LocalAddress != "" {
312+
if strings.Contains(rule.LocalAddress, "/") {
313+
if _, _, err := net.ParseCIDR(rule.LocalAddress); err != nil {
314+
return fmt.Errorf("invalid CIDR '%s'", rule.LocalAddress)
315+
}
316+
} else {
317+
if net.ParseIP(rule.LocalAddress) == nil {
318+
return fmt.Errorf("invalid ip address '%s'", rule.LocalAddress)
319+
}
320+
}
321+
}
322+
298323
if rule.Protocol == kubeovnv1.SgProtocolTCP || rule.Protocol == kubeovnv1.SgProtocolUDP {
299324
if rule.PortRangeMin < 1 || rule.PortRangeMin > 65535 || rule.PortRangeMax < 1 || rule.PortRangeMax > 65535 {
300325
return errors.New("portRange is out of range")
301326
}
302327
if rule.PortRangeMin > rule.PortRangeMax {
303328
return errors.New("portRange err, range Minimum value greater than maximum value")
304329
}
330+
if rule.LocalAddress != "" {
331+
if rule.LocalPortRangeMin < 1 || rule.LocalPortRangeMin > 65535 || rule.LocalPortRangeMax < 1 || rule.LocalPortRangeMax > 65535 {
332+
return errors.New("portRange is out of range")
333+
}
334+
if rule.LocalPortRangeMin > rule.LocalPortRangeMax {
335+
return errors.New("portRange err, range Minimum value greater than maximum value")
336+
}
337+
}
305338
}
306339
}
307340
return nil

pkg/ovs/interface.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ type ACL interface {
173173
UpdateEgressACLOps(pgName, asEgressName, asExceptName, protocol, aclName string, npp []netv1.NetworkPolicyPort, logEnable bool, logACLActions []ovnnb.ACLAction, logRate int, namedPortMap map[string]*util.NamedPortInfo) ([]ovsdb.Operation, error)
174174
CreateGatewayACL(lsName, pgName string) error
175175
CreateNodeACL(pgName, nodeIPStr, joinIPStr string) error
176-
CreateSgDenyAllACL(sgName string) error
177-
CreateSgBaseACL(sgName, direction string) error
176+
CreateSgDenyAllACL(sgName string, tier int) error
177+
CreateSgBaseACL(sgName, direction string, tier int) error
178178
UpdateSgACL(sg *kubeovnv1.SecurityGroup, direction string) error
179179
UpdateLogicalSwitchACL(lsName, cidrBlock string, subnetAcls []kubeovnv1.ACL, allowEWTraffic bool) error
180180
SetACLLog(pgName string, logEnable, isIngress bool) error

pkg/ovs/ovn-nb-acl.go

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -385,16 +385,16 @@ func (c *OVNNbClient) CreateNodeACL(pgName, nodeIPStr, joinIPStr string) error {
385385
return nil
386386
}
387387

388-
func (c *OVNNbClient) CreateSgDenyAllACL(sgName string) error {
388+
func (c *OVNNbClient) CreateSgDenyAllACL(sgName string, tier int) error {
389389
pgName := GetSgPortGroupName(sgName)
390390

391-
ingressACL, err := c.newACL(pgName, ovnnb.ACLDirectionToLport, util.SecurityGroupDropPriority, fmt.Sprintf("outport == @%s && ip", pgName), ovnnb.ACLActionDrop, util.NetpolACLTier)
391+
ingressACL, err := c.newACL(pgName, ovnnb.ACLDirectionToLport, util.SecurityGroupDropPriority, fmt.Sprintf("outport == @%s && ip", pgName), ovnnb.ACLActionDrop, tier)
392392
if err != nil {
393393
klog.Error(err)
394394
return fmt.Errorf("new deny all ingress acl for security group %s: %w", sgName, err)
395395
}
396396

397-
egressACL, err := c.newACL(pgName, ovnnb.ACLDirectionFromLport, util.SecurityGroupDropPriority, fmt.Sprintf("inport == @%s && ip", pgName), ovnnb.ACLActionDrop, util.NetpolACLTier)
397+
egressACL, err := c.newACL(pgName, ovnnb.ACLDirectionFromLport, util.SecurityGroupDropPriority, fmt.Sprintf("inport == @%s && ip", pgName), ovnnb.ACLActionDrop, tier)
398398
if err != nil {
399399
klog.Error(err)
400400
return fmt.Errorf("new deny all egress acl for security group %s: %w", sgName, err)
@@ -410,7 +410,7 @@ func (c *OVNNbClient) CreateSgDenyAllACL(sgName string) error {
410410
}
411411

412412
// CreateSgACL create allow acl for security group
413-
func (c *OVNNbClient) CreateSgBaseACL(sgName, direction string) error {
413+
func (c *OVNNbClient) CreateSgBaseACL(sgName, direction string, tier int) error {
414414
pgName := GetSgPortGroupName(sgName)
415415

416416
// ingress rule
@@ -434,7 +434,7 @@ func (c *OVNNbClient) CreateSgBaseACL(sgName, direction string) error {
434434
acls := make([]*ovnnb.ACL, 0)
435435

436436
newACL := func(match string) {
437-
acl, err := c.newACL(pgName, direction, util.SecurityGroupBasePriority, match, ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
437+
acl, err := c.newACL(pgName, direction, util.SecurityGroupBasePriority, match, ovnnb.ACLActionAllowRelated, tier)
438438
if err != nil {
439439
klog.Error(err)
440440
klog.Errorf("failed to create new base ingress acl for security group %s: %v", sgName, err)
@@ -523,7 +523,7 @@ func (c *OVNNbClient) UpdateSgACL(sg *kubeovnv1.SecurityGroup, direction string)
523523
NewACLMatch(ipSuffix, "", "", ""),
524524
NewACLMatch(ipSuffix+"."+srcOrDst, "==", "$"+asName, ""),
525525
)
526-
acl, err := c.newACL(pgName, direction, util.SecurityGroupAllowPriority, match.String(), ovnnb.ACLActionAllowRelated, util.NetpolACLTier)
526+
acl, err := c.newACL(pgName, direction, util.SecurityGroupAllowPriority, match.String(), ovnnb.ACLActionAllowRelated, sg.Spec.SecurityGroupTier)
527527
if err != nil {
528528
klog.Error(err)
529529
return fmt.Errorf("new allow acl for security group %s: %w", sg.Name, err)
@@ -535,7 +535,7 @@ func (c *OVNNbClient) UpdateSgACL(sg *kubeovnv1.SecurityGroup, direction string)
535535

536536
/* create rule acl */
537537
for _, rule := range sgRules {
538-
acl, err := c.newSgRuleACL(sg.Name, direction, rule)
538+
acl, err := c.newSgRuleACL(sg.Name, direction, rule, sg.Spec.SecurityGroupTier)
539539
if err != nil {
540540
klog.Error(err)
541541
return fmt.Errorf("new rule acl for security group %s: %w", sg.Name, err)
@@ -1050,7 +1050,7 @@ func (c *OVNNbClient) newACLWithoutCheck(parent, direction, priority, match, act
10501050
}
10511051

10521052
// createSgRuleACL create security group rule acl
1053-
func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.SecurityGroupRule) (*ovnnb.ACL, error) {
1053+
func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.SecurityGroupRule, tier int) (*ovnnb.ACL, error) {
10541054
ipSuffix := "ip4"
10551055
if rule.IPVersion == "ipv6" {
10561056
ipSuffix = "ip6"
@@ -1059,13 +1059,15 @@ func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.Secu
10591059
pgName := GetSgPortGroupName(sgName)
10601060

10611061
// ingress rule
1062-
srcOrDst, portDirection := "src", "outport"
1062+
localSrcOrDst, remoteSrcOrDst, portDirection := "dst", "src", "outport"
10631063
if direction == ovnnb.ACLDirectionFromLport { // egress rule
1064-
srcOrDst = "dst"
1064+
remoteSrcOrDst = "dst"
1065+
localSrcOrDst = "src"
10651066
portDirection = "inport"
10661067
}
10671068

1068-
ipKey := ipSuffix + "." + srcOrDst
1069+
remoteipKey := ipSuffix + "." + remoteSrcOrDst
1070+
localipKey := ipSuffix + "." + localSrcOrDst
10691071

10701072
/* match all traffic to or from pgName */
10711073
allIPMatch := NewAndACLMatch(
@@ -1075,9 +1077,10 @@ func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.Secu
10751077

10761078
/* allow allowed ip traffic */
10771079
// type address
1080+
10781081
allowedIPMatch := NewAndACLMatch(
10791082
allIPMatch,
1080-
NewACLMatch(ipKey, "==", rule.RemoteAddress, ""),
1083+
NewACLMatch(remoteipKey, "==", rule.RemoteAddress, ""),
10811084
)
10821085

10831086
// type securityGroup
@@ -1088,7 +1091,15 @@ func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.Secu
10881091
if rule.RemoteType == kubeovnv1.SgRemoteTypeSg {
10891092
allowedIPMatch = NewAndACLMatch(
10901093
allIPMatch,
1091-
NewACLMatch(ipKey, "==", "$"+remotePgName, ""),
1094+
NewACLMatch(remoteipKey, "==", "$"+remotePgName, ""),
1095+
)
1096+
}
1097+
1098+
// Add a rule to match local address only if it is set
1099+
if rule.LocalAddress != "" {
1100+
allowedIPMatch = NewAndACLMatch(
1101+
allowedIPMatch,
1102+
NewACLMatch(localipKey, "==", rule.LocalAddress, ""),
10921103
)
10931104
}
10941105

@@ -1113,16 +1124,27 @@ func (c *OVNNbClient) newSgRuleACL(sgName, direction string, rule kubeovnv1.Secu
11131124
allowedIPMatch,
11141125
NewACLMatch(string(rule.Protocol)+".dst", "<=", strconv.Itoa(rule.PortRangeMin), strconv.Itoa(rule.PortRangeMax)),
11151126
)
1127+
1128+
// Add a match on source port if a local address was provided.
1129+
if rule.LocalAddress != "" {
1130+
match = NewAndACLMatch(
1131+
match,
1132+
NewACLMatch(string(rule.Protocol)+".src", "<=", strconv.Itoa(rule.LocalPortRangeMin), strconv.Itoa(rule.LocalPortRangeMax)),
1133+
)
1134+
}
11161135
}
11171136

11181137
action := ovnnb.ACLActionDrop
11191138
if rule.Policy == kubeovnv1.SgPolicyAllow {
11201139
action = ovnnb.ACLActionAllowRelated
11211140
}
1141+
if rule.Policy == kubeovnv1.SgPolicyPass {
1142+
action = ovnnb.ACLActionPass
1143+
}
11221144

11231145
highestPriority, _ := strconv.Atoi(util.SecurityGroupHighestPriority)
11241146

1125-
acl, err := c.newACL(pgName, direction, strconv.Itoa(highestPriority-rule.Priority), match.String(), action, util.NetpolACLTier)
1147+
acl, err := c.newACL(pgName, direction, strconv.Itoa(highestPriority-rule.Priority), match.String(), action, tier)
11261148
if err != nil {
11271149
klog.Error(err)
11281150
return nil, fmt.Errorf("new security group acl for port group %s: %w", pgName, err)

0 commit comments

Comments
 (0)