yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/cloudprovider/securitygroup.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cloudprovider
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  
    22  	"gopkg.in/fatih/set.v0"
    23  
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/util/secrules"
    26  	"yunion.io/x/pkg/utils"
    27  )
    28  
    29  type SecurityGroupReference struct {
    30  	Id   string
    31  	Name string
    32  }
    33  
    34  type SecDriver interface {
    35  	GetDefaultSecurityGroupInRule() SecurityRule
    36  	GetDefaultSecurityGroupOutRule() SecurityRule
    37  	GetSecurityGroupRuleMaxPriority() int
    38  	GetSecurityGroupRuleMinPriority() int
    39  	IsOnlySupportAllowRules() bool
    40  	IsSupportPeerSecgroup() bool
    41  }
    42  
    43  func NewSecRuleInfo(driver SecDriver) SecRuleInfo {
    44  	return SecRuleInfo{
    45  		InDefaultRule:           driver.GetDefaultSecurityGroupInRule(),
    46  		OutDefaultRule:          driver.GetDefaultSecurityGroupOutRule(),
    47  		MinPriority:             driver.GetSecurityGroupRuleMinPriority(),
    48  		MaxPriority:             driver.GetSecurityGroupRuleMaxPriority(),
    49  		IsOnlySupportAllowRules: driver.IsOnlySupportAllowRules(),
    50  		IsSupportPeerSecgroup:   driver.IsSupportPeerSecgroup(),
    51  	}
    52  }
    53  
    54  const DEFAULT_DEST_RULE_ID = "default_dest_rule_id"
    55  const DEFAULT_SRC_RULE_ID = "default_src_rule_id"
    56  
    57  type SecRuleInfo struct {
    58  	InDefaultRule           SecurityRule
    59  	OutDefaultRule          SecurityRule
    60  	in                      SecurityRuleSet
    61  	out                     SecurityRuleSet
    62  	rules                   SecurityRuleSet
    63  	Rules                   SecurityRuleSet
    64  	MinPriority             int
    65  	MaxPriority             int
    66  	IsOnlySupportAllowRules bool
    67  	IsSupportPeerSecgroup   bool
    68  }
    69  
    70  func (r SecRuleInfo) getOffest(priority int) (int, int) {
    71  	if r.MinPriority < r.MaxPriority {
    72  		if priority >= r.MaxPriority {
    73  			return priority, -1
    74  		}
    75  		return priority + 1, 0
    76  	} else {
    77  		if priority <= r.MaxPriority {
    78  			return priority, 1
    79  		}
    80  		return priority - 1, 0
    81  	}
    82  }
    83  
    84  func (r SecRuleInfo) AddDefaultRule(d SecRuleInfo, inRules, outRules []SecurityRule, isSrc bool) ([]SecurityRule, []SecurityRule) {
    85  	min, max := r.MinPriority, r.MaxPriority
    86  	r.InDefaultRule.Priority = min + 1
    87  	r.OutDefaultRule.Priority = min + 1
    88  	r.InDefaultRule.minPriority, r.InDefaultRule.maxPriority = r.MinPriority, r.MaxPriority
    89  	r.OutDefaultRule.minPriority, r.OutDefaultRule.maxPriority = r.MinPriority, r.MaxPriority
    90  	if max >= min {
    91  		r.InDefaultRule.Priority = min - 1
    92  		r.OutDefaultRule.Priority = min - 1
    93  	}
    94  
    95  	if isSrc {
    96  		r.InDefaultRule.Id = DEFAULT_SRC_RULE_ID
    97  		r.OutDefaultRule.Id = DEFAULT_SRC_RULE_ID
    98  	} else {
    99  		r.InDefaultRule.ExternalId = DEFAULT_DEST_RULE_ID
   100  		r.OutDefaultRule.ExternalId = DEFAULT_DEST_RULE_ID
   101  	}
   102  
   103  	var isWideRule = func(rules []SecurityRule) bool {
   104  		for _, rule := range rules {
   105  			if strings.HasSuffix(rule.String(), "allow any") || strings.HasSuffix(rule.String(), "deny any") {
   106  				return true
   107  			}
   108  		}
   109  		return false
   110  	}
   111  
   112  	if !isWideRule(inRules) {
   113  		inRules = append(inRules, r.InDefaultRule)
   114  	}
   115  	if !isWideRule(outRules) {
   116  		outRules = append(outRules, r.OutDefaultRule)
   117  	}
   118  	return inRules, outRules
   119  }
   120  
   121  type SecurityGroupFilterOptions struct {
   122  	VpcId     string
   123  	Name      string
   124  	ProjectId string
   125  }
   126  
   127  type SecurityGroupCreateInput struct {
   128  	Name      string
   129  	Desc      string
   130  	VpcId     string
   131  	ProjectId string
   132  	Rules     []secrules.SecurityRule
   133  }
   134  
   135  type SecurityRule struct {
   136  	minPriority int
   137  	maxPriority int
   138  	offset      int
   139  
   140  	secrules.SecurityRule
   141  	Name       string
   142  	ExternalId string
   143  	Id         string
   144  
   145  	PeerSecgroupId string
   146  }
   147  
   148  func (self *SecurityRule) addPriority(offset int) {
   149  	self.Priority += offset
   150  	self.offset += offset
   151  }
   152  
   153  func (r SecurityRule) String() string {
   154  	if len(r.PeerSecgroupId) == 0 {
   155  		return r.SecurityRule.String()
   156  	}
   157  	return fmt.Sprintf("%s-%s", r.SecurityRule.String(), r.PeerSecgroupId)
   158  }
   159  
   160  type SecurityRuleSet []SecurityRule
   161  
   162  func (self *SecRuleInfo) Split() (in, out SecurityRuleSet) {
   163  	self.rules = SecurityRuleSet{}
   164  	self.in = SecurityRuleSet{}
   165  	self.out = SecurityRuleSet{}
   166  	for i := 0; i < len(self.Rules); i++ {
   167  		self.rules = append(self.rules, self.Rules[i])
   168  		if self.Rules[i].Direction == secrules.DIR_IN {
   169  			self.in = append(self.in, self.Rules[i])
   170  		} else {
   171  			self.out = append(self.out, self.Rules[i])
   172  		}
   173  		self.Rules[i].minPriority, self.Rules[i].maxPriority = self.MinPriority, self.MaxPriority
   174  		if !self.IsSupportPeerSecgroup && len(self.Rules[i].PeerSecgroupId) > 0 {
   175  			continue
   176  		}
   177  		if self.Rules[i].Direction == secrules.DIR_IN {
   178  			in = append(in, self.Rules[i])
   179  		} else {
   180  			out = append(out, self.Rules[i])
   181  		}
   182  	}
   183  	return
   184  }
   185  
   186  func (srs SecurityRuleSet) Len() int {
   187  	return len(srs)
   188  }
   189  
   190  func (srs SecurityRuleSet) Swap(i, j int) {
   191  	srs[i], srs[j] = srs[j], srs[i]
   192  }
   193  
   194  func (srs SecurityRuleSet) Less(i, j int) bool {
   195  	if srs[i].Priority < srs[j].Priority {
   196  		return true
   197  	}
   198  	if srs[i].Priority > srs[j].Priority {
   199  		return false
   200  	}
   201  	if len(srs) > 0 {
   202  		if (srs[0].minPriority <= srs[0].maxPriority && srs[i].String() < srs[j].String()) ||
   203  			(srs[0].minPriority > srs[0].maxPriority && srs[i].String() > srs[j].String()) {
   204  			return true
   205  		}
   206  	}
   207  	return false
   208  }
   209  
   210  func (srs SecurityRuleSet) CanBeSplitByProtocol() bool {
   211  	firstNormalRuleIndex, find := 0, false
   212  	for idx, r := range srs {
   213  		if !(strings.HasSuffix(r.String(), "allow any") || strings.HasSuffix(r.String(), "deny any")) && !find {
   214  			firstNormalRuleIndex = idx
   215  			find = true
   216  		} else {
   217  			if r.ExternalId == DEFAULT_SRC_RULE_ID || r.ExternalId == DEFAULT_DEST_RULE_ID {
   218  				return true
   219  			}
   220  			if idx > firstNormalRuleIndex {
   221  				return false
   222  			}
   223  		}
   224  	}
   225  	return true
   226  }
   227  
   228  func (srs SecurityRuleSet) AllowList() (secrules.SecurityRuleSet, bool) {
   229  	rules := secrules.SecurityRuleSet{}
   230  	isOk := true
   231  	for _, r := range srs {
   232  		if len(r.PeerSecgroupId) > 0 {
   233  			isOk = false
   234  		}
   235  		rules = append(rules, r.SecurityRule)
   236  	}
   237  	return rules.AllowList(), isOk
   238  }
   239  
   240  func (srs SecurityRuleSet) Debug() {
   241  	for i := 0; i < len(srs); i++ {
   242  		log.Debugf("Name: %s id: %s external_id: %s min: %d max: %d priority: %d %s", srs[i].Name, srs[i].Id, srs[i].ExternalId, srs[i].minPriority, srs[i].maxPriority, srs[i].Priority, srs[i].String())
   243  	}
   244  }
   245  
   246  func SortSecurityRule(rules SecurityRuleSet, isAsc bool, info SecRuleInfo) {
   247  	if (info.MaxPriority > info.MinPriority && isAsc) || (info.MaxPriority < info.MinPriority && !isAsc) || (info.IsOnlySupportAllowRules && isAsc) {
   248  		sort.Sort(rules)
   249  		return
   250  	}
   251  	sort.Sort(sort.Reverse(rules))
   252  	return
   253  }
   254  
   255  func isAllowListEqual(src, dest secrules.SecurityRuleSet) bool {
   256  	if len(src) != len(dest) {
   257  		return false
   258  	}
   259  	s1, s2 := set.New(set.ThreadSafe), set.New(set.ThreadSafe)
   260  	for i := 0; i < len(src); i++ {
   261  		s1.Add(src[i].String())
   262  		s2.Add(dest[i].String())
   263  	}
   264  	return s1.IsEqual(s2)
   265  }
   266  
   267  func isPeerListEqual(src, dest SecurityRuleSet) bool {
   268  	if len(src) != len(dest) {
   269  		return false
   270  	}
   271  	s1, s2 := set.New(set.ThreadSafe), set.New(set.ThreadSafe)
   272  	for i := 0; i < len(src); i++ {
   273  		s1.Add(src[i].String())
   274  		s2.Add(dest[i].String())
   275  	}
   276  	return s1.IsEqual(s2)
   277  }
   278  
   279  func CompareRules(src, dest SecRuleInfo, debug bool) (common, inAdds, outAdds, inDels, outDels SecurityRuleSet) {
   280  	srcInRules, srcOutRules := src.Split()
   281  	destInRules, destOutRules := dest.Split()
   282  
   283  	srcInRules, srcOutRules = src.AddDefaultRule(dest, srcInRules, srcOutRules, true)
   284  	destInRules, destOutRules = dest.AddDefaultRule(src, destInRules, destOutRules, false)
   285  
   286  	// AllowList 需要优先级从高到低排序
   287  	SortSecurityRule(srcInRules, false, src)
   288  	SortSecurityRule(srcOutRules, false, src)
   289  
   290  	SortSecurityRule(destInRules, false, dest)
   291  	SortSecurityRule(destOutRules, false, dest)
   292  
   293  	isInAllowSplit := srcInRules.CanBeSplitByProtocol() && destInRules.CanBeSplitByProtocol()
   294  	isOutAllowSplit := srcOutRules.CanBeSplitByProtocol() && destOutRules.CanBeSplitByProtocol()
   295  
   296  	srcInAllowList, srcInOk := srcInRules.AllowList()
   297  	srcOutAllowList, srcOutOk := srcOutRules.AllowList()
   298  
   299  	destInAllowList, destInOk := destInRules.AllowList()
   300  	destOutAllowList, destOutOk := destOutRules.AllowList()
   301  	inEquals, outEquals, inOk, outOk := isAllowListEqual(srcInAllowList, destInAllowList), isAllowListEqual(srcOutAllowList, destOutAllowList), srcInOk && destInOk, srcOutOk && destOutOk
   302  
   303  	if inEquals && outEquals && inOk && outOk {
   304  		common = dest.rules
   305  		return
   306  	}
   307  
   308  	if debug {
   309  		log.Debugf("====desc sort====")
   310  		log.Debugf("src in rules: ")
   311  		srcInRules.Debug()
   312  		log.Debugf("src out rules: ")
   313  		srcOutRules.Debug()
   314  		log.Debugf("dest in rules: ")
   315  		destInRules.Debug()
   316  		log.Debugf("dest out rules: ")
   317  		destOutRules.Debug()
   318  		log.Debugf("====desc sort end====")
   319  
   320  		log.Debugf("isInAllowSplit: %v isOutAllowSplit: %v", isInAllowSplit, isOutAllowSplit)
   321  
   322  		log.Debugf("AllowList:")
   323  		log.Debugf("In: src: %s dest: %s isEquals: %v", srcInAllowList.String(), destInAllowList.String(), inEquals)
   324  		log.Debugf("Out: src: %s dest: %s isEquals: %v", srcOutAllowList.String(), destOutAllowList.String(), outEquals)
   325  	}
   326  
   327  	var tryUseAllowList = func(defaultRule SecurityRule, allowList secrules.SecurityRuleSet, rules SecurityRuleSet, isOnlyAllowList bool, isOk bool) SecurityRuleSet {
   328  		if isOk && (len(allowList) < len(rules) || isOnlyAllowList) {
   329  			rules = SecurityRuleSet{}
   330  			for i := range allowList {
   331  				rule := SecurityRule{}
   332  				rule.SecurityRule = allowList[i]
   333  				rules = append(rules, rule)
   334  			}
   335  
   336  			if !utils.IsInStringArray(allowList.String(), []string{
   337  				"",
   338  				"in:allow any",
   339  				"out:allow any",
   340  				"in:deny any",
   341  				"out:deny any",
   342  			}) && strings.HasSuffix(defaultRule.SecurityRule.String(), "deny any") {
   343  				rules = append(rules, defaultRule)
   344  			}
   345  		}
   346  		return rules
   347  	}
   348  
   349  	srcInRules = tryUseAllowList(src.InDefaultRule, srcInAllowList, srcInRules, dest.IsOnlySupportAllowRules, inOk)
   350  	srcOutRules = tryUseAllowList(src.OutDefaultRule, srcOutAllowList, srcOutRules, dest.IsOnlySupportAllowRules, outOk)
   351  
   352  	if inEquals && inOk {
   353  		common = append(common, dest.in...)
   354  		srcInRules, destInRules = []SecurityRule{}, []SecurityRule{}
   355  	}
   356  	if outEquals && outOk {
   357  		common = append(common, dest.out...)
   358  		srcOutRules, destOutRules = []SecurityRule{}, []SecurityRule{}
   359  	}
   360  
   361  	// 默认从优先级低到高比较
   362  	SortSecurityRule(srcInRules, true, src)
   363  	SortSecurityRule(srcOutRules, true, src)
   364  
   365  	SortSecurityRule(destInRules, true, dest)
   366  	SortSecurityRule(destOutRules, true, dest)
   367  
   368  	if debug {
   369  		log.Debugf("====asc sort====")
   370  		log.Debugf("src in rules: ")
   371  		srcInRules.Debug()
   372  		log.Debugf("src out rules: ")
   373  		srcOutRules.Debug()
   374  		log.Debugf("dest in rules: ")
   375  		destInRules.Debug()
   376  		log.Debugf("dest out rules: ")
   377  		destOutRules.Debug()
   378  		log.Debugf("====asc sort end====")
   379  	}
   380  
   381  	var _compare = func(srcRules SecurityRuleSet, destRules SecurityRuleSet) (common, add, del SecurityRuleSet) {
   382  		i, j, priority := 0, 0, dest.MinPriority
   383  		if len(destRules) > 0 && (destRules[i].ExternalId != DEFAULT_DEST_RULE_ID) {
   384  			priority = destRules[0].Priority
   385  		}
   386  		for i < len(srcRules) || j < len(destRules) {
   387  			if i < len(srcRules) && j < len(destRules) {
   388  				destRuleStr := destRules[j].String()
   389  				srcRuleStr := srcRules[i].String()
   390  				if debug {
   391  					log.Debugf("compare src %s(%s) priority(%d) %s -> dest name(%s) %s(%s) priority(%d) %s\n",
   392  						srcRules[i].Id, srcRules[i].ExternalId, srcRules[i].Priority, srcRules[i].String(),
   393  						destRules[j].Name, destRules[j].ExternalId, destRules[j].Id, destRules[j].Priority, destRules[j].String())
   394  				}
   395  				cmp := strings.Compare(destRuleStr, srcRuleStr)
   396  				if cmp == 0 {
   397  					destRules[j].Id = srcRules[i].Id
   398  					common = append(common, destRules[j])
   399  					if destRules[j].ExternalId != DEFAULT_DEST_RULE_ID {
   400  						priority = destRules[j].Priority
   401  					}
   402  					i++
   403  					j++
   404  				} else if cmp < 0 {
   405  					del = append(del, destRules[j])
   406  					j++
   407  				} else {
   408  					offset := 0
   409  					if !dest.IsOnlySupportAllowRules && i-1 >= 0 && srcRules[i-1].Priority != srcRules[i].Priority {
   410  						priority, offset = dest.getOffest(priority)
   411  					}
   412  					if offset != 0 {
   413  						for r := range add {
   414  							add[r].addPriority(offset)
   415  						}
   416  						for r := range common {
   417  							common[r].addPriority(offset)
   418  						}
   419  					}
   420  					srcRules[i].Priority = priority
   421  					srcRules[i].minPriority, srcRules[i].maxPriority = dest.MinPriority, dest.MaxPriority
   422  					add = append(add, srcRules[i])
   423  					i++
   424  				}
   425  			} else if i >= len(srcRules) {
   426  				del = append(del, destRules[j])
   427  				j++
   428  			} else if j >= len(destRules) {
   429  				offset := 0
   430  				if !dest.IsOnlySupportAllowRules && i-1 >= 0 && srcRules[i-1].Priority != srcRules[i].Priority {
   431  					priority, offset = dest.getOffest(priority)
   432  				}
   433  				srcRules[i].Priority = priority
   434  				srcRules[i].minPriority, srcRules[i].maxPriority = dest.MinPriority, dest.MaxPriority
   435  				if offset != 0 {
   436  					for r := range add {
   437  						add[r].addPriority(offset)
   438  					}
   439  					for r := range common {
   440  						common[r].addPriority(offset)
   441  					}
   442  				}
   443  				add = append(add, srcRules[i])
   444  				i++
   445  			}
   446  		}
   447  		return
   448  	}
   449  
   450  	type rulePair struct {
   451  		srcRules  SecurityRuleSet
   452  		destRules SecurityRuleSet
   453  		protocol  string
   454  	}
   455  
   456  	var splitRules = func(src, dest SecurityRuleSet) []rulePair {
   457  		rules := map[string]rulePair{}
   458  		for _, r := range src {
   459  			pair, ok := rules[r.Protocol]
   460  			if !ok {
   461  				pair = rulePair{srcRules: SecurityRuleSet{}, destRules: SecurityRuleSet{}, protocol: r.Protocol}
   462  			}
   463  			pair.srcRules = append(pair.srcRules, r)
   464  			rules[r.Protocol] = pair
   465  		}
   466  
   467  		for _, r := range dest {
   468  			pair, ok := rules[r.Protocol]
   469  			if !ok {
   470  				pair = rulePair{srcRules: SecurityRuleSet{}, destRules: SecurityRuleSet{}, protocol: r.Protocol}
   471  			}
   472  			pair.destRules = append(pair.destRules, r)
   473  			rules[r.Protocol] = pair
   474  		}
   475  
   476  		ret := []rulePair{}
   477  		for _, r := range rules {
   478  			ret = append(ret, r)
   479  		}
   480  		return ret
   481  	}
   482  
   483  	var compare = func(src, dest SecurityRuleSet) (common, added, dels SecurityRuleSet) {
   484  		pairs := splitRules(src, dest)
   485  		for _, r := range pairs {
   486  			_common, _add, _dels := _compare(r.srcRules, r.destRules)
   487  			common = append(common, _common...)
   488  			added = append(added, _add...)
   489  			dels = append(dels, _dels...)
   490  		}
   491  		return
   492  	}
   493  
   494  	var inCommon, outCommon SecurityRuleSet
   495  	if isInAllowSplit {
   496  		inCommon, inAdds, inDels = compare(srcInRules, destInRules)
   497  	} else {
   498  		inCommon, inAdds, inDels = _compare(srcInRules, destInRules)
   499  	}
   500  	if isOutAllowSplit {
   501  		outCommon, outAdds, outDels = compare(srcOutRules, destOutRules)
   502  	} else {
   503  		outCommon, outAdds, outDels = _compare(srcOutRules, destOutRules)
   504  	}
   505  
   506  	var handleDefaultRules = func(removed, added []SecurityRule, isOnlyAllowList bool) ([]SecurityRule, []SecurityRule, *SecurityRule) {
   507  		ret := []SecurityRule{}
   508  		var defaultRule *SecurityRule = nil
   509  		for i := range removed {
   510  			rule := removed[i]
   511  			if rule.ExternalId == DEFAULT_DEST_RULE_ID {
   512  				if debug {
   513  					log.Debugf("remove dest default rule: %s external id %s priority: %d", rule.String(), rule.ExternalId, rule.Priority)
   514  				}
   515  				if rule.Action == secrules.SecurityRuleDeny && isOnlyAllowList {
   516  					continue
   517  				}
   518  				switch rule.Action {
   519  				case secrules.SecurityRuleDeny:
   520  					rule.Action = secrules.SecurityRuleAllow
   521  				case secrules.SecurityRuleAllow:
   522  					rule.Action = secrules.SecurityRuleDeny
   523  				}
   524  				rule.Priority = dest.MinPriority
   525  				defaultRule = &rule
   526  			} else {
   527  				ret = append(ret, rule)
   528  			}
   529  		}
   530  		return ret, added, defaultRule
   531  	}
   532  
   533  	var inDefault *SecurityRule
   534  	var outDefault *SecurityRule
   535  	inDels, inAdds, inDefault = handleDefaultRules(inDels, inAdds, dest.IsOnlySupportAllowRules)
   536  	if inDefault != nil {
   537  		inAllow, _ := append(inAdds, inCommon...).AllowList()
   538  		if len(inAllow.String()) == 0 || (!strings.Contains(inAllow.String(), inDefault.String()) && !srcInAllowList.Equals(inAllow)) {
   539  			inAdds = append(inAdds, *inDefault)
   540  		}
   541  	}
   542  	outDels, outAdds, outDefault = handleDefaultRules(outDels, outAdds, dest.IsOnlySupportAllowRules)
   543  
   544  	if outDefault != nil {
   545  		outAllow, _ := append(outAdds, outCommon...).AllowList()
   546  		if len(outAllow.String()) == 0 || (!strings.Contains(outAllow.String(), outDefault.String()) && !srcOutAllowList.Equals(outAllow)) {
   547  			outAdds = append(outAdds, *outDefault)
   548  		}
   549  	}
   550  	_common, _, _ := handleDefaultRules(append(inCommon, outCommon...), []SecurityRule{}, dest.IsOnlySupportAllowRules)
   551  	common = append(common, _common...)
   552  	return
   553  }
   554  
   555  func SortUniqPriority(rules SecurityRuleSet) []SecurityRule {
   556  	sort.Sort(rules)
   557  	priMap := map[int]bool{}
   558  	for i := range rules {
   559  		for {
   560  			_, ok := priMap[rules[i].Priority]
   561  			if !ok {
   562  				priMap[rules[i].Priority] = true
   563  				break
   564  			}
   565  			rules[i].Priority = rules[i].Priority - 1
   566  		}
   567  	}
   568  	return rules
   569  }