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 }