Files
cloudpods/pkg/multicloud/google/securitygroup.go
T
2020-07-30 14:23:56 +08:00

433 lines
12 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"
"strconv"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
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 SSecurityGroup struct {
multicloud.SSecurityGroup
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) ([]cloudprovider.SecurityRule, error) {
rules := []cloudprovider.SecurityRule{}
list := firewall.Allowed
if action == secrules.SecurityRuleDeny {
list = firewall.Denied
}
for _, allow := range list {
rule := cloudprovider.SecurityRule{
ExternalId: firewall.SelfLink,
SecurityRule: secrules.SecurityRule{
Action: action,
Direction: secrules.DIR_IN,
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 {
rule.ParseCIDR(ipRange)
ports := []int{}
for _, port := range allow.Ports {
if strings.Index(port, "-") > 0 {
port = strings.Replace(port, "0-", "1-", 1)
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() ([]cloudprovider.SecurityRule, error) {
rules := []cloudprovider.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)
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() ([]cloudprovider.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)
}
}
}
rules := []cloudprovider.SecurityRule{}
for _, firewall := range firewalls {
_rules, err := firewall.toRules()
if err != nil {
return nil, err
}
rules = append(rules, _rules...)
}
return rules, nil
}
func (region *SRegion) DeleteSecgroupRule(rule cloudprovider.SecurityRule) error {
firwall, err := region.GetFirewall(rule.ExternalId)
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(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error {
for _, r := range append(inDels, outDels...) {
err := secgroup.vpc.region.DeleteSecgroupRule(r)
if err != nil {
return errors.Wrap(err, "DeleteSecgroupRule")
}
}
for _, r := range append(inAdds, outAdds...) {
err := secgroup.vpc.region.CreateSecurityGroupRule(r, secgroup.vpc.globalnetwork.SelfLink, secgroup.Tag, secgroup.ServiceAccount)
if err != nil {
return errors.Wrapf(err, "CreateSecurityGroupRule(%s)", r.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(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) {
ivpc, err := region.GetIVpcById(opts.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(opts.Name) {
return secgroup, nil
}
}
return nil, cloudprovider.ErrNotFound
}
func (region *SRegion) CreateSecurityGroupRule(rule cloudprovider.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 := strings.ToLower(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
}