yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/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 qcloud 16 17 import ( 18 "fmt" 19 "sort" 20 "strconv" 21 "strings" 22 "time" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 "yunion.io/x/pkg/errors" 27 "yunion.io/x/pkg/util/secrules" 28 "yunion.io/x/pkg/utils" 29 30 "yunion.io/x/cloudmux/pkg/cloudprovider" 31 "yunion.io/x/cloudmux/pkg/multicloud" 32 ) 33 34 type SecurityGroupPolicy struct { 35 region *SRegion 36 PolicyIndex int // 安全组规则索引号。 37 Protocol string // 协议, 取值: TCP,UDP, ICMP。 38 Port string // 端口(all, 离散port, range)。 39 ServiceTemplate ServiceTemplateSpecification // 协议端口ID或者协议端口组ID。ServiceTemplate和Protocol+Port互斥。 40 CidrBlock string // 网段或IP(互斥)。 41 SecurityGroupId string // 已绑定安全组的网段或IP。 42 AddressTemplate AddressTemplateSpecification // IP地址ID或者ID地址组ID。 43 Action string // ACCEPT 或 DROP。 44 PolicyDescription string // 安全组规则描述。 45 direction string 46 } 47 48 type ServiceTemplateSpecification struct { 49 ServiceId string // 协议端口ID,例如:ppm-f5n1f8da。 50 ServiceGroupId string // 协议端口组ID,例如:ppmg-f5n1f8da。 51 } 52 53 type AddressTemplateSpecification struct { 54 AddressId string // IP地址ID,例如:ipm-2uw6ujo6。 55 AddressGroupId string // IP地址组ID,例如:ipmg-2uw6ujo6。 56 } 57 58 type SecurityGroupPolicySet struct { 59 Version string 60 Egress []SecurityGroupPolicy // 出站规则。 61 Ingress []SecurityGroupPolicy // 入站规则。 62 } 63 64 type SSecurityGroup struct { 65 multicloud.SSecurityGroup 66 QcloudTags 67 region *SRegion 68 SecurityGroupId string // 安全组实例ID,例如:sg-ohuuioma。 69 SecurityGroupName string // 安全组名称,可任意命名,但不得超过60个字符。 70 SecurityGroupDesc string // 安全组备注,最多100个字符。 71 ProjectId string // 项目id,默认0。可在qcloud控制台项目管理页面查询到。 72 IsDefault bool // 是否是默认安全组,默认安全组不支持删除。 73 CreatedTime time.Time // 安全组创建时间。 74 SecurityGroupPolicySet SecurityGroupPolicySet 75 } 76 77 func (self *SRegion) GetSecurityGroups(ids []string, vpcId string, name string, offset int, limit int) ([]SSecurityGroup, int, error) { 78 if limit > 50 || limit <= 0 { 79 limit = 50 80 } 81 params := make(map[string]string) 82 params["Limit"] = fmt.Sprintf("%d", limit) 83 params["Offset"] = fmt.Sprintf("%d", offset) 84 85 if len(name) > 0 { 86 params["Filters.0.Name"] = "security-group-name" 87 params["Filters.0.Values.0"] = name 88 } 89 90 for idx, id := range ids { 91 params[fmt.Sprintf("SecurityGroupIds.%d", idx)] = id 92 } 93 94 resp, err := self.vpcRequest("DescribeSecurityGroups", params) 95 if err != nil { 96 return nil, 0, errors.Wrapf(err, "DescribeSecurityGroups") 97 } 98 99 secgrps := make([]SSecurityGroup, 0) 100 err = resp.Unmarshal(&secgrps, "SecurityGroupSet") 101 if err != nil { 102 return nil, 0, errors.Wrapf(err, "resp.Unmarshal") 103 } 104 total, _ := resp.Float("TotalCount") 105 return secgrps, int(total), nil 106 } 107 108 func (self *SSecurityGroup) GetVpcId() string { 109 //腾讯云安全组未与vpc关联,统一使用normal 110 return "normal" 111 } 112 113 func (self *SSecurityGroup) GetId() string { 114 return self.SecurityGroupId 115 } 116 117 func (self *SSecurityGroup) GetGlobalId() string { 118 return self.SecurityGroupId 119 } 120 121 func (self *SSecurityGroup) GetDescription() string { 122 return self.SecurityGroupDesc 123 } 124 125 func (self *SSecurityGroup) GetName() string { 126 if len(self.SecurityGroupName) > 0 { 127 return self.SecurityGroupName 128 } 129 return self.SecurityGroupId 130 } 131 132 func (self *SecurityGroupPolicy) String() string { 133 rules := self.toRules() 134 result := []string{} 135 for _, rule := range rules { 136 result = append(result, rule.String()) 137 } 138 return strings.Join(result, ";") 139 } 140 141 type ReferredSecurityGroup struct { 142 SecurityGroupId string 143 ReferredSecurityGroupIds []string 144 } 145 146 func (self *SSecurityGroup) GetReferences() ([]cloudprovider.SecurityGroupReference, error) { 147 references, err := self.region.DescribeSecurityGroupReferences(self.SecurityGroupId) 148 if err != nil { 149 return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") 150 } 151 ret := []cloudprovider.SecurityGroupReference{} 152 for _, refer := range references { 153 if refer.SecurityGroupId == self.SecurityGroupId { 154 for _, id := range refer.ReferredSecurityGroupIds { 155 ret = append(ret, cloudprovider.SecurityGroupReference{ 156 Id: id, 157 }) 158 } 159 } 160 } 161 return ret, nil 162 } 163 164 func (self *SRegion) DescribeSecurityGroupReferences(id string) ([]ReferredSecurityGroup, error) { 165 params := map[string]string{ 166 "Region": self.Region, 167 "SecurityGroupIds.0": id, 168 } 169 resp, err := self.vpcRequest("DescribeSecurityGroupReferences", params) 170 if err != nil { 171 return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") 172 } 173 ret := []ReferredSecurityGroup{} 174 err = resp.Unmarshal(&ret, "ReferredSecurityGroupSet") 175 if err != nil { 176 return nil, errors.Wrapf(err, "resp.Unmarshal") 177 } 178 return ret, nil 179 } 180 181 func (self *SecurityGroupPolicy) toRules() []cloudprovider.SecurityRule { 182 result := []cloudprovider.SecurityRule{} 183 rule := cloudprovider.SecurityRule{ 184 ExternalId: fmt.Sprintf("%d", self.PolicyIndex), 185 SecurityRule: secrules.SecurityRule{ 186 Action: secrules.SecurityRuleAllow, 187 Protocol: secrules.PROTO_ANY, 188 Direction: secrules.TSecurityRuleDirection(self.direction), 189 Priority: self.PolicyIndex, 190 Ports: []int{}, 191 PortStart: -1, 192 PortEnd: -1, 193 }, 194 } 195 if len(self.SecurityGroupId) != 0 { 196 rule.ParseCIDR("0.0.0.0/0") 197 rule.PeerSecgroupId = self.SecurityGroupId 198 } 199 if strings.ToLower(self.Action) == "drop" { 200 rule.Action = secrules.SecurityRuleDeny 201 } 202 if utils.IsInStringArray(strings.ToLower(self.Protocol), []string{"tcp", "udp", "icmp"}) { 203 rule.Protocol = strings.ToLower(self.Protocol) 204 } 205 if strings.Index(self.Port, ",") > 0 { 206 for _, _port := range strings.Split(self.Port, ",") { 207 port, err := strconv.Atoi(_port) 208 if err != nil { 209 log.Errorf("parse secgroup port %s %s error %v", self.Port, _port, err) 210 continue 211 } 212 rule.Ports = append(rule.Ports, port) 213 } 214 } else if strings.Index(self.Port, "-") > 0 { 215 ports := strings.Split(self.Port, "-") 216 if len(ports) == 2 { 217 portStart, err := strconv.Atoi(ports[0]) 218 if err != nil { 219 return nil 220 } 221 portEnd, err := strconv.Atoi(ports[1]) 222 if err != nil { 223 return nil 224 } 225 rule.PortStart, rule.PortEnd = portStart, portEnd 226 } 227 } else if strings.ToLower(self.Port) != "all" { 228 port, err := strconv.Atoi(self.Port) 229 if err != nil { 230 return nil 231 } 232 rule.PortStart, rule.PortEnd = port, port 233 } 234 235 if len(self.AddressTemplate.AddressGroupId) > 0 { 236 addressGroup, total, err := self.region.AddressGroupList(self.AddressTemplate.AddressGroupId, "", 0, 1) 237 if err != nil { 238 log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err) 239 return nil 240 } 241 if total != 1 { 242 return nil 243 } 244 for i := 0; i < len(addressGroup[0].AddressTemplateIdSet); i++ { 245 rules, err := self.getAddressRules(rule, addressGroup[0].AddressTemplateIdSet[i]) 246 if err != nil { 247 return nil 248 } 249 result = append(result, rules...) 250 } 251 } else if len(self.AddressTemplate.AddressId) > 0 { 252 rules, err := self.getAddressRules(rule, self.AddressTemplate.AddressId) 253 if err != nil { 254 return nil 255 } 256 result = append(result, rules...) 257 } else if len(self.SecurityGroupId) > 0 { 258 rule.PeerSecgroupId = self.SecurityGroupId 259 result = append(result, rule) 260 } else if len(self.CidrBlock) > 0 { 261 rule.ParseCIDR(self.CidrBlock) 262 result = append(result, rule) 263 } 264 return result 265 } 266 267 func (self *SecurityGroupPolicy) getAddressRules(rule cloudprovider.SecurityRule, addressId string) ([]cloudprovider.SecurityRule, error) { 268 result := []cloudprovider.SecurityRule{} 269 address, total, err := self.region.AddressList(addressId, "", 0, 1) 270 if err != nil { 271 log.Errorf("Get AddressList %s failed %v", self.AddressTemplate.AddressId, err) 272 return nil, err 273 } 274 if total != 1 { 275 return nil, fmt.Errorf("failed to find address %s", addressId) 276 } 277 for _, ip := range address[0].AddressSet { 278 rule.ParseCIDR(ip) 279 result = append(result, rule) 280 } 281 return result, nil 282 } 283 284 func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) { 285 policySet, err := self.region.DescribeSecurityGroupPolicies(self.SecurityGroupId) 286 if err != nil { 287 return nil, err 288 } 289 for i := 0; i < len(policySet.Egress); i++ { 290 policySet.Egress[i].direction = "out" 291 } 292 for i := 0; i < len(policySet.Ingress); i++ { 293 policySet.Ingress[i].direction = "in" 294 } 295 originRules := []SecurityGroupPolicy{} 296 originRules = append(originRules, policySet.Egress...) 297 originRules = append(originRules, policySet.Ingress...) 298 for i := 0; i < len(originRules); i++ { 299 originRules[i].region = self.region 300 } 301 rules := []cloudprovider.SecurityRule{} 302 for _, rule := range originRules { 303 subRules := rule.toRules() 304 rules = append(rules, subRules...) 305 } 306 return rules, nil 307 } 308 309 func (self *SSecurityGroup) GetStatus() string { 310 return "" 311 } 312 313 func (self *SSecurityGroup) IsEmulated() bool { 314 return false 315 } 316 317 func (self *SSecurityGroup) Refresh() error { 318 groups, total, err := self.region.GetSecurityGroups([]string{self.SecurityGroupId}, "", "", 0, 0) 319 if err != nil { 320 return err 321 } 322 if total < 1 { 323 return cloudprovider.ErrNotFound 324 } 325 return jsonutils.Update(self, groups[0]) 326 } 327 328 func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { 329 rules := append(common, append(inAdds, outAdds...)...) 330 sort.Sort(cloudprovider.SecurityRuleSet(rules)) 331 return self.region.syncSecgroupRules(self.SecurityGroupId, rules) 332 } 333 334 func (self *SRegion) syncSecgroupRules(secgroupId string, rules []cloudprovider.SecurityRule) error { 335 params := map[string]string{ 336 "SecurityGroupId": secgroupId, 337 "SortPolicys": "True", 338 } 339 egressIndex, ingressIndex := 0, 0 340 for _, rule := range rules { 341 switch rule.Direction { 342 case secrules.DIR_IN: 343 params = convertSecgroupRule(ingressIndex, rule, params) 344 ingressIndex++ 345 case secrules.DIR_OUT: 346 params = convertSecgroupRule(egressIndex, rule, params) 347 egressIndex++ 348 } 349 } 350 _, err := self.vpcRequest("ModifySecurityGroupPolicies", params) 351 if err != nil { 352 return errors.Wrapf(err, "ModifySecurityGroupPolicies") 353 } 354 if egressIndex == 0 || ingressIndex == 0 { 355 ruleSet, err := self.DescribeSecurityGroupPolicies(secgroupId) 356 if err != nil { 357 return errors.Wrapf(err, "DescribeSecurityGroupPolicies") 358 } 359 params = map[string]string{ 360 "SecurityGroupId": secgroupId, 361 } 362 if egressIndex == 0 && len(ruleSet.Egress) > 0 { 363 for idx, rule := range ruleSet.Egress { 364 params[fmt.Sprintf("SecurityGroupPolicySet.Egress.%d.PolicyIndex", idx)] = fmt.Sprintf("%d", rule.PolicyIndex) 365 } 366 } 367 if ingressIndex == 0 && len(ruleSet.Ingress) > 0 { 368 for idx, rule := range ruleSet.Ingress { 369 params[fmt.Sprintf("SecurityGroupPolicySet.Ingress.%d.PolicyIndex", idx)] = fmt.Sprintf("%d", rule.PolicyIndex) 370 } 371 } 372 if len(params) > 1 { 373 _, err = self.vpcRequest("DeleteSecurityGroupPolicies", params) 374 if err != nil { 375 return errors.Wrapf(err, "DeleteSecurityGroupPolicies") 376 } 377 } 378 } 379 return nil 380 } 381 382 func convertSecgroupRule(policyIndex int, rule cloudprovider.SecurityRule, params map[string]string) map[string]string { 383 direction := "Egress" 384 action := "accept" 385 if rule.Action == secrules.SecurityRuleDeny { 386 action = "drop" 387 } 388 protocol := "ALL" 389 if rule.Protocol != secrules.PROTO_ANY { 390 protocol = rule.Protocol 391 } 392 if rule.Direction == secrules.DIR_IN { 393 direction = "Ingress" 394 } 395 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.Action", direction, policyIndex)] = action 396 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.PolicyDescription", direction, policyIndex)] = rule.Description 397 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.Protocol", direction, policyIndex)] = protocol 398 if len(rule.PeerSecgroupId) > 0 { 399 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.SecurityGroupId", direction, policyIndex)] = rule.PeerSecgroupId 400 } else { 401 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.CidrBlock", direction, policyIndex)] = rule.IPNet.String() 402 } 403 if rule.Protocol == secrules.PROTO_TCP || rule.Protocol == secrules.PROTO_UDP { 404 port := "ALL" 405 if rule.PortEnd > 0 && rule.PortStart > 0 { 406 if rule.PortStart == rule.PortEnd { 407 port = fmt.Sprintf("%d", rule.PortStart) 408 } else { 409 port = fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd) 410 } 411 } else if len(rule.Ports) > 0 { 412 ports := []string{} 413 for _, _port := range rule.Ports { 414 ports = append(ports, fmt.Sprintf("%d", _port)) 415 } 416 port = strings.Join(ports, ",") 417 } 418 params[fmt.Sprintf("SecurityGroupPolicySet.%s.%d.Port", direction, policyIndex)] = port 419 } 420 return params 421 } 422 423 func (self *SRegion) DescribeSecurityGroupPolicies(secGroupId string) (*SecurityGroupPolicySet, error) { 424 params := make(map[string]string) 425 params["Region"] = self.Region 426 params["SecurityGroupId"] = secGroupId 427 428 body, err := self.vpcRequest("DescribeSecurityGroupPolicies", params) 429 if err != nil { 430 log.Errorf("DescribeSecurityGroupAttribute fail %s", err) 431 return nil, err 432 } 433 434 policies := SecurityGroupPolicySet{} 435 err = body.Unmarshal(&policies, "SecurityGroupPolicySet") 436 if err != nil { 437 return nil, errors.Wrapf(err, "body.Unmarshal") 438 } 439 return &policies, nil 440 } 441 442 func (self *SRegion) DeleteSecurityGroup(secGroupId string) error { 443 params := make(map[string]string) 444 params["Region"] = self.Region 445 params["SecurityGroupId"] = secGroupId 446 _, err := self.vpcRequest("DeleteSecurityGroup", params) 447 return err 448 } 449 450 type AddressTemplate struct { 451 AddressSet []string 452 AddressTemplateId string 453 AddressTemplateName string 454 CreatedTime time.Time 455 } 456 457 func (self *SRegion) AddressList(addressId, addressName string, offset, limit int) ([]AddressTemplate, int, error) { 458 params := map[string]string{} 459 filter := 0 460 if len(addressId) > 0 { 461 params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-id" 462 params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressId 463 filter++ 464 } 465 if len(addressName) > 0 { 466 params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-name" 467 params[fmt.Sprintf("Filters.%d.Values.0", filter)] = addressName 468 filter++ 469 } 470 params["Offset"] = fmt.Sprintf("%d", offset) 471 if limit == 0 { 472 limit = 20 473 } 474 params["Limit"] = fmt.Sprintf("%d", limit) 475 body, err := self.vpcRequest("DescribeAddressTemplates", params) 476 if err != nil { 477 return nil, 0, err 478 } 479 addressTemplates := []AddressTemplate{} 480 err = body.Unmarshal(&addressTemplates, "AddressTemplateSet") 481 if err != nil { 482 return nil, 0, err 483 } 484 total, _ := body.Float("TotalCount") 485 return addressTemplates, int(total), nil 486 } 487 488 type AddressTemplateGroup struct { 489 AddressTemplateIdSet []string 490 AddressTemplateGroupName string 491 AddressTemplateGroupId string 492 CreatedTime time.Time 493 } 494 495 func (self *SRegion) AddressGroupList(groupId, groupName string, offset, limit int) ([]AddressTemplateGroup, int, error) { 496 params := map[string]string{} 497 filter := 0 498 if len(groupId) > 0 { 499 params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-id" 500 params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupId 501 filter++ 502 } 503 if len(groupName) > 0 { 504 params[fmt.Sprintf("Filters.%d.Name", filter)] = "address-template-group-name" 505 params[fmt.Sprintf("Filters.%d.Values.0", filter)] = groupName 506 filter++ 507 } 508 params["Offset"] = fmt.Sprintf("%d", offset) 509 if limit == 0 { 510 limit = 20 511 } 512 params["Limit"] = fmt.Sprintf("%d", limit) 513 body, err := self.vpcRequest("DescribeAddressTemplateGroups", params) 514 if err != nil { 515 return nil, 0, err 516 } 517 addressTemplateGroups := []AddressTemplateGroup{} 518 err = body.Unmarshal(&addressTemplateGroups, "AddressTemplateGroupSet") 519 if err != nil { 520 return nil, 0, err 521 } 522 total, _ := body.Float("TotalCount") 523 return addressTemplateGroups, int(total), nil 524 } 525 526 func (self *SRegion) DeleteSecgroupRule(secId string, direction string, index int) error { 527 params := map[string]string{ 528 "SecurityGroupId": secId, 529 } 530 switch direction { 531 case secrules.DIR_IN: 532 params["SecurityGroupPolicySet.Ingress.0.PolicyIndex"] = fmt.Sprintf("%d", index) 533 case secrules.DIR_OUT: 534 params["SecurityGroupPolicySet.Egress.0.PolicyIndex"] = fmt.Sprintf("%d", index) 535 } 536 _, err := self.vpcRequest("DeleteSecurityGroupPolicies", params) 537 return errors.Wrapf(err, "DeleteSecurityGroupPolicies") 538 } 539 540 func (self *SRegion) CreateSecurityGroup(name, projectId, description string) (*SSecurityGroup, error) { 541 params := make(map[string]string) 542 params["Region"] = self.Region 543 params["GroupName"] = name 544 params["GroupDescription"] = description 545 if len(projectId) > 0 { 546 params["ProjectId"] = projectId 547 } 548 549 if len(description) == 0 { 550 params["GroupDescription"] = "Customize Create" 551 } 552 secgroup := SSecurityGroup{region: self} 553 body, err := self.vpcRequest("CreateSecurityGroup", params) 554 if err != nil { 555 return nil, errors.Wrap(err, "CreateSecurityGroup") 556 } 557 err = body.Unmarshal(&secgroup, "SecurityGroup") 558 if err != nil { 559 return nil, errors.Wrap(err, "body.Unmarshal") 560 } 561 return &secgroup, nil 562 } 563 564 func (self *SSecurityGroup) GetProjectId() string { 565 return self.ProjectId 566 } 567 568 func (self *SSecurityGroup) Delete() error { 569 return self.region.DeleteSecurityGroup(self.SecurityGroupId) 570 }