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  }