github.com/cilium/cilium@v1.16.2/pkg/policy/groups/aws/aws.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package aws 5 6 import ( 7 "context" 8 "fmt" 9 "net/netip" 10 11 "github.com/aws/aws-sdk-go-v2/aws" 12 "github.com/aws/aws-sdk-go-v2/service/ec2" 13 ec2_types "github.com/aws/aws-sdk-go-v2/service/ec2/types" 14 15 cilium_ec2 "github.com/cilium/cilium/pkg/aws/ec2" 16 "github.com/cilium/cilium/pkg/policy/api" 17 ) 18 19 var ( 20 policySecurityGroupIDKey = aws.String("group-id") 21 policySecurityGroupName = aws.String("group-name") 22 policyEC2Labelskey = "tag" 23 ) 24 25 func init() { 26 api.RegisterToGroupsProvider(api.AWSProvider, GetIPsFromGroup) 27 } 28 29 // GetIPsFromGroup will return the list of the IPs for the given group filter 30 func GetIPsFromGroup(ctx context.Context, group *api.Groups) ([]netip.Addr, error) { 31 result := []netip.Addr{} 32 if group.AWS == nil { 33 return result, fmt.Errorf("no aws data available") 34 } 35 36 cfg, err := cilium_ec2.NewConfig(ctx) 37 if err != nil { 38 return nil, err 39 } 40 ec2Client := ec2.NewFromConfig(cfg) 41 42 // If the group has a security group filter, add the IPs from the network interfaces 43 if len(group.AWS.SecurityGroupsIds) > 0 || len(group.AWS.SecurityGroupsNames) > 0 { 44 ips, err := getNetworkInterfaceIpsFromFilter(ctx, group.AWS, ec2Client) 45 if err != nil { 46 return nil, err 47 } 48 result = append(result, ips...) 49 } 50 51 // If the group has a label filter, add the IPs from the instances 52 if len(group.AWS.Labels) > 0 { 53 ips, err := getInstancesIpsFromFilter(ctx, group.AWS, ec2Client) 54 if err != nil { 55 return nil, err 56 } 57 result = append(result, ips...) 58 } 59 60 return result, nil 61 } 62 63 // getNetworkInterfaceIpsFromFilter returns the IPs from the network interfaces for 64 // the given security group filter 65 func getNetworkInterfaceIpsFromFilter(ctx context.Context, filter *api.AWSGroup, ec2Client *ec2.Client) ([]netip.Addr, error) { 66 result := []netip.Addr{} 67 input := &ec2.DescribeNetworkInterfacesInput{} 68 69 if len(filter.SecurityGroupsIds) > 0 { 70 input.Filters = append(input.Filters, ec2_types.Filter{ 71 Name: policySecurityGroupIDKey, 72 Values: filter.SecurityGroupsIds, 73 }) 74 } 75 if len(filter.SecurityGroupsNames) > 0 { 76 input.Filters = append(input.Filters, ec2_types.Filter{ 77 Name: policySecurityGroupName, 78 Values: filter.SecurityGroupsNames, 79 }) 80 } 81 82 paginator := ec2.NewDescribeNetworkInterfacesPaginator(ec2Client, input) 83 for paginator.HasMorePages() { 84 output, err := paginator.NextPage(ctx) 85 if err != nil { 86 return nil, fmt.Errorf("cannot retrieve aws network interface information: %w", err) 87 } 88 // functionally equivalent to extractIPs, we return private IPs and associated public IPs 89 for _, iface := range output.NetworkInterfaces { 90 for _, ifaceIP := range iface.PrivateIpAddresses { 91 addr, err := netip.ParseAddr(aws.ToString(ifaceIP.PrivateIpAddress)) 92 if err != nil { 93 continue 94 } 95 result = append(result, addr) 96 if ifaceIP.Association != nil { 97 addr, err = netip.ParseAddr(aws.ToString(ifaceIP.Association.PublicIp)) 98 if err != nil { 99 continue 100 } 101 result = append(result, addr) 102 } 103 } 104 } 105 } 106 return result, nil 107 } 108 109 // getInstancesIpsFromFilter returns IPs from matching instances for the given 110 // label filter 111 func getInstancesIpsFromFilter(ctx context.Context, filter *api.AWSGroup, ec2Client *ec2.Client) ([]netip.Addr, error) { 112 var result []ec2_types.Reservation 113 input := &ec2.DescribeInstancesInput{} 114 115 for labelKey, labelValue := range filter.Labels { 116 newFilter := ec2_types.Filter{ 117 Name: aws.String(policyEC2Labelskey + ":" + labelKey), 118 Values: []string{labelValue}, 119 } 120 input.Filters = append(input.Filters, newFilter) 121 } 122 123 paginator := ec2.NewDescribeInstancesPaginator(ec2Client, input) 124 for paginator.HasMorePages() { 125 output, err := paginator.NextPage(ctx) 126 if err != nil { 127 return nil, fmt.Errorf("cannot retrieve aws ec2 instance information: %w", err) 128 } 129 result = append(result, output.Reservations...) 130 } 131 return extractIPs(result), nil 132 } 133 134 // extractIPs returns the private and associated public IPs from the given reservations 135 func extractIPs(reservations []ec2_types.Reservation) []netip.Addr { 136 result := []netip.Addr{} 137 for _, reservation := range reservations { 138 for _, instance := range reservation.Instances { 139 for _, iface := range instance.NetworkInterfaces { 140 for _, ifaceIP := range iface.PrivateIpAddresses { 141 addr, err := netip.ParseAddr(aws.ToString(ifaceIP.PrivateIpAddress)) 142 if err != nil { 143 continue 144 } 145 result = append(result, addr) 146 if ifaceIP.Association != nil { 147 addr, err = netip.ParseAddr(aws.ToString(ifaceIP.Association.PublicIp)) 148 if err != nil { 149 continue 150 } 151 result = append(result, addr) 152 } 153 } 154 } 155 } 156 } 157 return result 158 }