mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-23 14:59:50 +08:00
505 lines
13 KiB
Go
505 lines
13 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package google
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/regutils"
|
|
"yunion.io/x/pkg/util/secrules"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
)
|
|
|
|
const (
|
|
SECGROUP_TYPE_SERVICE_ACCOUNT = "serviceAccount"
|
|
SECGROUP_TYPE_TAG = "tag"
|
|
)
|
|
|
|
type SFirewallAction struct {
|
|
IPProtocol string
|
|
Ports []string
|
|
}
|
|
|
|
type SFirewall struct {
|
|
Id string
|
|
CreationTimestamp time.Time
|
|
Name string
|
|
Description string
|
|
Network string
|
|
Priority int
|
|
SourceRanges []string
|
|
DestinationRanges []string
|
|
TargetServiceAccounts []string
|
|
TargetTags []string
|
|
Allowed []SFirewallAction
|
|
Denied []SFirewallAction
|
|
Direction string
|
|
Disabled bool
|
|
SelfLink string
|
|
Kind string
|
|
}
|
|
|
|
type FirewallSet []SFirewall
|
|
|
|
func (f FirewallSet) Len() int {
|
|
return len(f)
|
|
}
|
|
|
|
func (f FirewallSet) Swap(i, j int) {
|
|
f[i], f[j] = f[j], f[i]
|
|
}
|
|
|
|
func (f FirewallSet) Less(i, j int) bool {
|
|
if f[i].Priority != f[j].Priority {
|
|
return f[i].Priority < f[j].Priority
|
|
}
|
|
return len(f[i].Allowed) < len(f[j].Allowed)
|
|
}
|
|
|
|
type SSecurityGroup struct {
|
|
vpc *SVpc
|
|
|
|
ServiceAccount string
|
|
Tag string
|
|
}
|
|
|
|
func (region *SRegion) GetFirewalls(network string, maxResults int, pageToken string) ([]SFirewall, error) {
|
|
firewalls := []SFirewall{}
|
|
params := map[string]string{"filter": "disabled = false"}
|
|
resource := "global/firewalls"
|
|
if len(network) > 0 {
|
|
params["filter"] = fmt.Sprintf(`(disabled = false) AND (network="%s")`, network)
|
|
}
|
|
return firewalls, region.List(resource, params, maxResults, pageToken, &firewalls)
|
|
}
|
|
|
|
func (region *SRegion) GetFirewall(id string) (*SFirewall, error) {
|
|
firewall := &SFirewall{}
|
|
return firewall, region.Get(id, firewall)
|
|
}
|
|
|
|
func (firewall *SFirewall) _toRules(action secrules.TSecurityRuleAction) ([]secrules.SecurityRule, error) {
|
|
rules := []secrules.SecurityRule{}
|
|
list := firewall.Allowed
|
|
if action == secrules.SecurityRuleDeny {
|
|
list = firewall.Denied
|
|
}
|
|
for _, allow := range list {
|
|
rule := secrules.SecurityRule{
|
|
Action: action,
|
|
Direction: secrules.DIR_IN,
|
|
Description: firewall.SelfLink,
|
|
Priority: firewall.Priority,
|
|
}
|
|
if firewall.Direction == "EGRESS" {
|
|
rule.Direction = secrules.DIR_OUT
|
|
}
|
|
switch allow.IPProtocol {
|
|
case "tcp", "udp", "icmp":
|
|
rule.Protocol = allow.IPProtocol
|
|
case "all":
|
|
rule.Protocol = secrules.PROTO_ANY
|
|
default:
|
|
return nil, fmt.Errorf("unsupport protocol %s", allow.IPProtocol)
|
|
}
|
|
ipRanges := firewall.SourceRanges
|
|
if rule.Direction == secrules.DIR_OUT {
|
|
ipRanges = firewall.DestinationRanges
|
|
}
|
|
for _, ipRange := range ipRanges {
|
|
if regutils.MatchCIDR(ipRange) {
|
|
_, rule.IPNet, _ = net.ParseCIDR(ipRange)
|
|
} else {
|
|
rule.IPNet = &net.IPNet{
|
|
IP: net.ParseIP(ipRange),
|
|
Mask: net.CIDRMask(32, 32),
|
|
}
|
|
}
|
|
ports := []int{}
|
|
for _, port := range allow.Ports {
|
|
if strings.Index(port, "-") > 0 {
|
|
err := rule.ParsePorts(port)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "Parse port %s", port)
|
|
}
|
|
rules = append(rules, rule)
|
|
} else {
|
|
_port, err := strconv.Atoi(port)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "Atio port %s", port)
|
|
}
|
|
ports = append(ports, _port)
|
|
}
|
|
}
|
|
if len(ports) > 0 {
|
|
rule.Ports = ports
|
|
rule.PortStart = -1
|
|
rule.PortEnd = -1
|
|
rules = append(rules, rule)
|
|
}
|
|
if len(allow.Ports) == 0 {
|
|
rule.Ports = []int{}
|
|
rule.PortStart = -1
|
|
rule.PortEnd = -1
|
|
rules = append(rules, rule)
|
|
}
|
|
}
|
|
}
|
|
return rules, nil
|
|
}
|
|
|
|
func (firewall *SFirewall) toRules() ([]secrules.SecurityRule, error) {
|
|
rules := []secrules.SecurityRule{}
|
|
_rules, err := firewall._toRules(secrules.SecurityRuleAllow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rules = append(rules, _rules...)
|
|
_rules, err = firewall._toRules(secrules.SecurityRuleDeny)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rules = append(rules, _rules...)
|
|
return rules, nil
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetId() string {
|
|
return secgroup.vpc.globalnetwork.GetGlobalId()
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetGlobalId() string {
|
|
if len(secgroup.Tag) > 0 {
|
|
return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_TAG, secgroup.Tag)
|
|
}
|
|
if len(secgroup.ServiceAccount) > 0 {
|
|
return fmt.Sprintf("%s/%s/%s", secgroup.GetId(), SECGROUP_TYPE_SERVICE_ACCOUNT, secgroup.ServiceAccount)
|
|
}
|
|
return secgroup.GetId()
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetDescription() string {
|
|
return ""
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetName() string {
|
|
if len(secgroup.Tag) > 0 {
|
|
return secgroup.Tag
|
|
}
|
|
if len(secgroup.ServiceAccount) > 0 {
|
|
return secgroup.ServiceAccount
|
|
}
|
|
return secgroup.vpc.globalnetwork.Name
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
|
|
return nil
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetStatus() string {
|
|
return ""
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) IsEmulated() bool {
|
|
return false
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) Refresh() error {
|
|
return nil
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) Delete() error {
|
|
rules, err := secgroup.GetRules()
|
|
if err != nil {
|
|
return errors.Wrap(err, "GetRules")
|
|
}
|
|
for _, rule := range rules {
|
|
err = secgroup.vpc.region.DeleteSecgroupRule(rule.Description, rule)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "DeleteSecgroupRule(%s)", rule.Description)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetProjectId() string {
|
|
return ""
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) GetVpcId() string {
|
|
return secgroup.vpc.GetGlobalId()
|
|
}
|
|
|
|
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
|
|
_firewalls, err := self.vpc.region.GetFirewalls(self.vpc.globalnetwork.SelfLink, 0, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
firewalls := []SFirewall{}
|
|
for _, firewall := range _firewalls {
|
|
if len(self.Tag) > 0 && utils.IsInStringArray(self.Tag, firewall.TargetTags) {
|
|
firewalls = append(firewalls, firewall)
|
|
} else if len(self.ServiceAccount) > 0 && utils.IsInStringArray(self.ServiceAccount, firewall.TargetServiceAccounts) {
|
|
firewalls = append(firewalls, firewall)
|
|
} else {
|
|
if len(self.Tag) == 0 && len(self.ServiceAccount) == 0 && len(firewall.TargetServiceAccounts) == 0 && len(firewall.TargetTags) == 0 {
|
|
firewalls = append(firewalls, firewall)
|
|
}
|
|
}
|
|
}
|
|
sort.Sort(FirewallSet(firewalls))
|
|
rules := []secrules.SecurityRule{}
|
|
priority := 100
|
|
for _, firewall := range firewalls {
|
|
firewall.Priority = priority
|
|
if priority > 2 {
|
|
priority--
|
|
}
|
|
_rules, err := firewall.toRules()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rules = append(rules, _rules...)
|
|
}
|
|
return rules, nil
|
|
}
|
|
|
|
func (region *SRegion) DeleteSecgroupRule(ruleId string, rule secrules.SecurityRule) error {
|
|
firwall, err := region.GetFirewall(ruleId)
|
|
if err != nil {
|
|
return errors.Wrap(err, "region.GetFirewall")
|
|
}
|
|
currentRule, err := firwall.toRules()
|
|
if err != nil {
|
|
return errors.Wrap(err, "firwall.toRules")
|
|
}
|
|
if len(currentRule) > 1 {
|
|
for _, _rule := range currentRule {
|
|
if _rule.String() != rule.String() {
|
|
for _, tag := range firwall.TargetTags {
|
|
err = region.CreateSecurityGroupRule(_rule, firwall.Network, tag, "")
|
|
if err != nil {
|
|
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
|
}
|
|
}
|
|
for _, serviceAccount := range firwall.TargetServiceAccounts {
|
|
err = region.CreateSecurityGroupRule(_rule, firwall.Network, "", serviceAccount)
|
|
if err != nil {
|
|
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
|
}
|
|
}
|
|
if len(firwall.TargetTags)+len(firwall.TargetServiceAccounts) == 0 {
|
|
err = region.CreateSecurityGroupRule(_rule, firwall.Network, "", "")
|
|
if err != nil {
|
|
return errors.Wrap(err, "region.CreateSecurityGroupRule")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return region.Delete(firwall.SelfLink)
|
|
}
|
|
|
|
func (secgroup *SSecurityGroup) SyncRules(rules []secrules.SecurityRule) error {
|
|
if len(rules) == 0 {
|
|
rules = append(rules, *secrules.MustParseSecurityRule("in:deny any"))
|
|
}
|
|
currentRule, err := secgroup.GetRules()
|
|
if err != nil {
|
|
return errors.Wrap(err, "secgroup.GetRules")
|
|
}
|
|
sort.Sort(secrules.SecurityRuleSet(rules))
|
|
region := secgroup.vpc.region
|
|
deleteRules := map[string]secrules.SecurityRule{}
|
|
addRules := []secrules.SecurityRule{}
|
|
i, j := 0, 0
|
|
for i < len(rules) || j < len(currentRule) {
|
|
if i < len(rules) && j < len(currentRule) {
|
|
currentRuleStr := currentRule[j].String()
|
|
ruleStr := rules[i].String()
|
|
cmp := strings.Compare(currentRuleStr, ruleStr)
|
|
if cmp == 0 {
|
|
i += 1
|
|
j += 1
|
|
} else if cmp > 0 {
|
|
// delete rule
|
|
deleteRules[currentRule[j].Description] = currentRule[j]
|
|
j += 1
|
|
} else {
|
|
rules[i].Priority = 101 - rules[i].Priority
|
|
addRules = append(addRules, rules[i])
|
|
i += 1
|
|
}
|
|
} else if i >= len(rules) {
|
|
// delete rule
|
|
deleteRules[currentRule[j].Description] = currentRule[j]
|
|
j += 1
|
|
} else if j >= len(currentRule) {
|
|
// add rule
|
|
rules[i].Priority = 101 - rules[i].Priority
|
|
addRules = append(addRules, rules[i])
|
|
err = region.CreateSecurityGroupRule(rules[i], secgroup.vpc.globalnetwork.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "region.CreateSecurityGroupRule(%s)", rules[i].String())
|
|
}
|
|
i += 1
|
|
}
|
|
}
|
|
for id, rule := range deleteRules {
|
|
err = region.DeleteSecgroupRule(id, rule)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "DeleteSecgroupRule(%s)", id)
|
|
}
|
|
}
|
|
for _, rule := range addRules {
|
|
err = region.CreateSecurityGroupRule(rule, secgroup.vpc.globalnetwork.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "CreateSecurityGroupRule(%s)", rule.String())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (region *SRegion) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) {
|
|
vpcs, err := region.GetIVpcs()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetIVpcs")
|
|
}
|
|
|
|
for _, vpc := range vpcs {
|
|
secgroups, err := vpc.GetISecurityGroups()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "GetISecurityGroups")
|
|
}
|
|
for _, secgroup := range secgroups {
|
|
if secgroup.GetGlobalId() == id {
|
|
return secgroup, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, cloudprovider.ErrNotFound
|
|
}
|
|
|
|
func (region *SRegion) GetISecurityGroupByName(vpcId string, name string) (cloudprovider.ICloudSecurityGroup, error) {
|
|
ivpc, err := region.GetIVpcById(vpcId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secgroups, err := ivpc.GetISecurityGroups()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "ivpc.GetISecurityGroups")
|
|
}
|
|
for _, secgroup := range secgroups {
|
|
if strings.ToLower(secgroup.GetName()) == strings.ToLower(name) {
|
|
return secgroup, nil
|
|
}
|
|
}
|
|
return nil, cloudprovider.ErrNotFound
|
|
}
|
|
|
|
func (region *SRegion) CreateSecurityGroupRule(rule secrules.SecurityRule, vpcId string, tag string, serviceAccount string) error {
|
|
name := fmt.Sprintf("%s-%d", rule.String(), rule.Priority)
|
|
if len(tag) > 0 {
|
|
name = fmt.Sprintf("for-tag-%s-%s", tag, name)
|
|
}
|
|
if len(serviceAccount) > 0 {
|
|
name = fmt.Sprintf("for-service-account-%s-%s", serviceAccount, name)
|
|
}
|
|
|
|
body := map[string]interface{}{
|
|
"name": strings.ToLower(name),
|
|
"priority": rule.Priority,
|
|
"network": vpcId,
|
|
"direction": "INGRESS",
|
|
}
|
|
if len(tag) > 0 {
|
|
body["targetTags"] = []string{strings.ToLower(tag)}
|
|
}
|
|
if len(serviceAccount) > 0 {
|
|
body["targetServiceAccounts"] = []string{serviceAccount}
|
|
}
|
|
if rule.Direction == secrules.DIR_OUT {
|
|
body["direction"] = "EGRESS"
|
|
} else {
|
|
body["sourceRanges"] = []string{rule.IPNet.String()}
|
|
}
|
|
|
|
protocol := string(rule.Protocol)
|
|
if protocol == secrules.PROTO_ANY {
|
|
protocol = "all"
|
|
}
|
|
|
|
ports := []string{}
|
|
if len(rule.Ports) > 0 {
|
|
for _, port := range rule.Ports {
|
|
ports = append(ports, fmt.Sprintf("%d", port))
|
|
}
|
|
} else if rule.PortStart > 0 && rule.PortEnd > 0 {
|
|
if rule.PortStart == rule.PortEnd {
|
|
ports = append(ports, fmt.Sprintf("%d", rule.PortStart))
|
|
} else {
|
|
ports = append(ports, fmt.Sprintf("%d-%d", rule.PortStart, rule.PortEnd))
|
|
}
|
|
}
|
|
|
|
actionInfo := []struct {
|
|
IPProtocol string
|
|
Ports []string
|
|
}{
|
|
{
|
|
IPProtocol: protocol,
|
|
Ports: ports,
|
|
},
|
|
}
|
|
|
|
if rule.Action == secrules.SecurityRuleDeny {
|
|
body["denied"] = actionInfo
|
|
} else {
|
|
body["allowed"] = actionInfo
|
|
}
|
|
|
|
firwall := &SFirewall{}
|
|
err := region.Insert("global/firewalls", jsonutils.Marshal(body), firwall)
|
|
if err != nil {
|
|
if strings.Index(err.Error(), "already exists") >= 0 {
|
|
return nil
|
|
}
|
|
return errors.Wrap(err, "region.Insert")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (region *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
|
conf.VpcId = fmt.Sprintf("%s/%s", region.GetGlobalId(), conf.VpcId)
|
|
ivpc, err := region.GetIVpcById(conf.VpcId)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "region.GetIVpcById(%s)", conf.VpcId)
|
|
}
|
|
|
|
vpc := ivpc.(*SVpc)
|
|
|
|
secgroup := &SSecurityGroup{vpc: vpc, Tag: strings.ToLower(conf.Name)}
|
|
return secgroup, nil
|
|
}
|