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 }