yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/classic_secruitygroup.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 azure
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/url"
    21  	"strconv"
    22  	"strings"
    23  	"unicode"
    24  
    25  	"yunion.io/x/jsonutils"
    26  	"yunion.io/x/pkg/errors"
    27  	"yunion.io/x/pkg/util/secrules"
    28  	"yunion.io/x/pkg/utils"
    29  
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  )
    33  
    34  type SClassicSecurityGroup struct {
    35  	multicloud.SSecurityGroup
    36  	AzureTags
    37  	region     *SRegion
    38  	vpc        *SClassicVpc
    39  	Properties ClassicSecurityGroupProperties `json:"properties,omitempty"`
    40  	ID         string
    41  	Name       string
    42  	Location   string
    43  	Type       string
    44  }
    45  
    46  type ClassicSecurityGroupProperties struct {
    47  	NetworkSecurityGroupId string `json:"networkSecurityGroupId,omitempty"`
    48  	State                  string `json:"state,omitempty"`
    49  }
    50  
    51  type ClassicSecurityGroupRuleProperties struct {
    52  	State                    string `json:"state,omitempty"`
    53  	Protocol                 string `json:"protocol,omitempty"`
    54  	SourcePortRange          string `json:"sourcePortRange,omitempty"`
    55  	DestinationPortRange     string `json:"destinationPortRange,omitempty"`
    56  	SourceAddressPrefix      string `json:"sourceAddressPrefix,omitempty"`
    57  	DestinationAddressPrefix string `json:"destinationAddressPrefix,omitempty"`
    58  	Action                   string `json:"action,omitempty"`
    59  	Priority                 int32  `json:"priority,omitempty"`
    60  	Type                     string `json:"type,omitempty"`
    61  	IsDefault                bool   `json:"isDefault,omitempty"`
    62  }
    63  
    64  type SClassicSecurityGroupRule struct {
    65  	Properties ClassicSecurityGroupRuleProperties `json:"properties,omitempty"`
    66  	ID         string
    67  	Name       string
    68  	Type       string
    69  }
    70  
    71  func (self *ClassicSecurityGroupRuleProperties) toRule() *cloudprovider.SecurityRule {
    72  	rule := cloudprovider.SecurityRule{
    73  		SecurityRule: secrules.SecurityRule{
    74  			Action:    secrules.TSecurityRuleAction(strings.ToLower(self.Action)),
    75  			Direction: secrules.TSecurityRuleDirection(strings.Replace(strings.ToLower(self.Type), "bound", "", -1)),
    76  			Protocol:  strings.ToLower(self.Protocol),
    77  			Priority:  int(self.Priority),
    78  		},
    79  	}
    80  	if rule.Protocol == "*" {
    81  		rule.Protocol = "any"
    82  	}
    83  	port := self.DestinationPortRange
    84  	ip := self.DestinationAddressPrefix
    85  	if rule.Direction == secrules.SecurityRuleEgress {
    86  		port = self.SourcePortRange
    87  		ip = self.SourceAddressPrefix
    88  	}
    89  
    90  	if utils.IsInStringArray(ip, []string{"INTERNET", "VIRTUAL_NETWORK", "AZURE_LOADBALANCER"}) {
    91  		return nil
    92  	}
    93  
    94  	if ip == "*" {
    95  		ip = "0.0.0.0/24"
    96  	}
    97  	if idx := strings.Index(ip, "/"); idx > -1 {
    98  		_, ipnet, err := net.ParseCIDR(ip)
    99  		if err == nil {
   100  			rule.IPNet = ipnet
   101  		}
   102  	} else if _ip := net.ParseIP(ip); _ip != nil {
   103  		rule.IPNet = &net.IPNet{
   104  			IP:   _ip,
   105  			Mask: net.CIDRMask(32, 32),
   106  		}
   107  	}
   108  	ports := strings.Split(port, "-")
   109  	if len(ports) == 2 {
   110  		rule.PortStart, _ = strconv.Atoi(ports[0])
   111  		rule.PortEnd, _ = strconv.Atoi(ports[1])
   112  	} else if len(ports) == 1 {
   113  		rule.PortStart, _ = strconv.Atoi(ports[0])
   114  		rule.PortEnd, _ = strconv.Atoi(ports[0])
   115  	}
   116  	return &rule
   117  }
   118  
   119  func (self *SClassicSecurityGroup) GetVpcId() string {
   120  	return "classic"
   121  }
   122  
   123  func (self *SClassicSecurityGroup) GetId() string {
   124  	return self.ID
   125  }
   126  
   127  func (self *SClassicSecurityGroup) GetGlobalId() string {
   128  	return strings.ToLower(self.ID)
   129  }
   130  
   131  func (self *SClassicSecurityGroup) GetDescription() string {
   132  	return ""
   133  }
   134  
   135  func (self *SClassicSecurityGroup) GetName() string {
   136  	return self.Name
   137  }
   138  
   139  func (self *SClassicSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) {
   140  	rules := make([]cloudprovider.SecurityRule, 0)
   141  	secgrouprules, err := self.region.getClassicSecurityGroupRules(self.ID)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	for i := 0; i < len(secgrouprules); i++ {
   147  		if secgrouprules[i].Properties.Priority >= 65000 {
   148  			continue
   149  		}
   150  		rule := secgrouprules[i].Properties.toRule()
   151  		if rule == nil {
   152  			continue
   153  		}
   154  		rule.Name = secgrouprules[i].Name
   155  		rule.ExternalId = secgrouprules[i].ID
   156  		if err := rule.ValidateRule(); err != nil && err != secrules.ErrInvalidPriority {
   157  			return nil, errors.Wrap(err, "rule.ValidateRule")
   158  		}
   159  		rules = append(rules, *rule)
   160  	}
   161  	return rules, nil
   162  }
   163  
   164  func (self *SClassicSecurityGroup) GetStatus() string {
   165  	return ""
   166  }
   167  
   168  func (self *SClassicSecurityGroup) IsEmulated() bool {
   169  	return false
   170  }
   171  
   172  func (region *SRegion) CreateClassicSecurityGroup(name string) (*SClassicSecurityGroup, error) {
   173  	if name == "Default" {
   174  		name = "Default-copy"
   175  	}
   176  	secgroup := SClassicSecurityGroup{
   177  		region:   region,
   178  		Name:     name,
   179  		Type:     "Microsoft.ClassicNetwork/networkSecurityGroups",
   180  		Location: region.Name,
   181  	}
   182  	return &secgroup, region.create("", jsonutils.Marshal(secgroup), &secgroup)
   183  }
   184  
   185  func (region *SRegion) GetClassicSecurityGroups(name string) ([]SClassicSecurityGroup, error) {
   186  	secgroups := []SClassicSecurityGroup{}
   187  	err := region.client.list("Microsoft.ClassicNetwork/networkSecurityGroups", url.Values{}, &secgroups)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	result := []SClassicSecurityGroup{}
   192  	for i := 0; i < len(secgroups); i++ {
   193  		if secgroups[i].Location == region.Name && (len(name) == 0 || strings.ToLower(secgroups[i].Name) == strings.ToLower(name)) {
   194  			result = append(result, secgroups[i])
   195  		}
   196  	}
   197  	return result, err
   198  }
   199  
   200  func (region *SRegion) GetClassicSecurityGroupDetails(secgroupId string) (*SClassicSecurityGroup, error) {
   201  	secgroup := SClassicSecurityGroup{region: region}
   202  	return &secgroup, region.get(secgroupId, url.Values{}, &secgroup)
   203  }
   204  
   205  func (region *SRegion) deleteClassicSecurityGroup(secgroupId string) error {
   206  	return region.del(secgroupId)
   207  }
   208  
   209  func (self *SClassicSecurityGroup) Delete() error {
   210  	return self.region.deleteClassicSecurityGroup(self.ID)
   211  }
   212  
   213  func (self *SClassicSecurityGroup) Refresh() error {
   214  	sec, err := self.region.GetClassicSecurityGroupDetails(self.ID)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	return jsonutils.Update(self, sec)
   219  }
   220  
   221  func convertClassicSecurityGroupRules(rule cloudprovider.SecurityRule) ([]SClassicSecurityGroupRule, error) {
   222  	rules := []SClassicSecurityGroupRule{}
   223  	if len(rule.Name) == 0 {
   224  		rule.Name = fmt.Sprintf("%s_%d", rule.String(), rule.Priority)
   225  	}
   226  	rule.Name = func(name string) string {
   227  		// 名称必须以字母或数字开头,以字母、数字或下划线结尾,并且只能包含字母、数字、下划线、句点或连字符
   228  		for _, s := range name {
   229  			if !(unicode.IsDigit(s) || unicode.IsLetter(s) || s == '.' || s == '-' || s == '_') {
   230  				name = strings.ReplaceAll(name, string(s), "_")
   231  			}
   232  		}
   233  		if !unicode.IsDigit(rune(name[0])) && !unicode.IsLetter(rune(name[0])) {
   234  			name = fmt.Sprintf("r_%s", name)
   235  		}
   236  		last := len(name) - 1
   237  		if !unicode.IsDigit(rune(name[last])) && !unicode.IsLetter(rune(name[last])) && name[last] != '_' {
   238  			name = fmt.Sprintf("%s_", name)
   239  		}
   240  		return name
   241  	}(rule.Name)
   242  	secRule := SClassicSecurityGroupRule{
   243  		Name: rule.Name,
   244  		Properties: ClassicSecurityGroupRuleProperties{
   245  			Action:                   utils.Capitalize(string(rule.Action)),
   246  			Priority:                 int32(rule.Priority),
   247  			Type:                     utils.Capitalize(string(rule.Direction)) + "bound",
   248  			Protocol:                 utils.Capitalize(rule.Protocol),
   249  			SourcePortRange:          "*",
   250  			DestinationPortRange:     "*",
   251  			SourceAddressPrefix:      "*",
   252  			DestinationAddressPrefix: "*",
   253  		},
   254  	}
   255  	if rule.Protocol == secrules.PROTO_ANY {
   256  		secRule.Properties.Protocol = "*"
   257  	}
   258  	if rule.Protocol == secrules.PROTO_ICMP {
   259  		return nil, fmt.Errorf("not support icmp protocol")
   260  	}
   261  	ipAddr := "*"
   262  	if rule.IPNet != nil {
   263  		ipAddr = rule.IPNet.String()
   264  	}
   265  	if rule.Direction == secrules.DIR_IN {
   266  		secRule.Properties.SourceAddressPrefix = ipAddr
   267  	} else {
   268  		secRule.Properties.DestinationAddressPrefix = ipAddr
   269  	}
   270  	if len(rule.Ports) > 0 {
   271  		for _, port := range rule.Ports {
   272  			secRule.Properties.DestinationPortRange = fmt.Sprintf("%d", port)
   273  			rules = append(rules, secRule)
   274  		}
   275  		return rules, nil
   276  	} else if rule.PortStart > 0 && rule.PortEnd > 0 {
   277  		secRule.Properties.DestinationPortRange = fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd)
   278  	}
   279  	rules = append(rules, secRule)
   280  	return rules, nil
   281  }
   282  
   283  func (self *SRegion) getClassicSecurityGroupRules(secgroupId string) ([]SClassicSecurityGroupRule, error) {
   284  	rules := []SClassicSecurityGroupRule{}
   285  	params := url.Values{}
   286  	params.Set("api-version", "2015-06-01")
   287  	resource := fmt.Sprintf("%s/securityRules", secgroupId)
   288  	err := self.client.list(resource, params, &rules)
   289  	if err != nil {
   290  		return nil, errors.Wrapf(err, "list")
   291  	}
   292  	return rules, nil
   293  }
   294  
   295  func (self *SRegion) addClassicSecgroupRule(secgroupId string, rule SClassicSecurityGroupRule) error {
   296  	resource := fmt.Sprintf("%s/securityRules/%s", secgroupId, rule.Name)
   297  	_, err := self.put(resource, jsonutils.Marshal(rule))
   298  	return err
   299  }
   300  
   301  func (self *SClassicSecurityGroup) GetProjectId() string {
   302  	return getResourceGroup(self.ID)
   303  }
   304  
   305  func (self *SClassicSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error {
   306  	for _, r := range append(inDels, outDels...) {
   307  		err := self.region.del(r.ExternalId)
   308  		if err != nil {
   309  			return errors.Wrapf(err, "Delete(%s)", r.ExternalId)
   310  		}
   311  		for _, r := range append(inAdds, outAdds...) {
   312  			_rules, err := convertClassicSecurityGroupRules(r)
   313  			if err != nil {
   314  				return errors.Wrapf(err, "convertClassicSecurityGroupRules(%s)", r.String())
   315  			}
   316  			names := []string{}
   317  			for _, r := range _rules {
   318  				for {
   319  					if !utils.IsInStringArray(r.Name, names) {
   320  						names = append(names, r.Name)
   321  						break
   322  					}
   323  					r.Name = fmt.Sprintf("%s_", r.Name)
   324  				}
   325  				err = self.region.addClassicSecgroupRule(self.ID, r)
   326  				if err != nil {
   327  					return errors.Wrap(err, "addClassicSecgroupRule")
   328  				}
   329  			}
   330  		}
   331  	}
   332  	return nil
   333  }