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  }