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