yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/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 hcs
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/url"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/pkg/errors"
    24  	"yunion.io/x/pkg/util/secrules"
    25  	"yunion.io/x/pkg/utils"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  )
    31  
    32  type SecurityGroupRule struct {
    33  	Direction       string `json:"direction"`
    34  	Ethertype       string `json:"ethertype"`
    35  	Id              string `json:"id"`
    36  	Description     string `json:"description"`
    37  	SecurityGroupId string `json:"security_group_id"`
    38  	RemoteGroupId   string `json:"remote_group_id"`
    39  }
    40  
    41  type SecurityGroupRuleDetail struct {
    42  	Direction       string `json:"direction"`
    43  	Ethertype       string `json:"ethertype"`
    44  	Id              string `json:"id"`
    45  	Description     string `json:"description"`
    46  	PortRangeMax    int64  `json:"port_range_max"`
    47  	PortRangeMin    int64  `json:"port_range_min"`
    48  	Protocol        string `json:"protocol"`
    49  	RemoteGroupId   string `json:"remote_group_id"`
    50  	RemoteIPPrefix  string `json:"remote_ip_prefix"`
    51  	SecurityGroupId string `json:"security_group_id"`
    52  	TenantId        string `json:"tenant_id"`
    53  }
    54  
    55  type SSecurityGroup struct {
    56  	multicloud.SSecurityGroup
    57  	HcsTags
    58  	region *SRegion
    59  
    60  	Id                  string              `json:"id"`
    61  	Name                string              `json:"name"`
    62  	Description         string              `json:"description"`
    63  	VpcId               string              `json:"vpc_id"`
    64  	EnterpriseProjectId string              `json:"enterprise_project_id "`
    65  	SecurityGroupRules  []SecurityGroupRule `json:"security_group_rules"`
    66  }
    67  
    68  // 判断是否兼容云端安全组规则
    69  func compatibleSecurityGroupRule(r SecurityGroupRule) bool {
    70  	// 忽略了源地址是安全组的规则
    71  	if len(r.RemoteGroupId) > 0 {
    72  		return false
    73  	}
    74  
    75  	// 忽略IPV6
    76  	if r.Ethertype == "IPv6" {
    77  		return false
    78  	}
    79  
    80  	return true
    81  }
    82  
    83  func (self *SSecurityGroup) GetId() string {
    84  	return self.Id
    85  }
    86  
    87  func (self *SSecurityGroup) GetVpcId() string {
    88  	return self.VpcId
    89  }
    90  
    91  func (self *SSecurityGroup) GetName() string {
    92  	if len(self.Name) > 0 {
    93  		return self.Name
    94  	}
    95  	return self.Id
    96  }
    97  
    98  func (self *SSecurityGroup) GetGlobalId() string {
    99  	return self.Id
   100  }
   101  
   102  func (self *SSecurityGroup) GetStatus() string {
   103  	return ""
   104  }
   105  
   106  func (self *SSecurityGroup) Refresh() error {
   107  	if new, err := self.region.GetSecurityGroupDetails(self.GetId()); err != nil {
   108  		return err
   109  	} else {
   110  		return jsonutils.Update(self, new)
   111  	}
   112  }
   113  
   114  func (self *SSecurityGroup) IsEmulated() bool {
   115  	return false
   116  }
   117  
   118  func (self *SSecurityGroup) GetDescription() string {
   119  	if self.Description == self.VpcId {
   120  		return ""
   121  	}
   122  	return self.Description
   123  }
   124  
   125  // todo: 这里需要优化查询太多了
   126  func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) {
   127  	rules := make([]cloudprovider.SecurityRule, 0)
   128  	for _, r := range self.SecurityGroupRules {
   129  		if !compatibleSecurityGroupRule(r) {
   130  			continue
   131  		}
   132  
   133  		rule, err := self.GetSecurityRule(r.Id)
   134  		if err != nil {
   135  			return rules, err
   136  		}
   137  
   138  		rules = append(rules, rule)
   139  	}
   140  
   141  	return rules, nil
   142  }
   143  
   144  func (self *SSecurityGroup) GetSecurityRule(ruleId string) (cloudprovider.SecurityRule, error) {
   145  	remoteRule := SecurityGroupRuleDetail{}
   146  	err := self.region.vpcGet("security-group-rules/"+ruleId, &remoteRule)
   147  	if err != nil {
   148  		return cloudprovider.SecurityRule{}, err
   149  	}
   150  
   151  	var direction secrules.TSecurityRuleDirection
   152  	if remoteRule.Direction == "ingress" {
   153  		direction = secrules.SecurityRuleIngress
   154  	} else {
   155  		direction = secrules.SecurityRuleEgress
   156  	}
   157  
   158  	protocol := secrules.PROTO_ANY
   159  	if remoteRule.Protocol != "" {
   160  		protocol = remoteRule.Protocol
   161  	}
   162  
   163  	var portStart int
   164  	var portEnd int
   165  	if protocol == secrules.PROTO_ICMP {
   166  		portStart = -1
   167  		portEnd = -1
   168  	} else {
   169  		portStart = int(remoteRule.PortRangeMin)
   170  		portEnd = int(remoteRule.PortRangeMax)
   171  	}
   172  
   173  	ipNet := &net.IPNet{}
   174  	if len(remoteRule.RemoteIPPrefix) > 0 {
   175  		_, ipNet, err = net.ParseCIDR(remoteRule.RemoteIPPrefix)
   176  	} else {
   177  		_, ipNet, err = net.ParseCIDR("0.0.0.0/0")
   178  	}
   179  
   180  	if err != nil {
   181  		return cloudprovider.SecurityRule{}, err
   182  	}
   183  
   184  	rule := cloudprovider.SecurityRule{
   185  		ExternalId: ruleId,
   186  		SecurityRule: secrules.SecurityRule{
   187  			Priority:    1,
   188  			Action:      secrules.SecurityRuleAllow,
   189  			IPNet:       ipNet,
   190  			Protocol:    protocol,
   191  			Direction:   direction,
   192  			PortStart:   portStart,
   193  			PortEnd:     portEnd,
   194  			Ports:       nil,
   195  			Description: remoteRule.Description,
   196  		},
   197  	}
   198  
   199  	err = rule.ValidateRule()
   200  	return rule, err
   201  }
   202  
   203  func (self *SRegion) GetSecurityGroupDetails(id string) (*SSecurityGroup, error) {
   204  	ret := &SSecurityGroup{region: self}
   205  	return ret, self.vpcGet("security-groups/"+id, ret)
   206  }
   207  
   208  // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090617.html
   209  func (self *SRegion) GetSecurityGroups(vpcId string) ([]SSecurityGroup, error) {
   210  	query := url.Values{}
   211  	if len(vpcId) > 0 && !utils.IsInStringArray(vpcId, []string{"default", api.NORMAL_VPC_ID}) { // vpc_id = default or normal 时报错 '{"code":"VPC.0601","message":"Query security groups error vpcId is invalid."}'
   212  		query.Set("vpc_id", vpcId)
   213  	}
   214  	ret := []SSecurityGroup{}
   215  	return ret, self.vpcList("security-groups", query, &ret)
   216  }
   217  
   218  func (self *SSecurityGroup) GetProjectId() string {
   219  	return ""
   220  }
   221  
   222  func (self *SSecurityGroup) Delete() error {
   223  	return self.region.DeleteSecurityGroup(self.Id)
   224  }
   225  
   226  func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error {
   227  	for _, r := range append(inDels, outDels...) {
   228  		err := self.region.DeleteSecurityGroupRule(r.ExternalId)
   229  		if err != nil {
   230  			return errors.Wrapf(err, "delSecurityGroupRule(%s %s)", r.ExternalId, r.String())
   231  		}
   232  	}
   233  	for _, r := range append(inAdds, outAdds...) {
   234  		err := self.region.CreateSecurityGroupRule(self.Id, r)
   235  		if err != nil {
   236  			return errors.Wrapf(err, "addSecurityGroupRule(%d %s)", r.Priority, r.String())
   237  		}
   238  	}
   239  	return nil
   240  }
   241  
   242  func (self *SRegion) DeleteSecurityGroup(id string) error {
   243  	return self.vpcDelete("security-groups/" + id)
   244  }
   245  
   246  func (self *SRegion) DeleteSecurityGroupRule(ruleId string) error {
   247  	return self.vpcDelete("security-group-rules/" + ruleId)
   248  }
   249  
   250  func (self *SRegion) CreateSecurityGroupRule(secGrpId string, rule cloudprovider.SecurityRule) error {
   251  	direction := ""
   252  	if rule.Direction == secrules.SecurityRuleIngress {
   253  		direction = "ingress"
   254  	} else {
   255  		direction = "egress"
   256  	}
   257  
   258  	protocal := rule.Protocol
   259  	if rule.Protocol == secrules.PROTO_ANY {
   260  		protocal = ""
   261  	}
   262  
   263  	// imcp协议默认为any
   264  	if rule.Protocol == secrules.PROTO_ICMP {
   265  		return self.AddSecurityGroupRule(secGrpId, direction, "-1", "-1", protocal, rule.IPNet.String())
   266  	}
   267  
   268  	if len(rule.Ports) > 0 {
   269  		for _, port := range rule.Ports {
   270  			portStr := fmt.Sprintf("%d", port)
   271  			err := self.AddSecurityGroupRule(secGrpId, direction, portStr, portStr, protocal, rule.IPNet.String())
   272  			if err != nil {
   273  				return err
   274  			}
   275  		}
   276  	} else {
   277  		portStart := fmt.Sprintf("%d", rule.PortStart)
   278  		portEnd := fmt.Sprintf("%d", rule.PortEnd)
   279  		err := self.AddSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocal, rule.IPNet.String())
   280  		if err != nil {
   281  			return err
   282  		}
   283  	}
   284  	return nil
   285  }
   286  
   287  func (self *SRegion) AddSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocol, ipNet string) error {
   288  	rule := map[string]interface{}{
   289  		"security_group_id": secGrpId,
   290  		"direction":         direction,
   291  		"remote_ip_prefix":  ipNet,
   292  		"ethertype":         "IPV4",
   293  	}
   294  	if len(portStart) > 0 && portStart != "0" && portStart != "-1" {
   295  		rule["port_range_min"] = portStart
   296  	}
   297  	if len(portEnd) > 0 && portEnd != "0" && portEnd != "-1" {
   298  		rule["port_range_max"] = portEnd
   299  	}
   300  	if len(protocol) > 0 {
   301  		rule["protocol"] = protocol
   302  	}
   303  	params := map[string]interface{}{
   304  		"security_group_rule": rule,
   305  	}
   306  	return self.vpcCreate("security-group-rules", params, nil)
   307  }
   308  
   309  func (self *SRegion) CreateSecurityGroup(vpcId string, name string, desc string) (*SSecurityGroup, error) {
   310  	params := map[string]interface{}{
   311  		"security_group": map[string]interface{}{
   312  			"name":   name,
   313  			"vpc_id": vpcId,
   314  		},
   315  	}
   316  	ret := &SSecurityGroup{region: self}
   317  	return ret, self.vpcCreate("security-groups", params, ret)
   318  }
   319  
   320  func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
   321  	return self.CreateSecurityGroup(conf.VpcId, conf.Name, conf.Desc)
   322  }
   323  
   324  func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) {
   325  	return self.GetSecurityGroupDetails(secgroupId)
   326  }
   327  
   328  func (self *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) {
   329  	secgroups, err := self.GetSecurityGroups(opts.VpcId)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	for i := range secgroups {
   334  		if secgroups[i].GetName() == opts.Name {
   335  			secgroups[i].region = self
   336  			return &secgroups[i], nil
   337  		}
   338  	}
   339  	return nil, errors.Wrapf(cloudprovider.ErrNotFound, "vpc:%s, name:%s", opts.VpcId, opts.Name)
   340  }