yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/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 aws 16 17 import ( 18 "fmt" 19 "math/rand" 20 "strings" 21 "time" 22 23 "github.com/aws/aws-sdk-go/service/ec2" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 "yunion.io/x/pkg/errors" 28 "yunion.io/x/pkg/util/secrules" 29 30 "yunion.io/x/cloudmux/pkg/cloudprovider" 31 "yunion.io/x/onecloud/pkg/httperrors" 32 "yunion.io/x/cloudmux/pkg/multicloud" 33 ) 34 35 type SSecurityGroup struct { 36 multicloud.SSecurityGroup 37 AwsTags 38 vpc *SVpc 39 40 RegionId string 41 VpcId string 42 SecurityGroupId string 43 Description string 44 SecurityGroupName string 45 Permissions []cloudprovider.SecurityRule 46 47 // CreationTime time.Time 48 // InnerAccessPolicy string 49 } 50 51 func randomString(prefix string, length int) string { 52 bytes := []byte("0123456789abcdefghijklmnopqrstuvwxyz") 53 result := []byte{} 54 r := rand.New(rand.NewSource(time.Now().UnixNano())) 55 for i := 0; i < length; i++ { 56 result = append(result, bytes[r.Intn(len(bytes))]) 57 } 58 return prefix + string(result) 59 } 60 61 func (self *SSecurityGroup) GetId() string { 62 return self.SecurityGroupId 63 } 64 65 func (self *SSecurityGroup) GetVpcId() string { 66 return self.VpcId 67 } 68 69 func (self *SSecurityGroup) GetName() string { 70 if len(self.SecurityGroupName) > 0 { 71 return self.SecurityGroupName 72 } 73 return self.SecurityGroupId 74 } 75 76 func (self *SSecurityGroup) GetGlobalId() string { 77 return self.SecurityGroupId 78 } 79 80 func (self *SSecurityGroup) GetStatus() string { 81 return "" 82 } 83 84 func (self *SSecurityGroup) Refresh() error { 85 if new, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil { 86 return err 87 } else { 88 return jsonutils.Update(self, new) 89 } 90 } 91 92 func (self *SSecurityGroup) IsEmulated() bool { 93 return false 94 } 95 96 func (self *SSecurityGroup) GetDescription() string { 97 return self.Description 98 } 99 100 func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) { 101 secgrp, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId) 102 if err != nil { 103 return nil, errors.Wrap(err, "GetSecurityGroupDetails") 104 } 105 return secgrp.Permissions, nil 106 } 107 108 func (self *SRegion) addSecurityGroupRules(secGrpId string, rule cloudprovider.SecurityRule) error { 109 if len(rule.Ports) != 0 { 110 for _, port := range rule.Ports { 111 rule.PortStart, rule.PortEnd = port, port 112 err := self.addSecurityGroupRule(secGrpId, rule) 113 if err != nil { 114 return errors.Wrapf(err, "addSecurityGroupRule(%d %s)", rule.Priority, rule.String()) 115 } 116 } 117 return nil 118 } 119 return self.addSecurityGroupRule(secGrpId, rule) 120 } 121 122 func (self *SRegion) addSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error { 123 ec2Client, err := self.getEc2Client() 124 if err != nil { 125 return errors.Wrap(err, "getEc2Client") 126 } 127 128 ipPermissions, err := YunionSecRuleToAws(rule) 129 log.Debugf("Aws security group rule: %s", ipPermissions) 130 if err != nil { 131 return err 132 } 133 134 if rule.Direction == secrules.SecurityRuleIngress { 135 params := &ec2.AuthorizeSecurityGroupIngressInput{} 136 params.SetGroupId(secGrpId) 137 params.SetIpPermissions(ipPermissions) 138 _, err = ec2Client.AuthorizeSecurityGroupIngress(params) 139 } 140 141 if rule.Direction == secrules.SecurityRuleEgress { 142 params := &ec2.AuthorizeSecurityGroupEgressInput{} 143 params.SetGroupId(secGrpId) 144 params.SetIpPermissions(ipPermissions) 145 _, err = ec2Client.AuthorizeSecurityGroupEgress(params) 146 } 147 148 if err != nil && strings.Contains(err.Error(), "InvalidPermission.Duplicate") { 149 log.Debugf("addSecurityGroupRule %s %s", rule.Direction, err.Error()) 150 return nil 151 } 152 153 return err 154 } 155 156 func (self *SRegion) DelSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error { 157 ec2Client, err := self.getEc2Client() 158 if err != nil { 159 return errors.Wrap(err, "getEc2Client") 160 } 161 162 ipPermissions, err := YunionSecRuleToAws(rule) 163 if err != nil { 164 return err 165 } 166 167 if rule.Direction == secrules.SecurityRuleIngress { 168 params := &ec2.RevokeSecurityGroupIngressInput{} 169 params.SetGroupId(secGrpId) 170 params.SetIpPermissions(ipPermissions) 171 _, err = ec2Client.RevokeSecurityGroupIngress(params) 172 } 173 174 if rule.Direction == secrules.SecurityRuleEgress { 175 params := &ec2.RevokeSecurityGroupEgressInput{} 176 params.SetGroupId(secGrpId) 177 params.SetIpPermissions(ipPermissions) 178 _, err = ec2Client.RevokeSecurityGroupEgress(params) 179 } 180 181 if err != nil { 182 log.Debugf("delSecurityGroupRule %s %s", rule.Direction, err.Error()) 183 return err 184 } 185 return nil 186 } 187 188 func (self *SRegion) updateSecurityGroupRuleDescription(secGrpId string, rule cloudprovider.SecurityRule) error { 189 ipPermissions, err := YunionSecRuleToAws(rule) 190 if err != nil { 191 return err 192 } 193 194 ec2Client, err := self.getEc2Client() 195 if err != nil { 196 return errors.Wrap(err, "getEc2Client") 197 } 198 199 if rule.Direction == secrules.SecurityRuleIngress { 200 params := &ec2.UpdateSecurityGroupRuleDescriptionsIngressInput{} 201 params.SetGroupId(secGrpId) 202 params.SetIpPermissions(ipPermissions) 203 ret, err := ec2Client.UpdateSecurityGroupRuleDescriptionsIngress(params) 204 if err != nil { 205 return err 206 } else if ret.Return != nil && *ret.Return == false { 207 log.Debugf("update security group %s rule description failed: %s", secGrpId, ipPermissions) 208 } 209 } 210 211 if rule.Direction == secrules.SecurityRuleEgress { 212 params := &ec2.UpdateSecurityGroupRuleDescriptionsEgressInput{} 213 params.SetGroupId(secGrpId) 214 params.SetIpPermissions(ipPermissions) 215 ret, err := ec2Client.UpdateSecurityGroupRuleDescriptionsEgress(params) 216 if err != nil { 217 return err 218 } else if ret.Return != nil && *ret.Return == false { 219 log.Debugf("update security group %s rule description failed: %s", secGrpId, ipPermissions) 220 } 221 } 222 return nil 223 } 224 225 func (self *SRegion) CreateSecurityGroup(vpcId string, name string, secgroupIdTag string, desc string) (string, error) { 226 ec2Client, err := self.getEc2Client() 227 if err != nil { 228 return "", errors.Wrap(err, "getEc2Client") 229 } 230 231 params := &ec2.CreateSecurityGroupInput{} 232 params.SetVpcId(vpcId) 233 // 这里的描述aws 上层代码拼接的描述。并非用户提交的描述,用户描述放置在Yunion本地数据库中。) 234 if len(desc) == 0 { 235 desc = "vpc default group" 236 } 237 params.SetDescription(desc) 238 if strings.ToLower(name) == "default" { 239 name = randomString(fmt.Sprintf("%s-", vpcId), 9) 240 } 241 params.SetGroupName(name) 242 243 group, err := ec2Client.CreateSecurityGroup(params) 244 if err != nil { 245 return "", err 246 } 247 248 tagspec := TagSpec{ResourceType: "security-group"} 249 if len(secgroupIdTag) > 0 { 250 tagspec.SetTag("id", secgroupIdTag) 251 } 252 tagspec.SetNameTag(name) 253 tagspec.SetDescTag(desc) 254 tags, _ := tagspec.GetTagSpecifications() 255 tagParams := &ec2.CreateTagsInput{} 256 tagParams.SetResources([]*string{group.GroupId}) 257 tagParams.SetTags(tags.Tags) 258 _, err = ec2Client.CreateTags(tagParams) 259 if err != nil { 260 return "", err 261 } 262 263 return *group.GroupId, nil 264 } 265 266 func (self *SRegion) createDefaultSecurityGroup(vpcId string) (string, error) { 267 name := randomString(fmt.Sprintf("%s-", vpcId), 9) 268 secId, err := self.CreateSecurityGroup(vpcId, name, "default", "vpc default group") 269 if err != nil { 270 return "", err 271 } 272 273 rule := cloudprovider.SecurityRule{ 274 SecurityRule: secrules.SecurityRule{ 275 Priority: 1, 276 Action: secrules.SecurityRuleAllow, 277 Protocol: "", 278 Direction: secrules.SecurityRuleIngress, 279 PortStart: -1, 280 PortEnd: -1, 281 }, 282 } 283 284 err = self.addSecurityGroupRule(secId, rule) 285 if err != nil { 286 return "", err 287 } 288 return secId, nil 289 } 290 291 func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) { 292 if len(secGroupId) == 0 { 293 return nil, fmt.Errorf("GetSecurityGroupDetails security group id should not be empty.") 294 } 295 params := &ec2.DescribeSecurityGroupsInput{} 296 params.SetGroupIds([]*string{&secGroupId}) 297 298 ec2Client, err := self.getEc2Client() 299 if err != nil { 300 return nil, errors.Wrap(err, "getEc2Client") 301 } 302 ret, err := ec2Client.DescribeSecurityGroups(params) 303 err = parseNotFoundError(err) 304 if err != nil { 305 return nil, errors.Wrap(err, "DescribeSecurityGroups") 306 } 307 308 if len(ret.SecurityGroups) == 1 { 309 s := ret.SecurityGroups[0] 310 vpc, err := self.getVpc(*s.VpcId) 311 if err != nil { 312 return nil, fmt.Errorf("vpc %s not found", *s.VpcId) 313 } 314 315 permissions := self.getSecRules(s.IpPermissions, s.IpPermissionsEgress) 316 317 return &SSecurityGroup{ 318 vpc: vpc, 319 Description: *s.Description, 320 SecurityGroupId: *s.GroupId, 321 SecurityGroupName: *s.GroupName, 322 VpcId: *s.VpcId, 323 Permissions: permissions, 324 RegionId: self.RegionId, 325 }, nil 326 } else { 327 return nil, fmt.Errorf("required one security group. but found: %d", len(ret.SecurityGroups)) 328 } 329 } 330 331 func (self *SRegion) getSecurityGroupById(vpcId, secgroupId string) (*SSecurityGroup, error) { 332 if len(secgroupId) == 0 { 333 return nil, httperrors.NewInputParameterError("security group id should not be empty") 334 } 335 336 secgroups, total, err := self.GetSecurityGroups(vpcId, "", secgroupId, 0, 0) 337 if err != nil { 338 log.Errorf("GetSecurityGroups vpc %s secgroupId %s: %s", vpcId, secgroupId, err) 339 return nil, errors.Wrap(err, "GetSecurityGroups") 340 } 341 342 if total != 1 { 343 log.Debugf("failed to find SecurityGroup %s: %d found", secgroupId, total) 344 return nil, httperrors.NewNotFoundError("failed to find SecurityGroup %s", secgroupId) 345 } 346 return &secgroups[0], nil 347 } 348 349 func (self *SRegion) addTagToSecurityGroup(secgroupId, key, value string, index int) error { 350 return nil 351 } 352 353 func (self *SRegion) modifySecurityGroup(secGrpId string, name string, desc string) error { 354 tagspec := TagSpec{ResourceType: "security-group"} 355 tagspec.SetNameTag(name) 356 tagspec.SetDescTag(desc) 357 ec2Tags, _ := tagspec.GetTagSpecifications() 358 params := &ec2.CreateTagsInput{} 359 params.SetTags(ec2Tags.Tags) 360 params.SetResources([]*string{&secGrpId}) 361 362 ec2Client, err := self.getEc2Client() 363 if err != nil { 364 return errors.Wrap(err, "getEc2Client") 365 } 366 _, err = ec2Client.CreateTags(params) 367 if err != nil { 368 return errors.Wrap(err, "CreateTags") 369 } 370 371 return nil 372 } 373 374 func (self *SRegion) getSecRules(ingress []*ec2.IpPermission, egress []*ec2.IpPermission) []cloudprovider.SecurityRule { 375 rules := []cloudprovider.SecurityRule{} 376 for _, p := range ingress { 377 ret, err := AwsIpPermissionToYunion(secrules.SecurityRuleIngress, *p) 378 if err != nil { 379 log.Debugln(err) 380 } 381 382 for _, rule := range ret { 383 rules = append(rules, rule) 384 } 385 } 386 387 for _, p := range egress { 388 ret, err := AwsIpPermissionToYunion(secrules.SecurityRuleEgress, *p) 389 if err != nil { 390 log.Debugln(err) 391 } 392 393 for _, rule := range ret { 394 rules = append(rules, rule) 395 } 396 } 397 398 return rules 399 } 400 401 func (self *SRegion) GetSecurityGroups(vpcId string, name string, secgroupId string, offset int, limit int) ([]SSecurityGroup, int, error) { 402 params := &ec2.DescribeSecurityGroupsInput{} 403 filters := make([]*ec2.Filter, 0) 404 if len(vpcId) > 0 { 405 filters = AppendSingleValueFilter(filters, "vpc-id", vpcId) 406 } 407 408 if len(name) > 0 { 409 filters = AppendSingleValueFilter(filters, "group-name", name) 410 } 411 412 if len(secgroupId) > 0 { 413 params.SetGroupIds([]*string{&secgroupId}) 414 } 415 416 if len(filters) > 0 { 417 params.SetFilters(filters) 418 } 419 420 ec2Client, err := self.getEc2Client() 421 if err != nil { 422 return nil, 0, errors.Wrap(err, "getEc2Client") 423 } 424 ret, err := ec2Client.DescribeSecurityGroups(params) 425 err = parseNotFoundError(err) 426 if err != nil { 427 return nil, 0, err 428 } 429 430 securityGroups := []SSecurityGroup{} 431 for _, item := range ret.SecurityGroups { 432 if err := FillZero(item); err != nil { 433 return nil, 0, err 434 } 435 436 if len(*item.VpcId) == 0 { 437 log.Debugf("ingored: security group with no vpc binded") 438 continue 439 } 440 441 vpc, err := self.getVpc(*item.VpcId) 442 if err != nil { 443 log.Errorf("vpc %s not found", *item.VpcId) 444 continue 445 } 446 447 permissions := self.getSecRules(item.IpPermissions, item.IpPermissionsEgress) 448 group := SSecurityGroup{ 449 vpc: vpc, 450 Description: *item.Description, 451 SecurityGroupId: *item.GroupId, 452 SecurityGroupName: *item.GroupName, 453 VpcId: *item.VpcId, 454 Permissions: permissions, 455 RegionId: self.RegionId, 456 // Tags: *item.Tags, 457 } 458 459 securityGroups = append(securityGroups, group) 460 } 461 462 return securityGroups, len(securityGroups), nil 463 } 464 465 func (self *SSecurityGroup) GetProjectId() string { 466 return "" 467 } 468 469 func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { 470 for _, r := range append(inDels, outDels...) { 471 err := self.vpc.region.DelSecurityGroupRule(self.SecurityGroupId, r) 472 if err != nil { 473 if strings.Contains(err.Error(), "InvalidPermission.NotFound") { 474 continue 475 } 476 return errors.Wrapf(err, "delSecurityGroupRule %s %d %s", r.Name, r.Priority, r.String()) 477 } 478 } 479 480 for _, r := range append(inAdds, outAdds...) { 481 err := self.vpc.region.addSecurityGroupRules(self.SecurityGroupId, r) 482 if err != nil { 483 return errors.Wrapf(err, "addSecurityGroupRules %d %s", r.Priority, r.String()) 484 } 485 } 486 return nil 487 } 488 489 func (self *SSecurityGroup) Delete() error { 490 return self.vpc.region.DeleteSecurityGroup(self.SecurityGroupId) 491 }