Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filtering AWS CLI output for security groups that have specific port and IP ACLs

I'm having difficulty filtering AWS CLI describe-security-groups output

Goal: Find all SGs with ingress rules on port 22 with cidr 0.0.0.0/0

Amazons' own docs provide an example, but state there's a limitation with their query in that it will first filter the entire data set for port 22 then filter that entire data set for 0.0.0.0/0. What this means is that SGs with the following rules will still trigger:

ingress 22 sg-12345678
ingress 443 0.0.0.0/0

This completely defeats the purpose of the filtering, and I'm not even sure why Amazon provides that example with a heading of "To describe security groups that have specific rules"

Route 1: aws cli query first then jq

This route is based off what I found here: https://github.com/aws/aws-cli/issues/971

aws ec2 describe-security-groups --output json --query 'SecurityGroups[*].[GroupName,GroupId,IpPermissions[?ToPort==`22`].[IpRanges[?CidrIp==`0.0.0.0/0`]]]'

Which provides a list of all security groups, but showing nested data for any SGs with 22 0.0.0.0/0 (and successfully ignoring any 0.0.0.0/0 ACLs for other ports) In the output below, SG1 is what I'm interested, and SG2/SG3 needs to be filtered out.

[
    [
        "SG 1", 
        "sg-11111111", 
        [
            [
                [
                    {
                        "CidrIp": "0.0.0.0/0"
                    }
                ]
            ]
        ]
    ],
    [
        "SG 2",
        "sg-22222222",
        [
            [
                []
            ]
        ]
    ],
    [
        "SG 3", 
        "sg-33333333", 
        []
    ]
]

This is a great first step as I've eliminated 0.0.0.0/0 ACLs that aren't associated with port 22. But as I try to run jq to simply remove entries that have empty data sets, I'm having difficulty cause all the keys have been stripped.

  • As I try to select deeper into the nested sections, I end up with errors like cannot iterate over null
  • If I try to use contains, I just get back nothing, so I'm not even sure where I'm going wrong there

Route 2: jq un-queried CLI output

I've been unable to get away from the pitfall of the original AWS example by using jq from the start, where I can first query for all SGs that contain port 22, then query for any ACLs of 0.0.0.0/0, which of course gives me false positives. Due to the stream nature of jq, I haven't figured out how to check condition A (port 22) then check condition B (0.0.0.0/0) only on items related to condition A.

Here's some sanitized raw CLI output of 2 SGs, again, I need to get the first one without triggering a false positive on the 2nd

{
    "SecurityGroups": [
        {
            "Description": "SG 1", 
            "IpPermissions": [
                {
                    "PrefixListIds": [], 
                    "FromPort": 22, 
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ], 
                    "ToPort": 22, 
                    "IpProtocol": "tcp", 
                    "UserIdGroupPairs": [], 
                    "Ipv6Ranges": []
                }
            ], 
            "GroupName": "SG 1",
            "VpcId": "vpc-12345678", 
            "OwnerId": "1234567890", 
            "GroupId": "sg-11111111"
        }, 
        {
            "Description": "SG 2", 
            "IpPermissions": [
                {
                    "PrefixListIds": [], 
                    "FromPort": 22, 
                    "IpRanges": [], 
                    "ToPort": 22, 
                    "IpProtocol": "tcp", 
                    "UserIdGroupPairs": [
                        {
                            "UserId": "1234567890", 
                            "GroupId": "sg-abcdefab"
                        }
                    ], 
                    "Ipv6Ranges": []
                },
                {
                    "PrefixListIds": [], 
                    "FromPort": 443, 
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ], 
                    "ToPort": 443, 
                    "IpProtocol": "tcp", 
                    "UserIdGroupPairs": [], 
                    "Ipv6Ranges": []
                }
            ], 
            "GroupName": "SG 2", 
            "VpcId": "vpc-12345678", 
            "OwnerId": "1234567890", 
            "GroupId": "sg-22222222"
        } 
    ]
}
like image 923
Ryan Avatar asked Mar 08 '23 18:03

Ryan


2 Answers

You need to use advanced JMESPath conditions. See if the below cli command satisfies what you are asking.

aws ec2 describe-security-groups \
    --filters "Name=ip-permission.to-port,Values=22" \
    --query 'SecurityGroups[?IpPermissions[?ToPort==`22` && contains(IpRanges[].CidrIp, `0.0.0.0/0`)]].{GroupId: GroupId, GroupName: GroupName}' \
    --output json \
    --region us-east-1
like image 145
Madhukar Mohanraju Avatar answered Mar 10 '23 08:03

Madhukar Mohanraju


Here is a jq filter which will return only SecurityGroups with an IpPermission with FromPort=22 and an IpRange CidrIp of "0.0.0.0/0":

  .SecurityGroups[]
| select(.IpPermissions[] | .FromPort == 22 and .IpRanges[].CidrIp == "0.0.0.0/0")

Sample Run (assuming filter in filter.jq and data in data.json)

$ jq -M -f filter.jq data.json
{
  "Description": "SG 1",
  "IpPermissions": [
    {
      "PrefixListIds": [],
      "FromPort": 22,
      "IpRanges": [
        {
          "CidrIp": "0.0.0.0/0"
        }
      ],
      "ToPort": 22,
      "IpProtocol": "tcp",
      "UserIdGroupPairs": [],
      "Ipv6Ranges": []
    }
  ],
  "GroupName": "SG 1",
  "VpcId": "vpc-12345678",
  "OwnerId": "1234567890",
  "GroupId": "sg-11111111"
}

Try it online at jqplay.org

like image 37
jq170727 Avatar answered Mar 10 '23 08:03

jq170727