Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

[WIP] Add validation for default ingresscontroller load balancer #16

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.18.8
github.com/aws/aws-sdk-go-v2/credentials v1.13.8
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.15.0
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.19.0
github.com/aws/aws-sdk-go-v2/service/route53 v1.26.0
github.com/openshift-online/ocm-cli v0.1.65
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0 h1:m6HYlpZlTWb9vHuuRHpWRieqPHWlS0mvQ90OJNrG/Nk=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.77.0/go.mod h1:mV0E7631M1eXdB+tlGFIw6JxfsC7Pz7+7Aw15oLVhZw=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.15.0 h1:FFfQypN9iItIrGhbl8em90uXMFBLrCkNC1yJ65+m9Sk=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.15.0/go.mod h1:3OUv9SlYvymsCF3I5NftITc1+B09cF5lg4IsZgQQy1U=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.19.0 h1:Fs+mQ2VSOH3YhNJcfImnl7dsKAm/gqw4Q9iqLRIiPWE=
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.19.0/go.mod h1:ix71C17la8K2MUJrqJzu+i7+aPoQYTAy14hKQbGDB9w=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func main() {
sugared.Infof("%s: \"Mirror mirror on the wall, who's the fairest of them all?\"", mirrosa.ClusterInfo.Name)

if err := mirrosa.ValidateComponents(context.TODO(),
mirrosa.NewIngressControllerLoadBalancer(),
mirrosa.NewVpc(),
mirrosa.NewDhcpOptions(),
mirrosa.NewSecurityGroup(),
Expand Down
12 changes: 6 additions & 6 deletions pkg/mirrosa/api_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const (
"\n - An internal (-int) NLB to balance traffic within the cluster's VPC."
)

// elb represents the expected state of an Elastic Load Balancer in AWS
type elb struct {
// mirrosaElb represents the expected state of an Elastic Load Balancer in AWS
type mirrosaElb struct {
name string
expectedListeners map[string]listener
}
Expand Down Expand Up @@ -143,10 +143,10 @@ func (n NetworkLoadBalancer) FilterValue() string {
}

// getExpectedNLBs returns a map of expected elb instances given a NetworkLoadBalancer Component
func (n NetworkLoadBalancer) getExpectedNLBs() map[string]elb {
expected := map[string]elb{}
func (n NetworkLoadBalancer) getExpectedNLBs() map[string]mirrosaElb {
expected := map[string]mirrosaElb{}

expected["api-int"] = elb{
expected["api-int"] = mirrosaElb{
name: fmt.Sprintf("%s-int", n.InfraName),
expectedListeners: map[string]listener{
"etcd": {
Expand All @@ -164,7 +164,7 @@ func (n NetworkLoadBalancer) getExpectedNLBs() map[string]elb {

// TODO: Handle non-STS, where the external NLB is optional if it is a private cluster
if !n.PrivateLink && n.Sts {
expected["api-ext"] = elb{
expected["api-ext"] = mirrosaElb{
name: fmt.Sprintf("%s-ext", n.InfraName),
expectedListeners: map[string]listener{
"kube-apiserver": {
Expand Down
164 changes: 164 additions & 0 deletions pkg/mirrosa/ingresscontroller_loadbalancer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package mirrosa

import (
"context"
"errors"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"go.uber.org/zap"
)

const (
defaultIngressControllerLoadBalancerTagKey = "kubernetes.io/service-name"
defaultIngressControllerLoadBalancerTagValue = "openshift-ingress/router-default"
defaultIngressControllerLoadBalancerDescription = "TODO"
)

var _ Component = &DefaultIngressControllerLoadBalancer{}

type DefaultIngressControllerLoadBalancer struct {
log *zap.SugaredLogger
InfraName string

Ec2Client Ec2AwsApi
ElbClient *elb.Client
ElbV2Client NetworkLoadBalancerAPIClient
}

func (c *Client) NewIngressControllerLoadBalancer() DefaultIngressControllerLoadBalancer {
return DefaultIngressControllerLoadBalancer{
log: c.log,
InfraName: c.ClusterInfo.InfraName,
Ec2Client: ec2.NewFromConfig(c.AwsConfig),
ElbClient: elb.NewFromConfig(c.AwsConfig),
ElbV2Client: elbv2.NewFromConfig(c.AwsConfig),
}
}

func (d DefaultIngressControllerLoadBalancer) Validate(ctx context.Context) error {
d.log.Info("searching for default ingress controller load balancer")

d.log.Info("searching classic load balancers")
clb, err := d.searchForCLB(ctx)
if err != nil {
d.log.Info("failed to find classic load balancer for default ingress controller")
// TODO: Search NLBs
}

if len(clb.SecurityGroups) != 1 {
return fmt.Errorf("expected 1 security group attached to the default ingress controller load balancer, found %d", len(clb.SecurityGroups))
}

resp, err := d.Ec2Client.DescribeSecurityGroupRules(ctx, &ec2.DescribeSecurityGroupRulesInput{
Filters: []ec2Types.Filter{
{
Name: aws.String("group-id"),
Values: []string{clb.SecurityGroups[0]},
},
},
})
if err != nil {
return err
}

expectedRules := map[string]securityGroupRule{
"http": {
CidrIpv4: "0.0.0.0/0",
IpProtocol: ec2Types.ProtocolTcp,
FromPort: 80,
ToPort: 80,
IsEgress: false,
},
"https": {
CidrIpv4: "0.0.0.0/0",
IpProtocol: ec2Types.ProtocolTcp,
FromPort: 443,
ToPort: 443,
IsEgress: false,
},
"icmp": {
CidrIpv4: "0.0.0.0/0",
IpProtocol: "icmp",
FromPort: 3,
ToPort: 4,
IsEgress: false,
},
"egress": {
CidrIpv4: "0.0.0.0/0",
IpProtocol: "-1",
FromPort: -1,
ToPort: -1,
IsEgress: true,
},
}

for _, rule := range resp.SecurityGroupRules {
// If we've found all the required security group rules, stop
if len(expectedRules) == 0 {
break
}

d.log.Debugf("found security group rule %s", *rule.SecurityGroupRuleId)
for k, expectedRule := range expectedRules {
if compareSecurityGroupRules(expectedRule, rule) {
d.log.Infof("security group rule validated for %s: %+v", k, expectedRule)
delete(expectedRules, k)
}
}
}

if len(expectedRules) > 0 {
return fmt.Errorf("missing required rules in default ingress controller load balancer security group %v", expectedRules)
}

return nil
}

func (d DefaultIngressControllerLoadBalancer) Documentation() string {
return defaultIngressControllerLoadBalancerDescription
}

func (d DefaultIngressControllerLoadBalancer) FilterValue() string {
return "Default Ingress Controller Load Balancer"
}

func (d DefaultIngressControllerLoadBalancer) searchForCLB(ctx context.Context) (*types.LoadBalancerDescription, error) {
resp, err := d.ElbClient.DescribeLoadBalancers(ctx, &elb.DescribeLoadBalancersInput{})
if err != nil {
return nil, fmt.Errorf("failed to describe CLBs: %w", err)
}

for _, clb := range resp.LoadBalancerDescriptions {
expectedTags := map[string]string{
defaultIngressControllerLoadBalancerTagKey: defaultIngressControllerLoadBalancerTagValue,
fmt.Sprintf("kubernetes.io/cluster/%s", d.InfraName): "owned",
}

tagsResp, err := d.ElbClient.DescribeTags(ctx, &elb.DescribeTagsInput{
LoadBalancerNames: []string{*clb.LoadBalancerName},
})
if err != nil {
return nil, fmt.Errorf("failed to describe tags of CLB %s:%w", *clb.LoadBalancerName, err)
}

for _, tag := range tagsResp.TagDescriptions[0].Tags {
if len(expectedTags) == 0 {
return &clb, nil
}

if v, ok := expectedTags[*tag.Key]; ok {
if v == *tag.Value {
d.log.Infof("found match for tag %s:%s", *tag.Key, *tag.Value)
delete(expectedTags, *tag.Key)
}
}
}
}

return nil, errors.New("no matching CLB found")
}