pkg/skoop/network/aliyun/infrashim.go (189 lines of code) (raw):

package aliyun import ( "fmt" "net" "strings" slb "github.com/alibabacloud-go/slb-20140515/v4/client" "github.com/samber/lo" "github.com/alibaba/kubeskoop/pkg/skoop/infra/aliyun" "github.com/alibaba/kubeskoop/pkg/skoop/model" "github.com/alibaba/kubeskoop/pkg/skoop/network" v1 "k8s.io/api/core/v1" "k8s.io/klog/v2" ) type aliyunInfraShim struct { cloudManager *aliyun.CloudManager vpcAssertion *vpcAssertion slbAssertion *slbAssertion } func NewInfraShim(cloudManager *aliyun.CloudManager) (network.InfraShim, error) { vpcAssertion, err := newVPCAssertion(cloudManager) if err != nil { return nil, err } slbAssertion, err := newSLBAssertion(cloudManager) if err != nil { return nil, err } return &aliyunInfraShim{ vpcAssertion: vpcAssertion, slbAssertion: slbAssertion, cloudManager: cloudManager, }, nil } func (s *aliyunInfraShim) NodeToNode(src *v1.Node, _ string, dst *v1.Node, packet *model.Packet) ([]model.Suspicion, error) { klog.V(3).Infof("Node to node packet %+v", packet) srcID, err := s.getECSFromNode(src) if err != nil { return nil, err } dstID, err := s.getECSFromNode(dst) if err != nil { return nil, err } suspicions, err := s.vpcAssertion.AssertSecurityGroup(srcID, dstID, packet) if err != nil { return nil, err } routeSuspicions, err := s.vpcAssertion.AssertRoute(srcID, dstID, packet, "") if err != nil { return nil, err } suspicions = append(suspicions, routeSuspicions...) return suspicions, nil } func (s *aliyunInfraShim) NodeToExternal(src *v1.Node, _ string, packet *model.Packet) ([]model.Suspicion, error) { srcID, err := s.getECSFromNode(src) if err != nil { return nil, err } ecsInfo, err := s.cloudManager.GetECSInfo(srcID) if err != nil { return nil, err } if s.isPrivate(packet.Dst) { return s.vpcAssertion.AssertSecurityGroup(srcID, "", packet) } var suspicions []model.Suspicion if ecsInfo.Network.EIPAddress != "" { klog.V(3).Infof("Use eip address for ecs %q, packet %q", srcID, packet) packet.Src = net.ParseIP(ecsInfo.Network.EIPAddress) suspicions, err = s.vpcAssertion.AssertSecurityGroup(srcID, "", packet) if err != nil { return nil, err } } else { klog.V(3).Infof("Use nat gateway for ecs %q, packet %q", srcID, packet) suspicions, err = s.vpcAssertion.AssertSecurityGroup(srcID, "", packet) if err != nil { return nil, err } snatSuspicions, err := s.vpcAssertion.AssertSNAT(srcID, packet, ecsInfo.Network.IP[0]) if err != nil { return nil, err } suspicions = append(suspicions, snatSuspicions...) } return suspicions, nil } func (s *aliyunInfraShim) ExternalToNode(dst *v1.Node, packet *model.Packet) ([]model.Suspicion, error) { dstID, err := s.getECSFromNode(dst) if err != nil { return nil, err } ecsInfo, err := s.cloudManager.GetECSInfo(dstID) if err != nil { return nil, err } suspicions, err := s.vpcAssertion.AssertSecurityGroup("", dstID, packet) if err != nil { return nil, err } if ecsInfo.Network.EIPAddress != "" && ecsInfo.Network.EIPAddress == packet.Dst.String() { return suspicions, nil } routeSuspicions, err := s.vpcAssertion.AssertRoute("", dstID, packet, "") if err != nil { return nil, err } suspicions = append(suspicions, routeSuspicions...) return suspicions, nil } func (s *aliyunInfraShim) ExternalToLoadBalancer(dst *v1.Service, packet *model.Packet, backends []network.LoadBalancerBackend) ([]model.Suspicion, error) { var sus []model.Suspicion lb, err := s.getCLBIDFromService(dst) if err != nil { return nil, err } lbSuspicions, err := s.slbAssertion.assertLoadBalancer(lb, packet.Dport, packet.Protocol) if err != nil { return nil, err } sus = append(sus, lbSuspicions...) // todo: support protocol set by annotation // todo: support named port p, found := lo.Find(dst.Spec.Ports, func(i v1.ServicePort) bool { return i.Port == int32(packet.Dport) && strings.EqualFold(string(i.Protocol), string(packet.Protocol)) }) if !found { return nil, fmt.Errorf("cannot find port %d protocol %s on service", packet.Dport, packet.Protocol) } protocol := strings.ToLower(string(p.Protocol)) portSuspicions, err := s.slbAssertion.assertListenerAndServerGroup(*lb.LoadBalancerId, p.Port, protocol, packet, backends) if err != nil { return nil, err } sus = append(sus, portSuspicions...) return sus, nil } func (s *aliyunInfraShim) getECSFromNode(node *v1.Node) (string, error) { providerIDs := strings.Split(node.Spec.ProviderID, ".") if len(providerIDs) != 2 { return "", fmt.Errorf("provider id %s does not match format <region>.<instance>", node.Spec.ProviderID) } return providerIDs[1], nil } func (s *aliyunInfraShim) getCLBIDFromService(svc *v1.Service) (*slb.DescribeLoadBalancersResponseBodyLoadBalancersLoadBalancer, error) { if id, ok := svc.Labels["service.k8s.alibaba/loadbalancer-id"]; ok { lb, err := s.cloudManager.GetSLBFromID(id) if err != nil { return nil, err } if lb == nil { return nil, fmt.Errorf("cannot find loadbalacner by id %q", id) } return lb, nil } if len(svc.Status.LoadBalancer.Ingress) == 0 { return nil, fmt.Errorf("cannot find loadbalancer ip on service %s/%s", svc.Namespace, svc.Name) } ip := net.ParseIP(svc.Status.LoadBalancer.Ingress[0].IP) if ip == nil { return nil, fmt.Errorf("ip %q on service %s/%s is invalid", svc.Status.LoadBalancer.Ingress[0].IP, svc.Namespace, svc.Name) } var lb *slb.DescribeLoadBalancersResponseBodyLoadBalancersLoadBalancer var err error if s.isPrivate(ip) { lb, err = s.cloudManager.GetSLBFromPrivateIP(ip.String()) } else { lb, err = s.cloudManager.GetSLBFromPublicIP(ip.String()) } if lb == nil { return nil, fmt.Errorf("cannot find loadbalancer by ip %q", ip) } if err != nil { return nil, err } return lb, nil } func (s *aliyunInfraShim) isPrivate(ip net.IP) bool { // vpc reserved address 100.64.0.0/10 if ip[0] == 100 && ip[1]&0xc0 == 64 { return true } for _, cidr := range s.cloudManager.VPCCIDRs() { if cidr.Contains(ip) { return true } } return false }