yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/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 aliyun 16 17 import ( 18 "fmt" 19 "strings" 20 "time" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/log" 24 "yunion.io/x/pkg/errors" 25 "yunion.io/x/pkg/util/secrules" 26 "yunion.io/x/pkg/utils" 27 28 "yunion.io/x/cloudmux/pkg/cloudprovider" 29 "yunion.io/x/cloudmux/pkg/multicloud" 30 ) 31 32 // {"CreationTime":"2017-03-19T13:37:48Z","Description":"System created security group.","SecurityGroupId":"sg-j6cannq0xxj2r9z0yxwl","SecurityGroupName":"sg-j6cannq0xxj2r9z0yxwl","Tags":{"Tag":[]},"VpcId":"vpc-j6c86z3sh8ufhgsxwme0q"} 33 // {"Description":"System created security group.","InnerAccessPolicy":"Accept","Permissions":{"Permission":[{"CreateTime":"2017-03-19T13:37:54Z","Description":"","DestCidrIp":"","DestGroupId":"","DestGroupName":"","DestGroupOwnerAccount":"","Direction":"ingress","IpProtocol":"ALL","NicType":"intranet","Policy":"Accept","PortRange":"-1/-1","Priority":110,"SourceCidrIp":"0.0.0.0/0","SourceGroupId":"","SourceGroupName":"","SourceGroupOwnerAccount":""},{"CreateTime":"2017-03-19T13:37:55Z","Description":"","DestCidrIp":"0.0.0.0/0","DestGroupId":"","DestGroupName":"","DestGroupOwnerAccount":"","Direction":"egress","IpProtocol":"ALL","NicType":"intranet","Policy":"Accept","PortRange":"-1/-1","Priority":110,"SourceCidrIp":"","SourceGroupId":"","SourceGroupName":"","SourceGroupOwnerAccount":""}]},"RegionId":"cn-hongkong","RequestId":"FBFE0950-5F2D-40DE-8C3C-E5A62AE7F7DA","SecurityGroupId":"sg-j6cannq0xxj2r9z0yxwl","SecurityGroupName":"sg-j6cannq0xxj2r9z0yxwl","VpcId":"vpc-j6c86z3sh8ufhgsxwme0q"} 34 35 type SecurityGroupPermissionNicType string 36 37 const ( 38 IntranetNicType SecurityGroupPermissionNicType = "intranet" 39 InternetNicType SecurityGroupPermissionNicType = "internet" 40 ) 41 42 type SPermission struct { 43 CreateTime time.Time 44 Description string 45 DestCidrIp string 46 DestGroupId string 47 DestGroupName string 48 DestGroupOwnerAccount string 49 Direction string 50 IpProtocol string 51 NicType SecurityGroupPermissionNicType 52 Policy string 53 PortRange string 54 Priority int 55 SourceCidrIp string 56 SourceGroupId string 57 SourceGroupName string 58 SourceGroupOwnerAccount string 59 } 60 61 type SPermissions struct { 62 Permission []SPermission 63 } 64 65 type SSecurityGroup struct { 66 multicloud.SSecurityGroup 67 AliyunTags 68 69 vpc *SVpc 70 CreationTime time.Time 71 Description string 72 SecurityGroupId string 73 SecurityGroupName string 74 VpcId string 75 InnerAccessPolicy string 76 Permissions SPermissions 77 RegionId string 78 } 79 80 func (self *SSecurityGroup) GetVpcId() string { 81 return self.VpcId 82 } 83 84 func (self *SSecurityGroup) GetId() string { 85 return self.SecurityGroupId 86 } 87 88 func (self *SSecurityGroup) GetGlobalId() string { 89 return self.SecurityGroupId 90 } 91 92 func (self *SSecurityGroup) GetDescription() string { 93 return self.Description 94 } 95 96 func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) { 97 rules := make([]cloudprovider.SecurityRule, 0) 98 secgrp, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId) 99 if err != nil { 100 return nil, err 101 } 102 for _, permission := range secgrp.Permissions.Permission { 103 rule, err := permission.toRule() 104 if err != nil { 105 log.Errorf("convert rule %s for group %s(%s) error: %v", permission.Description, self.SecurityGroupName, self.SecurityGroupId, err) 106 continue 107 } 108 rules = append(rules, rule) 109 } 110 return rules, nil 111 } 112 113 func (self *SSecurityGroup) GetName() string { 114 if len(self.SecurityGroupName) > 0 { 115 return self.SecurityGroupName 116 } 117 return self.SecurityGroupId 118 } 119 120 func (self *SSecurityGroup) GetStatus() string { 121 return "" 122 } 123 124 func (self *SSecurityGroup) IsEmulated() bool { 125 return false 126 } 127 128 func (self *SSecurityGroup) Refresh() error { 129 group, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId) 130 if err != nil { 131 return err 132 } 133 return jsonutils.Update(self, group) 134 } 135 136 func (self *SSecurityGroup) GetReferences() ([]cloudprovider.SecurityGroupReference, error) { 137 references, err := self.vpc.region.DescribeSecurityGroupReferences(self.SecurityGroupId) 138 if err != nil { 139 return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") 140 } 141 ret := []cloudprovider.SecurityGroupReference{} 142 for _, reference := range references { 143 if reference.SecurityGroupId == self.SecurityGroupId { 144 for _, sec := range reference.ReferencingSecurityGroups.ReferencingSecurityGroup { 145 ret = append(ret, cloudprovider.SecurityGroupReference{ 146 Id: sec.SecurityGroupId, 147 }) 148 } 149 } 150 } 151 return ret, nil 152 } 153 154 type ReferencingSecurityGroup struct { 155 AliUid string 156 SecurityGroupId string 157 } 158 159 type ReferencingSecurityGroups struct { 160 ReferencingSecurityGroup []ReferencingSecurityGroup 161 } 162 163 type SecurityGroupReferences struct { 164 SecurityGroupId string 165 ReferencingSecurityGroups ReferencingSecurityGroups 166 } 167 168 func (self *SRegion) DescribeSecurityGroupReferences(id string) ([]SecurityGroupReferences, error) { 169 params := map[string]string{ 170 "RegionId": self.RegionId, 171 "SecurityGroupId.1": id, 172 } 173 resp, err := self.ecsRequest("DescribeSecurityGroupReferences", params) 174 if err != nil { 175 return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") 176 } 177 ret := []SecurityGroupReferences{} 178 err = resp.Unmarshal(&ret, "SecurityGroupReferences", "SecurityGroupReference") 179 return ret, errors.Wrapf(err, "resp.Unmarshal") 180 } 181 182 func (self *SRegion) GetSecurityGroups(vpcId, name string, securityGroupIds []string, offset int, limit int) ([]SSecurityGroup, int, error) { 183 if limit > 50 || limit <= 0 { 184 limit = 50 185 } 186 params := make(map[string]string) 187 params["RegionId"] = self.RegionId 188 params["PageSize"] = fmt.Sprintf("%d", limit) 189 params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1) 190 if len(vpcId) > 0 { 191 params["VpcId"] = vpcId 192 } 193 if len(name) > 0 { 194 params["SecurityGroupName"] = name 195 } 196 197 if securityGroupIds != nil && len(securityGroupIds) > 0 { 198 params["SecurityGroupIds"] = jsonutils.Marshal(securityGroupIds).String() 199 } 200 201 body, err := self.ecsRequest("DescribeSecurityGroups", params) 202 if err != nil { 203 log.Errorf("GetSecurityGroups fail %s", err) 204 return nil, 0, err 205 } 206 207 secgrps := make([]SSecurityGroup, 0) 208 err = body.Unmarshal(&secgrps, "SecurityGroups", "SecurityGroup") 209 if err != nil { 210 log.Errorf("Unmarshal security groups fail %s", err) 211 return nil, 0, err 212 } 213 total, _ := body.Int("TotalCount") 214 return secgrps, int(total), nil 215 } 216 217 func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) { 218 params := make(map[string]string) 219 params["RegionId"] = self.RegionId 220 params["SecurityGroupId"] = secGroupId 221 222 body, err := self.ecsRequest("DescribeSecurityGroupAttribute", params) 223 if err != nil { 224 return nil, errors.Wrap(err, "DescribeSecurityGroupAttribute") 225 } 226 227 secgrp := SSecurityGroup{} 228 err = body.Unmarshal(&secgrp) 229 if err != nil { 230 return nil, errors.Wrap(err, "body.Unmarshal") 231 } 232 return &secgrp, nil 233 } 234 235 func (self *SRegion) CreateSecurityGroup(vpcId string, name string, desc string) (string, error) { 236 params := make(map[string]string) 237 if len(vpcId) > 0 { 238 params["VpcId"] = vpcId 239 } 240 241 if name == "Default" { 242 name = "Default-copy" 243 } 244 245 if len(name) > 0 { 246 params["SecurityGroupName"] = name 247 } 248 if len(desc) > 0 { 249 params["Description"] = desc 250 } 251 params["ClientToken"] = utils.GenRequestId(20) 252 253 body, err := self.ecsRequest("CreateSecurityGroup", params) 254 if err != nil { 255 return "", errors.Wrap(err, "CreateSecurityGroup") 256 } 257 return body.GetString("SecurityGroupId") 258 } 259 260 func (self *SRegion) modifySecurityGroupRule(secGrpId string, rule *secrules.SecurityRule) error { 261 params := make(map[string]string) 262 params["RegionId"] = self.RegionId 263 params["SecurityGroupId"] = secGrpId 264 params["NicType"] = string(IntranetNicType) 265 params["Description"] = rule.Description 266 params["PortRange"] = fmt.Sprintf("%d/%d", rule.PortStart, rule.PortEnd) 267 protocol := rule.Protocol 268 if len(rule.Protocol) == 0 || rule.Protocol == secrules.PROTO_ANY { 269 protocol = "all" 270 } 271 params["IpProtocol"] = protocol 272 if rule.PortStart < 1 && rule.PortEnd < 1 { 273 if protocol == "udp" || protocol == "tcp" { 274 params["PortRange"] = "1/65535" 275 } else { 276 params["PortRange"] = "-1/-1" 277 } 278 } 279 if rule.Action == secrules.SecurityRuleAllow { 280 params["Policy"] = "accept" 281 } else { 282 params["Policy"] = "drop" 283 } 284 params["Priority"] = fmt.Sprintf("%d", rule.Priority) 285 if rule.Direction == secrules.SecurityRuleIngress { 286 if rule.IPNet != nil { 287 params["SourceCidrIp"] = rule.IPNet.String() 288 } else { 289 params["SourceCidrIp"] = "0.0.0.0/0" 290 } 291 _, err := self.ecsRequest("ModifySecurityGroupRule", params) 292 return err 293 } else { // rule.Direction == secrules.SecurityRuleEgress { 294 //阿里云不支持出方向API接口调用 295 return nil 296 // if rule.IPNet != nil { 297 // params["DestCidrIp"] = rule.IPNet.String() 298 // } else { 299 // params["DestCidrIp"] = "0.0.0.0/0" 300 // } 301 // _, err := self.ecsRequest("ModifySecurityGroupRule", params) 302 // return err 303 } 304 } 305 306 func (self *SRegion) modifySecurityGroup(secGrpId string, name string, desc string) error { 307 params := make(map[string]string) 308 params["RegionId"] = self.RegionId 309 params["SecurityGroupId"] = secGrpId 310 params["SecurityGroupName"] = name 311 if len(desc) > 0 { 312 params["Description"] = desc 313 } 314 _, err := self.ecsRequest("ModifySecurityGroupAttribute", params) 315 return err 316 } 317 318 func (self *SRegion) AddSecurityGroupRules(secGrpId string, rule cloudprovider.SecurityRule) error { 319 if len(rule.Ports) != 0 { 320 for _, port := range rule.Ports { 321 rule.PortStart, rule.PortEnd = port, port 322 err := self.addSecurityGroupRule(secGrpId, rule) 323 if err != nil { 324 return errors.Wrapf(err, "addSecurityGroupRule %s", rule.String()) 325 } 326 } 327 return nil 328 } 329 return self.addSecurityGroupRule(secGrpId, rule) 330 } 331 332 func (self *SRegion) addSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error { 333 params := make(map[string]string) 334 params["RegionId"] = self.RegionId 335 params["SecurityGroupId"] = secGrpId 336 params["NicType"] = string(IntranetNicType) 337 params["Description"] = rule.Description 338 params["PortRange"] = fmt.Sprintf("%d/%d", rule.PortStart, rule.PortEnd) 339 protocol := rule.Protocol 340 if len(rule.Protocol) == 0 || rule.Protocol == secrules.PROTO_ANY { 341 protocol = "all" 342 } 343 params["IpProtocol"] = protocol 344 if rule.PortStart < 1 && rule.PortEnd < 1 { 345 if protocol == "udp" || protocol == "tcp" { 346 params["PortRange"] = "1/65535" 347 } else { 348 params["PortRange"] = "-1/-1" 349 } 350 } 351 if rule.Action == secrules.SecurityRuleAllow { 352 params["Policy"] = "accept" 353 } else { 354 params["Policy"] = "drop" 355 } 356 357 // 忽略地址为0.0.0.0/32这样的阿里云规则 358 if rule.IPNet.IP.String() == "0.0.0.0" && rule.IPNet.String() != "0.0.0.0/0" && len(rule.PeerSecgroupId) == 0 { 359 return nil 360 } 361 362 params["Priority"] = fmt.Sprintf("%d", rule.Priority) 363 if rule.Direction == secrules.SecurityRuleIngress { 364 if len(rule.PeerSecgroupId) > 0 { 365 params["SourceGroupId"] = rule.PeerSecgroupId 366 } else if rule.IPNet != nil { 367 params["SourceCidrIp"] = rule.IPNet.String() 368 } else { 369 params["SourceCidrIp"] = "0.0.0.0/0" 370 } 371 _, err := self.ecsRequest("AuthorizeSecurityGroup", params) 372 return err 373 } else { // rule.Direction == secrules.SecurityRuleEgress { 374 if len(rule.PeerSecgroupId) > 0 { 375 params["DestGroupId"] = rule.PeerSecgroupId 376 } else if rule.IPNet != nil { 377 params["DestCidrIp"] = rule.IPNet.String() 378 } else { 379 params["DestCidrIp"] = "0.0.0.0/0" 380 } 381 _, err := self.ecsRequest("AuthorizeSecurityGroupEgress", params) 382 return err 383 } 384 } 385 386 func (self *SRegion) DelSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error { 387 params := make(map[string]string) 388 params["RegionId"] = self.RegionId 389 params["SecurityGroupId"] = secGrpId 390 params["NicType"] = string(IntranetNicType) 391 params["PortRange"] = fmt.Sprintf("%d/%d", rule.PortStart, rule.PortEnd) 392 protocol := rule.Protocol 393 if len(rule.Protocol) == 0 || rule.Protocol == secrules.PROTO_ANY { 394 protocol = "all" 395 } 396 params["IpProtocol"] = protocol 397 if rule.PortStart < 1 && rule.PortEnd < 1 { 398 if protocol == "udp" || protocol == "tcp" { 399 params["PortRange"] = "1/65535" 400 } else { 401 params["PortRange"] = "-1/-1" 402 } 403 } 404 if rule.Action == secrules.SecurityRuleAllow { 405 params["Policy"] = "accept" 406 } else { 407 params["Policy"] = "drop" 408 } 409 params["Priority"] = fmt.Sprintf("%d", rule.Priority) 410 if rule.Direction == secrules.SecurityRuleIngress { 411 if len(rule.PeerSecgroupId) > 0 { 412 params["SourceGroupId"] = rule.PeerSecgroupId 413 } else if rule.IPNet != nil { 414 params["SourceCidrIp"] = rule.IPNet.String() 415 } else { 416 params["SourceCidrIp"] = "0.0.0.0/0" 417 } 418 _, err := self.ecsRequest("RevokeSecurityGroup", params) 419 return err 420 } else { // rule.Direction == secrules.SecurityRuleEgress { 421 if len(rule.PeerSecgroupId) > 0 { 422 params["DestGroupId"] = rule.PeerSecgroupId 423 } else if rule.IPNet != nil { 424 params["DestCidrIp"] = rule.IPNet.String() 425 } else { 426 params["DestCidrIp"] = "0.0.0.0/0" 427 } 428 _, err := self.ecsRequest("RevokeSecurityGroupEgress", params) 429 return err 430 } 431 } 432 433 func (self *SPermission) toRule() (cloudprovider.SecurityRule, error) { 434 rule := cloudprovider.SecurityRule{ 435 PeerSecgroupId: self.SourceGroupId, 436 SecurityRule: secrules.SecurityRule{ 437 Action: secrules.SecurityRuleDeny, 438 Direction: secrules.DIR_IN, 439 Priority: self.Priority, 440 Description: self.Description, 441 PortStart: -1, 442 PortEnd: -1, 443 }, 444 } 445 if strings.ToLower(self.Policy) == "accept" { 446 rule.Action = secrules.SecurityRuleAllow 447 } 448 449 cidr := self.SourceCidrIp 450 if self.Direction == "egress" { 451 rule.Direction = secrules.DIR_OUT 452 cidr = self.DestCidrIp 453 } 454 if len(self.SourceGroupId) > 0 || len(self.DestGroupId) > 0 { 455 cidr = "0.0.0.0/0" 456 rule.PeerSecgroupId = self.SourceGroupId + self.DestGroupId 457 } 458 459 rule.ParseCIDR(cidr) 460 461 switch strings.ToLower(self.IpProtocol) { 462 case "tcp", "udp", "icmp": 463 rule.Protocol = strings.ToLower(self.IpProtocol) 464 case "all": 465 rule.Protocol = secrules.PROTO_ANY 466 default: 467 return rule, fmt.Errorf("unsupported protocal %s", self.IpProtocol) 468 } 469 470 port, ports := "", strings.Split(self.PortRange, "/") 471 if ports[0] == ports[1] { 472 if ports[0] != "-1" { 473 port = ports[0] 474 } 475 } else if ports[0] != "1" && ports[1] != "65535" { 476 port = fmt.Sprintf("%s-%s", ports[0], ports[1]) 477 } 478 err := rule.ParsePorts(port) 479 if err != nil { 480 return rule, errors.Wrapf(err, "ParsePorts(%s)", port) 481 } 482 return rule, nil 483 } 484 485 func (self *SRegion) AssignSecurityGroup(secgroupId, instanceId string) error { 486 return self.SetSecurityGroups([]string{secgroupId}, instanceId) 487 } 488 489 func (self *SRegion) SetSecurityGroups(secgroupIds []string, instanceId string) error { 490 params := map[string]string{"InstanceId": instanceId} 491 for _, secgroupId := range secgroupIds { 492 params["SecurityGroupId"] = secgroupId 493 if _, err := self.ecsRequest("JoinSecurityGroup", params); err != nil { 494 return err 495 } 496 } 497 instance, err := self.GetInstance(instanceId) 498 if err != nil { 499 return err 500 } 501 for _, _secgroupId := range instance.SecurityGroupIds.SecurityGroupId { 502 if !utils.IsInStringArray(_secgroupId, secgroupIds) { 503 if err := self.leaveSecurityGroup(_secgroupId, instanceId); err != nil { 504 return err 505 } 506 } 507 } 508 return nil 509 } 510 511 func (self *SRegion) leaveSecurityGroup(secgroupId, instanceId string) error { 512 params := map[string]string{"InstanceId": instanceId, "SecurityGroupId": secgroupId} 513 _, err := self.ecsRequest("LeaveSecurityGroup", params) 514 return err 515 } 516 517 func (self *SRegion) DeleteSecurityGroup(secGrpId string) error { 518 params := make(map[string]string) 519 params["SecurityGroupId"] = secGrpId 520 521 _, err := self.ecsRequest("DeleteSecurityGroup", params) 522 if err != nil { 523 log.Errorf("Delete security group fail %s", err) 524 return err 525 } 526 return nil 527 } 528 529 func (self *SSecurityGroup) Delete() error { 530 return self.vpc.region.DeleteSecurityGroup(self.SecurityGroupId) 531 } 532 533 func (self *SSecurityGroup) GetProjectId() string { 534 return "" 535 } 536 537 func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { 538 for _, rule := range append(inDels, outDels...) { 539 err := self.vpc.region.DelSecurityGroupRule(self.SecurityGroupId, rule) 540 if err != nil { 541 return errors.Wrapf(err, "DelSecurityGroupRule(Name:%s priority: %d %s)", rule.Name, rule.Priority, rule.String()) 542 } 543 } 544 for _, rule := range append(inAdds, outAdds...) { 545 err := self.vpc.region.AddSecurityGroupRules(self.SecurityGroupId, rule) 546 if err != nil { 547 return errors.Wrapf(err, "AddSecurityGroupRules(priority: %d %s)", rule.Priority, rule.String()) 548 } 549 } 550 return nil 551 }