Files
cloudpods/pkg/multicloud/aws/securitygroup.go

522 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 aws
import (
"fmt"
"math/rand"
"sort"
"strings"
"time"
"github.com/aws/aws-sdk-go/service/ec2"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/onecloud/pkg/httperrors"
)
type Tags struct {
Tag []Tag
}
type Tag struct {
TagKey string
TagValue string
}
type SSecurityGroup struct {
vpc *SVpc
RegionId string
VpcId string
SecurityGroupId string
Description string
SecurityGroupName string
Permissions []secrules.SecurityRule
Tags Tags
// CreationTime time.Time
// InnerAccessPolicy string
}
func randomString(prefix string, length int) string {
bytes := []byte("0123456789abcdefghijklmnopqrstuvwxyz")
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < length; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return prefix + string(result)
}
func (self *SSecurityGroup) GetId() string {
return self.SecurityGroupId
}
func (self *SSecurityGroup) GetVpcId() string {
return self.VpcId
}
func (self *SSecurityGroup) GetName() string {
if len(self.SecurityGroupName) > 0 {
return self.SecurityGroupName
}
return self.SecurityGroupId
}
func (self *SSecurityGroup) GetGlobalId() string {
return self.SecurityGroupId
}
func (self *SSecurityGroup) GetStatus() string {
return ""
}
func (self *SSecurityGroup) Refresh() error {
if new, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil {
return err
} else {
return jsonutils.Update(self, new)
}
}
func (self *SSecurityGroup) IsEmulated() bool {
return false
}
func (self *SSecurityGroup) GetMetadata() *jsonutils.JSONDict {
if len(self.Tags.Tag) == 0 {
return nil
}
data := jsonutils.NewDict()
for _, value := range self.Tags.Tag {
data.Add(jsonutils.NewString(value.TagValue), value.TagKey)
}
return data
}
func (self *SSecurityGroup) GetDescription() string {
return self.Description
}
func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) {
rules := make([]secrules.SecurityRule, 0)
if secgrp, err := self.vpc.region.GetSecurityGroupDetails(self.SecurityGroupId); err != nil {
return rules, err
} else {
rules = secgrp.Permissions
}
return rules, nil
}
func (self *SRegion) addSecurityGroupRules(secGrpId string, rule *secrules.SecurityRule) error {
if len(rule.Ports) != 0 {
for _, port := range rule.Ports {
rule.PortStart, rule.PortEnd = port, port
if err := self.addSecurityGroupRule(secGrpId, rule); err != nil {
return err
}
}
} else {
return self.addSecurityGroupRule(secGrpId, rule)
}
return nil
}
func (self *SRegion) addSecurityGroupRule(secGrpId string, rule *secrules.SecurityRule) error {
ipPermissions, err := YunionSecRuleToAws(*rule)
log.Debugf("Aws security group rule: %s", ipPermissions)
if err != nil {
return err
}
if rule.Direction == secrules.SecurityRuleIngress {
params := &ec2.AuthorizeSecurityGroupIngressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
_, err = self.ec2Client.AuthorizeSecurityGroupIngress(params)
}
if rule.Direction == secrules.SecurityRuleEgress {
params := &ec2.AuthorizeSecurityGroupEgressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
_, err = self.ec2Client.AuthorizeSecurityGroupEgress(params)
}
if err != nil && strings.Contains(err.Error(), "InvalidPermission.Duplicate") {
log.Debugf("addSecurityGroupRule %s %s", rule.Direction, err.Error())
return nil
}
return err
}
func (self *SRegion) delSecurityGroupRule(secGrpId string, rule *secrules.SecurityRule) error {
ipPermissions, err := YunionSecRuleToAws(*rule)
if err != nil {
return err
}
if rule.Direction == secrules.SecurityRuleIngress {
params := &ec2.RevokeSecurityGroupIngressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
_, err = self.ec2Client.RevokeSecurityGroupIngress(params)
}
if rule.Direction == secrules.SecurityRuleEgress {
params := &ec2.RevokeSecurityGroupEgressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
_, err = self.ec2Client.RevokeSecurityGroupEgress(params)
}
if err != nil {
log.Debugf("delSecurityGroupRule %s %s", rule.Direction, err.Error())
return err
}
return nil
}
func (self *SRegion) updateSecurityGroupRuleDescription(secGrpId string, rule *secrules.SecurityRule) error {
ipPermissions, err := YunionSecRuleToAws(*rule)
if err != nil {
return err
}
if rule.Direction == secrules.SecurityRuleIngress {
params := &ec2.UpdateSecurityGroupRuleDescriptionsIngressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
ret, err := self.ec2Client.UpdateSecurityGroupRuleDescriptionsIngress(params)
if err != nil {
return err
} else if ret.Return != nil && *ret.Return == false {
log.Debugf("update security group %s rule description failed: %s", secGrpId, ipPermissions)
}
}
if rule.Direction == secrules.SecurityRuleEgress {
params := &ec2.UpdateSecurityGroupRuleDescriptionsEgressInput{}
params.SetGroupId(secGrpId)
params.SetIpPermissions(ipPermissions)
ret, err := self.ec2Client.UpdateSecurityGroupRuleDescriptionsEgress(params)
if err != nil {
return err
} else if ret.Return != nil && *ret.Return == false {
log.Debugf("update security group %s rule description failed: %s", secGrpId, ipPermissions)
}
}
return nil
}
func (self *SRegion) CreateSecurityGroup(vpcId string, name string, secgroupIdTag string, desc string) (string, error) {
params := &ec2.CreateSecurityGroupInput{}
params.SetVpcId(vpcId)
// 这里的描述aws 上层代码拼接的描述。并非用户提交的描述用户描述放置在Yunion本地数据库中。
if len(desc) == 0 {
desc = "vpc default group"
}
params.SetDescription(desc)
if strings.ToLower(name) == "default" {
name = randomString(fmt.Sprintf("%s-", vpcId), 9)
}
params.SetGroupName(name)
group, err := self.ec2Client.CreateSecurityGroup(params)
if err != nil {
return "", err
}
tagspec := TagSpec{ResourceType: "security-group"}
if len(secgroupIdTag) > 0 {
tagspec.SetTag("id", secgroupIdTag)
}
tagspec.SetNameTag(name)
tagspec.SetDescTag(desc)
tags, _ := tagspec.GetTagSpecifications()
tagParams := &ec2.CreateTagsInput{}
tagParams.SetResources([]*string{group.GroupId})
tagParams.SetTags(tags.Tags)
_, err = self.ec2Client.CreateTags(tagParams)
if err != nil {
return "", err
}
return *group.GroupId, nil
}
func (self *SRegion) createDefaultSecurityGroup(vpcId string) (string, error) {
name := randomString(fmt.Sprintf("%s-", vpcId), 9)
secId, err := self.CreateSecurityGroup(vpcId, name, "default", "vpc default group")
if err != nil {
return "", err
}
rule := &secrules.SecurityRule{
Priority: 1,
Action: secrules.SecurityRuleAllow,
Protocol: "",
Direction: secrules.SecurityRuleIngress,
PortStart: -1,
PortEnd: -1,
}
err = self.addSecurityGroupRule(secId, rule)
if err != nil {
return "", err
}
return secId, nil
}
func (self *SRegion) GetSecurityGroupDetails(secGroupId string) (*SSecurityGroup, error) {
if len(secGroupId) == 0 {
return nil, fmt.Errorf("GetSecurityGroupDetails security group id should not be empty.")
}
params := &ec2.DescribeSecurityGroupsInput{}
params.SetGroupIds([]*string{&secGroupId})
ret, err := self.ec2Client.DescribeSecurityGroups(params)
err = parseNotFoundError(err)
if err != nil {
return nil, err
}
if len(ret.SecurityGroups) == 1 {
s := ret.SecurityGroups[0]
vpc, err := self.getVpc(*s.VpcId)
if err != nil {
return nil, fmt.Errorf("vpc %s not found", *s.VpcId)
}
permissions := self.getSecRules(s.IpPermissions, s.IpPermissionsEgress)
return &SSecurityGroup{
vpc: vpc,
Description: *s.Description,
SecurityGroupId: *s.GroupId,
SecurityGroupName: *s.GroupName,
VpcId: *s.VpcId,
Permissions: permissions,
RegionId: self.RegionId,
}, nil
} else {
return nil, fmt.Errorf("required one security group. but found: %d", len(ret.SecurityGroups))
}
}
func (self *SRegion) getSecurityGroupById(vpcId, secgroupId string) (*SSecurityGroup, error) {
if len(secgroupId) == 0 {
return nil, httperrors.NewInputParameterError("security group id should not be empty")
}
secgroups, total, err := self.GetSecurityGroups(vpcId, "", secgroupId, 0, 0)
if err != nil {
return nil, err
}
if total != 1 {
log.Debugf("failed to find SecurityGroup %s: %d found", secgroupId, total)
return nil, httperrors.NewNotFoundError("failed to find SecurityGroup %s", secgroupId)
}
return &secgroups[0], nil
}
func (self *SRegion) addTagToSecurityGroup(secgroupId, key, value string, index int) error {
return nil
}
func (self *SRegion) modifySecurityGroup(secGrpId string, name string, desc string) error {
tagspec := TagSpec{ResourceType: "security-group"}
tagspec.SetNameTag(name)
tagspec.SetDescTag(desc)
ec2Tags, _ := tagspec.GetTagSpecifications()
params := &ec2.CreateTagsInput{}
params.SetTags(ec2Tags.Tags)
params.SetResources([]*string{&secGrpId})
_, err := self.ec2Client.CreateTags(params)
if err != nil {
return err
}
return nil
}
func (self *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) error {
var DeleteRules []secrules.SecurityRule
var AddRules []secrules.SecurityRule
if secgroup, err := self.GetSecurityGroupDetails(secgroupId); err != nil {
return err
} else {
sort.Sort(secrules.SecurityRuleSet(rules))
sort.Sort(secrules.SecurityRuleSet(secgroup.Permissions))
i, j := 0, 0
for i < len(rules) || j < len(secgroup.Permissions) {
if i < len(rules) && j < len(secgroup.Permissions) {
permissionStr := secgroup.Permissions[j].String()
ruleStr := rules[i].String()
cmp := strings.Compare(permissionStr, ruleStr)
if cmp == 0 {
DeleteRules = append(DeleteRules, secgroup.Permissions[j])
AddRules = append(AddRules, rules[i])
i += 1
j += 1
} else if cmp > 0 {
DeleteRules = append(DeleteRules, secgroup.Permissions[j])
j += 1
} else {
AddRules = append(AddRules, rules[i])
i += 1
}
} else if i >= len(rules) {
DeleteRules = append(DeleteRules, secgroup.Permissions[j])
j += 1
} else if j >= len(secgroup.Permissions) {
AddRules = append(AddRules, rules[i])
i += 1
}
}
}
for _, r := range DeleteRules {
if err := self.delSecurityGroupRule(secgroupId, &r); err != nil {
if strings.Contains(err.Error(), "InvalidPermission.NotFound") {
continue
}
log.Errorf("delSecurityGroupRule %v error: %s", r, err.Error())
return err
}
}
for _, r := range AddRules {
if err := self.addSecurityGroupRules(secgroupId, &r); err != nil {
log.Errorf("addSecurityGroupRule %v error: %s", r, err.Error())
return err
}
}
return nil
}
func (self *SRegion) getSecRules(ingress []*ec2.IpPermission, egress []*ec2.IpPermission) []secrules.SecurityRule {
rules := []secrules.SecurityRule{}
for _, p := range ingress {
ret, err := AwsIpPermissionToYunion(secrules.SecurityRuleIngress, *p)
if err != nil {
log.Debugln(err)
}
for _, rule := range ret {
rules = append(rules, rule)
}
}
for _, p := range egress {
ret, err := AwsIpPermissionToYunion(secrules.SecurityRuleEgress, *p)
if err != nil {
log.Debugln(err)
}
for _, rule := range ret {
rules = append(rules, rule)
}
}
return rules
}
func (self *SRegion) GetSecurityGroups(vpcId string, name string, secgroupId string, offset int, limit int) ([]SSecurityGroup, int, error) {
params := &ec2.DescribeSecurityGroupsInput{}
filters := make([]*ec2.Filter, 0)
if len(vpcId) > 0 {
filters = AppendSingleValueFilter(filters, "vpc-id", vpcId)
}
if len(name) > 0 {
filters = AppendSingleValueFilter(filters, "group-name", name)
}
if len(secgroupId) > 0 {
params.SetGroupIds([]*string{&secgroupId})
}
if len(filters) > 0 {
params.SetFilters(filters)
}
ret, err := self.ec2Client.DescribeSecurityGroups(params)
err = parseNotFoundError(err)
if err != nil {
return nil, 0, err
}
securityGroups := []SSecurityGroup{}
for _, item := range ret.SecurityGroups {
if err := FillZero(item); err != nil {
return nil, 0, err
}
if len(*item.VpcId) == 0 {
log.Debugf("ingored: security group with no vpc binded")
continue
}
vpc, err := self.getVpc(*item.VpcId)
if err != nil {
log.Errorf("vpc %s not found", *item.VpcId)
continue
}
permissions := self.getSecRules(item.IpPermissions, item.IpPermissionsEgress)
group := SSecurityGroup{
vpc: vpc,
Description: *item.Description,
SecurityGroupId: *item.GroupId,
SecurityGroupName: *item.GroupName,
VpcId: *item.VpcId,
Permissions: permissions,
RegionId: self.RegionId,
// Tags: *item.Tags,
}
securityGroups = append(securityGroups, group)
}
return securityGroups, len(securityGroups), nil
}
func (self *SSecurityGroup) GetProjectId() string {
return ""
}
func (self *SSecurityGroup) SyncRules(rules []secrules.SecurityRule) error {
rules = SecurityRuleSetToAllowSet(rules)
return self.vpc.region.syncSecgroupRules(self.SecurityGroupId, rules)
}
func (self *SSecurityGroup) Delete() error {
return self.vpc.region.DeleteSecurityGroup(self.SecurityGroupId)
}