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 }