yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/google/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 google
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    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  const (
    33  	SECGROUP_TYPE_SERVICE_ACCOUNT = "serviceAccount"
    34  	SECGROUP_TYPE_TAG             = "tag"
    35  )
    36  
    37  type SFirewallAction struct {
    38  	IPProtocol string
    39  	Ports      []string
    40  }
    41  
    42  type SFirewall struct {
    43  	Id                    string
    44  	CreationTimestamp     time.Time
    45  	Name                  string
    46  	Description           string
    47  	Network               string
    48  	Priority              int
    49  	SourceRanges          []string
    50  	DestinationRanges     []string
    51  	TargetServiceAccounts []string
    52  	TargetTags            []string
    53  	Allowed               []SFirewallAction
    54  	Denied                []SFirewallAction
    55  	Direction             string
    56  	Disabled              bool
    57  	SelfLink              string
    58  	Kind                  string
    59  }
    60  
    61  type SSecurityGroup struct {
    62  	multicloud.SSecurityGroup
    63  	GoogleTags
    64  	gvpc *SGlobalNetwork
    65  
    66  	ServiceAccount string
    67  	Tag            string
    68  }
    69  
    70  func (self *SGoogleClient) GetFirewalls(network string, maxResults int, pageToken string) ([]SFirewall, error) {
    71  	firewalls := []SFirewall{}
    72  	params := map[string]string{"filter": "disabled = false"}
    73  	resource := "global/firewalls"
    74  	if len(network) > 0 {
    75  		params["filter"] = fmt.Sprintf(`(disabled = false) AND (network="%s")`, network)
    76  	}
    77  	return firewalls, self._ecsListAll("GET", resource, params, &firewalls)
    78  }
    79  
    80  func (self *SGoogleClient) GetFirewall(id string) (*SFirewall, error) {
    81  	firewall := &SFirewall{}
    82  	return firewall, self.ecsGet("global/firewalls", id, firewall)
    83  }
    84  
    85  func (firewall *SFirewall) _toRules(action secrules.TSecurityRuleAction) ([]cloudprovider.SecurityRule, error) {
    86  	rules := []cloudprovider.SecurityRule{}
    87  	list := firewall.Allowed
    88  	if action == secrules.SecurityRuleDeny {
    89  		list = firewall.Denied
    90  	}
    91  	for _, allow := range list {
    92  		rule := cloudprovider.SecurityRule{
    93  			ExternalId: firewall.SelfLink,
    94  			SecurityRule: secrules.SecurityRule{
    95  				Action:    action,
    96  				Direction: secrules.DIR_IN,
    97  				Priority:  firewall.Priority,
    98  			},
    99  		}
   100  		if firewall.Direction == "EGRESS" {
   101  			rule.Direction = secrules.DIR_OUT
   102  		}
   103  		switch allow.IPProtocol {
   104  		case "tcp", "udp", "icmp":
   105  			rule.Protocol = allow.IPProtocol
   106  		case "all":
   107  			rule.Protocol = secrules.PROTO_ANY
   108  		default:
   109  			return nil, fmt.Errorf("unsupport protocol %s", allow.IPProtocol)
   110  		}
   111  		ipRanges := firewall.SourceRanges
   112  		if rule.Direction == secrules.DIR_OUT {
   113  			ipRanges = firewall.DestinationRanges
   114  		}
   115  		for _, ipRange := range ipRanges {
   116  			rule.ParseCIDR(ipRange)
   117  			ports := []int{}
   118  			for _, port := range allow.Ports {
   119  				if strings.Index(port, "-") > 0 {
   120  					port = strings.Replace(port, "0-", "1-", 1)
   121  					err := rule.ParsePorts(port)
   122  					if err != nil {
   123  						return nil, errors.Wrapf(err, "Parse port %s", port)
   124  					}
   125  					rules = append(rules, rule)
   126  				} else {
   127  					_port, err := strconv.Atoi(port)
   128  					if err != nil {
   129  						return nil, errors.Wrapf(err, "Atio port %s", port)
   130  					}
   131  					ports = append(ports, _port)
   132  				}
   133  			}
   134  			if len(ports) > 0 {
   135  				rule.Ports = ports
   136  				rule.PortStart = -1
   137  				rule.PortEnd = -1
   138  				rules = append(rules, rule)
   139  			}
   140  			if len(allow.Ports) == 0 {
   141  				rule.Ports = []int{}
   142  				rule.PortStart = -1
   143  				rule.PortEnd = -1
   144  				rules = append(rules, rule)
   145  			}
   146  		}
   147  	}
   148  	return rules, nil
   149  }
   150  
   151  func (firewall *SFirewall) toRules() ([]cloudprovider.SecurityRule, error) {
   152  	rules := []cloudprovider.SecurityRule{}
   153  	_rules, err := firewall._toRules(secrules.SecurityRuleAllow)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	rules = append(rules, _rules...)
   158  	_rules, err = firewall._toRules(secrules.SecurityRuleDeny)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	rules = append(rules, _rules...)
   163  	return rules, nil
   164  }
   165  
   166  func (secgroup *SSecurityGroup) GetId() string {
   167  	return secgroup.gvpc.GetGlobalId()
   168  }
   169  
   170  func (secgroup *SSecurityGroup) GetGlobalId() string {
   171  	if len(secgroup.Tag) > 0 {
   172  		return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_TAG, secgroup.Tag)
   173  	}
   174  	if len(secgroup.ServiceAccount) > 0 {
   175  		return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_SERVICE_ACCOUNT, secgroup.ServiceAccount)
   176  	}
   177  	return secgroup.GetId()
   178  }
   179  
   180  func (secgroup *SSecurityGroup) GetDescription() string {
   181  	return ""
   182  }
   183  
   184  func (secgroup *SSecurityGroup) GetName() string {
   185  	if len(secgroup.Tag) > 0 {
   186  		return secgroup.Tag
   187  	}
   188  	if len(secgroup.ServiceAccount) > 0 {
   189  		return secgroup.ServiceAccount
   190  	}
   191  	return secgroup.gvpc.Name
   192  }
   193  
   194  func (secgroup *SSecurityGroup) GetStatus() string {
   195  	return ""
   196  }
   197  
   198  func (secgroup *SSecurityGroup) IsEmulated() bool {
   199  	return false
   200  }
   201  
   202  func (secgroup *SSecurityGroup) Refresh() error {
   203  	return nil
   204  }
   205  
   206  func (secgroup *SSecurityGroup) Delete() error {
   207  	rules, err := secgroup.GetRules()
   208  	if err != nil {
   209  		return errors.Wrap(err, "GetRules")
   210  	}
   211  	for _, rule := range rules {
   212  		err = secgroup.gvpc.client.DeleteSecgroupRule(rule)
   213  		if err != nil {
   214  			return errors.Wrapf(err, "DeleteSecgroupRule(%s)", rule.Description)
   215  		}
   216  	}
   217  	return nil
   218  }
   219  
   220  func (secgroup *SSecurityGroup) GetProjectId() string {
   221  	return ""
   222  }
   223  
   224  func (secgroup *SSecurityGroup) GetVpcId() string {
   225  	return secgroup.gvpc.GetGlobalId()
   226  }
   227  
   228  func (self *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) {
   229  	_firewalls, err := self.gvpc.client.GetFirewalls(self.gvpc.SelfLink, 0, "")
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	firewalls := []SFirewall{}
   234  	for _, firewall := range _firewalls {
   235  		if len(self.Tag) > 0 && utils.IsInStringArray(self.Tag, firewall.TargetTags) {
   236  			firewalls = append(firewalls, firewall)
   237  		} else if len(self.ServiceAccount) > 0 && utils.IsInStringArray(self.ServiceAccount, firewall.TargetServiceAccounts) {
   238  			firewalls = append(firewalls, firewall)
   239  		} else {
   240  			if len(self.Tag) == 0 && len(self.ServiceAccount) == 0 && len(firewall.TargetServiceAccounts) == 0 && len(firewall.TargetTags) == 0 {
   241  				firewalls = append(firewalls, firewall)
   242  			}
   243  		}
   244  	}
   245  	rules := []cloudprovider.SecurityRule{}
   246  	for _, firewall := range firewalls {
   247  		if firewall.Priority == 65535 { // skip 65535 rule, because it not work
   248  			continue
   249  		}
   250  		_rules, err := firewall.toRules()
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  		rules = append(rules, _rules...)
   255  	}
   256  	return rules, nil
   257  }
   258  
   259  func (self *SGoogleClient) DeleteSecgroupRule(rule cloudprovider.SecurityRule) error {
   260  	firwall, err := self.GetFirewall(rule.ExternalId)
   261  	if err != nil {
   262  		return errors.Wrap(err, "region.GetFirewall")
   263  	}
   264  	currentRule, err := firwall.toRules()
   265  	if err != nil {
   266  		return errors.Wrap(err, "firwall.toRules")
   267  	}
   268  	if len(currentRule) > 1 {
   269  		for _, _rule := range currentRule {
   270  			if _rule.String() != rule.String() {
   271  				for _, tag := range firwall.TargetTags {
   272  					err = self.CreateSecurityGroupRule(_rule, firwall.Network, tag, "")
   273  					if err != nil {
   274  						return errors.Wrap(err, "region.CreateSecurityGroupRule")
   275  					}
   276  				}
   277  				for _, serviceAccount := range firwall.TargetServiceAccounts {
   278  					err = self.CreateSecurityGroupRule(_rule, firwall.Network, "", serviceAccount)
   279  					if err != nil {
   280  						return errors.Wrap(err, "region.CreateSecurityGroupRule")
   281  					}
   282  				}
   283  				if len(firwall.TargetTags)+len(firwall.TargetServiceAccounts) == 0 {
   284  					err = self.CreateSecurityGroupRule(_rule, firwall.Network, "", "")
   285  					if err != nil {
   286  						return errors.Wrap(err, "region.CreateSecurityGroupRule")
   287  					}
   288  				}
   289  			}
   290  		}
   291  	}
   292  	return self.ecsDelete(firwall.SelfLink, nil)
   293  }
   294  
   295  func (secgroup *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error {
   296  	for _, r := range append(inDels, outDels...) {
   297  		err := secgroup.gvpc.client.DeleteSecgroupRule(r)
   298  		if err != nil {
   299  			return errors.Wrap(err, "DeleteSecgroupRule")
   300  		}
   301  	}
   302  	for _, r := range append(inAdds, outAdds...) {
   303  		err := secgroup.gvpc.client.CreateSecurityGroupRule(r, secgroup.gvpc.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
   304  		if err != nil {
   305  			return errors.Wrapf(err, "CreateSecurityGroupRule(%s)", r.String())
   306  		}
   307  	}
   308  	return nil
   309  }
   310  
   311  func (region *SRegion) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) {
   312  	vpcs, err := region.GetIVpcs()
   313  	if err != nil {
   314  		return nil, errors.Wrap(err, "GetIVpcs")
   315  	}
   316  
   317  	for _, vpc := range vpcs {
   318  		secgroups, err := vpc.GetISecurityGroups()
   319  		if err != nil {
   320  			return nil, errors.Wrap(err, "GetISecurityGroups")
   321  		}
   322  		for _, secgroup := range secgroups {
   323  			if secgroup.GetGlobalId() == id {
   324  				return secgroup, nil
   325  			}
   326  		}
   327  	}
   328  
   329  	return nil, cloudprovider.ErrNotFound
   330  }
   331  
   332  func (region *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) {
   333  	ivpc, err := region.GetIVpcById(opts.VpcId)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	secgroups, err := ivpc.GetISecurityGroups()
   338  	if err != nil {
   339  		return nil, errors.Wrap(err, "ivpc.GetISecurityGroups")
   340  	}
   341  	for _, secgroup := range secgroups {
   342  		if strings.ToLower(secgroup.GetName()) == strings.ToLower(opts.Name) {
   343  			return secgroup, nil
   344  		}
   345  	}
   346  	return nil, cloudprovider.ErrNotFound
   347  }
   348  
   349  func (self *SGoogleClient) CreateSecurityGroupRule(rule cloudprovider.SecurityRule, vpcId string, tag string, serviceAccount string) error {
   350  	name := fmt.Sprintf("%s-%d", rule.String(), rule.Priority)
   351  	if len(tag) > 0 {
   352  		name = fmt.Sprintf("%s-%s", tag, name)
   353  	}
   354  	if len(serviceAccount) > 0 {
   355  		name = fmt.Sprintf("%s-%s", serviceAccount, name)
   356  	}
   357  
   358  	body := map[string]interface{}{
   359  		"name":      strings.ToLower(name),
   360  		"priority":  rule.Priority,
   361  		"network":   vpcId,
   362  		"direction": "INGRESS",
   363  	}
   364  	if len(tag) > 0 {
   365  		body["targetTags"] = []string{strings.ToLower(tag)}
   366  	}
   367  	if len(serviceAccount) > 0 {
   368  		body["targetServiceAccounts"] = []string{serviceAccount}
   369  	}
   370  	if rule.Direction == secrules.DIR_OUT {
   371  		body["direction"] = "EGRESS"
   372  	} else {
   373  		body["sourceRanges"] = []string{rule.IPNet.String()}
   374  	}
   375  
   376  	protocol := strings.ToLower(rule.Protocol)
   377  	if protocol == secrules.PROTO_ANY {
   378  		protocol = "all"
   379  	}
   380  
   381  	ports := []string{}
   382  	if len(rule.Ports) > 0 {
   383  		for _, port := range rule.Ports {
   384  			ports = append(ports, fmt.Sprintf("%d", port))
   385  		}
   386  	} else if rule.PortStart > 0 && rule.PortEnd > 0 {
   387  		if rule.PortStart == rule.PortEnd {
   388  			ports = append(ports, fmt.Sprintf("%d", rule.PortStart))
   389  		} else {
   390  			ports = append(ports, fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd))
   391  		}
   392  	}
   393  
   394  	actionInfo := []struct {
   395  		IPProtocol string
   396  		Ports      []string
   397  	}{
   398  		{
   399  			IPProtocol: protocol,
   400  			Ports:      ports,
   401  		},
   402  	}
   403  
   404  	if rule.Action == secrules.SecurityRuleDeny {
   405  		body["denied"] = actionInfo
   406  	} else {
   407  		body["allowed"] = actionInfo
   408  	}
   409  
   410  	firwall := &SFirewall{}
   411  	err := self.Insert("global/firewalls", jsonutils.Marshal(body), firwall)
   412  	if err != nil {
   413  		if strings.Index(err.Error(), "already exists") >= 0 {
   414  			return nil
   415  		}
   416  		return errors.Wrap(err, "region.Insert")
   417  	}
   418  	return nil
   419  }
   420  
   421  func (region *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
   422  	gvpc, err := region.client.GetGlobalNetwork(conf.VpcId)
   423  	if err != nil {
   424  		return nil, errors.Wrapf(err, "region.GetIVpcById(%s)", conf.VpcId)
   425  	}
   426  
   427  	secgroup := &SSecurityGroup{gvpc: gvpc, Tag: strings.ToLower(conf.Name)}
   428  	return secgroup, nil
   429  }