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 }