mirror of https://github.com/kubernetes/kops.git
334 lines
8.1 KiB
Go
334 lines
8.1 KiB
Go
/*
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
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 awstasks
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
|
"github.com/golang/glog"
|
|
"k8s.io/kops/upup/pkg/fi"
|
|
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
|
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
|
"k8s.io/kubernetes/pkg/util/validation/field"
|
|
"strings"
|
|
)
|
|
|
|
//go:generate fitask -type=SecurityGroupRule
|
|
type SecurityGroupRule struct {
|
|
Name *string
|
|
|
|
SecurityGroup *SecurityGroup
|
|
CIDR *string
|
|
Protocol *string
|
|
|
|
// FromPort is the lower-bound (inclusive) of the port-range
|
|
FromPort *int64
|
|
// ToPort is the upper-bound (inclusive) of the port-range
|
|
ToPort *int64
|
|
SourceGroup *SecurityGroup
|
|
|
|
Egress *bool
|
|
}
|
|
|
|
func (e *SecurityGroupRule) Find(c *fi.Context) (*SecurityGroupRule, error) {
|
|
cloud := c.Cloud.(awsup.AWSCloud)
|
|
|
|
if e.SecurityGroup == nil || e.SecurityGroup.ID == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if e.SourceGroup != nil && e.SourceGroup.ID == nil {
|
|
glog.V(4).Infof("Skipping find of SecurityGroupRule %s, because SourceGroup was not found", fi.StringValue(e.Name))
|
|
return nil, nil
|
|
}
|
|
|
|
request := &ec2.DescribeSecurityGroupsInput{
|
|
Filters: []*ec2.Filter{
|
|
awsup.NewEC2Filter("group-id", *e.SecurityGroup.ID),
|
|
},
|
|
}
|
|
|
|
response, err := cloud.EC2().DescribeSecurityGroups(request)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error listing SecurityGroup: %v", err)
|
|
}
|
|
|
|
if response == nil || len(response.SecurityGroups) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
if len(response.SecurityGroups) != 1 {
|
|
glog.Fatalf("found multiple security groups for id=%s", *e.SecurityGroup.ID)
|
|
}
|
|
sg := response.SecurityGroups[0]
|
|
//glog.V(2).Info("found existing security group")
|
|
|
|
var foundRule *ec2.IpPermission
|
|
|
|
ipPermissions := sg.IpPermissions
|
|
if fi.BoolValue(e.Egress) {
|
|
ipPermissions = sg.IpPermissionsEgress
|
|
}
|
|
|
|
for _, rule := range ipPermissions {
|
|
if e.matches(rule) {
|
|
foundRule = rule
|
|
break
|
|
}
|
|
}
|
|
|
|
if foundRule != nil {
|
|
actual := &SecurityGroupRule{
|
|
Name: e.Name,
|
|
SecurityGroup: &SecurityGroup{ID: e.SecurityGroup.ID},
|
|
FromPort: foundRule.FromPort,
|
|
ToPort: foundRule.ToPort,
|
|
Protocol: foundRule.IpProtocol,
|
|
Egress: e.Egress,
|
|
}
|
|
|
|
if aws.StringValue(actual.Protocol) == "-1" {
|
|
actual.Protocol = nil
|
|
}
|
|
if e.CIDR != nil {
|
|
actual.CIDR = e.CIDR
|
|
}
|
|
if e.SourceGroup != nil {
|
|
actual.SourceGroup = &SecurityGroup{ID: e.SourceGroup.ID}
|
|
}
|
|
return actual, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (e *SecurityGroupRule) matches(rule *ec2.IpPermission) bool {
|
|
if aws.Int64Value(rule.FromPort) != aws.Int64Value(e.FromPort) {
|
|
return false
|
|
}
|
|
if aws.Int64Value(rule.ToPort) != aws.Int64Value(e.ToPort) {
|
|
return false
|
|
}
|
|
|
|
matchProtocol := "-1" // Wildcard
|
|
if e.Protocol != nil {
|
|
matchProtocol = *e.Protocol
|
|
}
|
|
if aws.StringValue(rule.IpProtocol) != matchProtocol {
|
|
return false
|
|
}
|
|
|
|
if e.CIDR != nil {
|
|
// TODO: Only if len 1?
|
|
match := false
|
|
for _, ipRange := range rule.IpRanges {
|
|
if aws.StringValue(ipRange.CidrIp) == *e.CIDR {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
if !match {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if e.SourceGroup != nil {
|
|
// TODO: Only if len 1?
|
|
match := false
|
|
for _, spec := range rule.UserIdGroupPairs {
|
|
if e.SourceGroup == nil {
|
|
continue
|
|
}
|
|
|
|
if e.SourceGroup.ID == nil {
|
|
glog.Warningf("SourceGroup had nil ID: %v", e.SourceGroup)
|
|
continue
|
|
}
|
|
|
|
if aws.StringValue(spec.GroupId) == *e.SourceGroup.ID {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
if !match {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (e *SecurityGroupRule) Run(c *fi.Context) error {
|
|
return fi.DefaultDeltaRunMethod(e, c)
|
|
}
|
|
|
|
func (_ *SecurityGroupRule) CheckChanges(a, e, changes *SecurityGroupRule) error {
|
|
if a == nil {
|
|
if e.SecurityGroup == nil {
|
|
return field.Required(field.NewPath("SecurityGroup"), "")
|
|
}
|
|
}
|
|
|
|
if e.FromPort != nil && e.Protocol == nil {
|
|
return field.Required(field.NewPath("Protocol"), "Protocol must be specified with FromPort")
|
|
}
|
|
if e.ToPort != nil && e.Protocol == nil {
|
|
return field.Required(field.NewPath("Protocol"), "Protocol must be specified with ToPort")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Description returns a human readable summary of the security group rule
|
|
func (e *SecurityGroupRule) Description() string {
|
|
var description []string
|
|
|
|
if e.Protocol != nil {
|
|
description = append(description, fmt.Sprintf("protocol=%s", *e.Protocol))
|
|
}
|
|
|
|
if e.FromPort != nil {
|
|
description = append(description, fmt.Sprintf("fromPort=%d", *e.FromPort))
|
|
}
|
|
|
|
if e.ToPort != nil {
|
|
description = append(description, fmt.Sprintf("toPort=%d", *e.ToPort))
|
|
}
|
|
|
|
if e.SourceGroup != nil {
|
|
description = append(description, fmt.Sprintf("sourceGroup=%s", fi.StringValue(e.SourceGroup.ID)))
|
|
}
|
|
|
|
if e.CIDR != nil {
|
|
description = append(description, fmt.Sprintf("cidr=%s", *e.CIDR))
|
|
}
|
|
|
|
return strings.Join(description, " ")
|
|
}
|
|
|
|
func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SecurityGroupRule) error {
|
|
name := fi.StringValue(e.Name)
|
|
|
|
if a == nil {
|
|
protocol := e.Protocol
|
|
if protocol == nil {
|
|
protocol = aws.String("-1")
|
|
}
|
|
|
|
ipPermission := &ec2.IpPermission{
|
|
IpProtocol: protocol,
|
|
FromPort: e.FromPort,
|
|
ToPort: e.ToPort,
|
|
}
|
|
|
|
if e.SourceGroup != nil {
|
|
ipPermission.UserIdGroupPairs = []*ec2.UserIdGroupPair{
|
|
{
|
|
GroupId: e.SourceGroup.ID,
|
|
},
|
|
}
|
|
} else {
|
|
// Default to 0.0.0.0/0 ?
|
|
ipPermission.IpRanges = []*ec2.IpRange{
|
|
{CidrIp: e.CIDR},
|
|
}
|
|
}
|
|
|
|
description := e.Description()
|
|
|
|
if fi.BoolValue(e.Egress) {
|
|
request := &ec2.AuthorizeSecurityGroupEgressInput{
|
|
GroupId: e.SecurityGroup.ID,
|
|
}
|
|
request.IpPermissions = []*ec2.IpPermission{ipPermission}
|
|
|
|
glog.V(2).Infof("%s: Calling EC2 AuthorizeSecurityGroupEgress (%s)", name, description)
|
|
_, err := t.Cloud.EC2().AuthorizeSecurityGroupEgress(request)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating SecurityGroupEgress: %v", err)
|
|
}
|
|
} else {
|
|
request := &ec2.AuthorizeSecurityGroupIngressInput{
|
|
GroupId: e.SecurityGroup.ID,
|
|
}
|
|
request.IpPermissions = []*ec2.IpPermission{ipPermission}
|
|
|
|
glog.V(2).Infof("%s: Calling EC2 AuthorizeSecurityGroupIngress (%s)", name, description)
|
|
_, err := t.Cloud.EC2().AuthorizeSecurityGroupIngress(request)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating SecurityGroupIngress: %v", err)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// No tags on security group rules (there are tags on the group though)
|
|
|
|
return nil
|
|
}
|
|
|
|
type terraformSecurityGroupIngress struct {
|
|
Type *string `json:"type"`
|
|
|
|
SecurityGroup *terraform.Literal `json:"security_group_id"`
|
|
SourceGroup *terraform.Literal `json:"source_security_group_id,omitempty"`
|
|
|
|
FromPort *int64 `json:"from_port,omitempty"`
|
|
ToPort *int64 `json:"to_port,omitempty"`
|
|
|
|
Protocol *string `json:"protocol,omitempty"`
|
|
CIDRBlocks []string `json:"cidr_blocks,omitempty"`
|
|
}
|
|
|
|
func (_ *SecurityGroupRule) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *SecurityGroupRule) error {
|
|
tf := &terraformSecurityGroupIngress{
|
|
Type: fi.String("ingress"),
|
|
SecurityGroup: e.SecurityGroup.TerraformLink(),
|
|
FromPort: e.FromPort,
|
|
ToPort: e.ToPort,
|
|
Protocol: e.Protocol,
|
|
}
|
|
if fi.BoolValue(e.Egress) {
|
|
tf.Type = fi.String("egress")
|
|
}
|
|
|
|
if e.Protocol == nil {
|
|
tf.Protocol = fi.String("-1")
|
|
tf.FromPort = fi.Int64(0)
|
|
tf.ToPort = fi.Int64(0)
|
|
}
|
|
|
|
if tf.FromPort == nil {
|
|
// FromPort is required by tf
|
|
tf.FromPort = fi.Int64(0)
|
|
}
|
|
if tf.ToPort == nil {
|
|
// ToPort is required by tf
|
|
tf.ToPort = fi.Int64(65535)
|
|
}
|
|
|
|
if e.SourceGroup != nil {
|
|
tf.SourceGroup = e.SourceGroup.TerraformLink()
|
|
}
|
|
|
|
if e.CIDR != nil {
|
|
tf.CIDRBlocks = append(tf.CIDRBlocks, *e.CIDR)
|
|
}
|
|
return t.RenderResource("aws_security_group_rule", *e.Name, tf)
|
|
}
|