Repository: cloud-custodian/cloud-custodian

Issue:
security group egress filter not behaving as expected
I am working in an account/region where there are 5 security groups in existence with the following egress rule  applied on all of them:
Type          Protocol   Port Range   Destination Description
All traffic   All             All                  0.0.0.0/0
I have tried a number of egress filter configurations against this account/region and found the following:
1. 
- type: egress
  Cidr:
     value: "0.0.0.0/0"

matches all the security groups. (as expected)

2.
- type: egress
  IpProtocol: -1 

no matches.

3.
- type: egress
  FromPort: 0
  ToPort: 65535

no matches.

Any combination of 3 or 2 with 1 matches nothing.

This is not the behaviour I would expect. Am I misunderstanding something?




Discussion:
its helpful to have a describe-security-group output to understand what the rules representation for the things your trying to match on.

1. Fwiw, Its worth noting a recent bug fix for this that allows Cidr: "0.0.0.0/0" (ie without the value) .. its also worth calling out you can do more interesting Cidr comparisons with `value_type: cidr` ie check if an sg allows access to a given ip or subnet.

2. This looks like a bug, the schema for the filter accepts `-1` as integer (in addition to the string protocols), but the sg property is a string for `"-1"`

3. FromPort / ToPort is an attribute match on a permission, not a range match.  We use Ports to do range matches against arrays of individual ports, and OnlyPorts to negative assertions against range.. ie OnlyPorts: [80, 443] matches any group that allows anything but those ports.  

Its worth considering if we should change FromPort/ToPort to also support full value_type syntax to allow for range matching with them.


Thanks for the pointers as regards usage in 1 and 3.  
With respect to 3, it seems that 'Ports' is accepted as a valid property for ingress filters, but not for egress at the moment.



Resolving patch:
diff --git a/c7n/resources/vpc.py b/c7n/resources/vpc.py
--- a/c7n/resources/vpc.py
+++ b/c7n/resources/vpc.py
@@ -1139,10 +1139,17 @@ def __call__(self, resource):
 
 
 SGPermissionSchema = {
-    'IpProtocol': {'enum': [-1, 'tcp', 'udp', 'icmp', 'icmpv6']},
+    'match-operator': {'type': 'string', 'enum': ['or', 'and']},
+    'Ports': {'type': 'array', 'items': {'type': 'integer'}},
+    'SelfReference': {'type': 'boolean'},
     'OnlyPorts': {'type': 'array', 'items': {'type': 'integer'}},
-    'FromPort': {'type': 'integer'},
-    'ToPort': {'type': 'integer'},
+    'IpProtocol': {'enum': ["-1", -1, 'tcp', 'udp', 'icmp', 'icmpv6']},
+    'FromPort': {'oneOf': [
+        {'$ref': '#/definitions/filters/value'},
+        {'type': 'integer'}]},
+    'ToPort': {'oneOf': [
+        {'$ref': '#/definitions/filters/value'},
+        {'type': 'integer'}]},
     'UserIdGroupPairs': {},
     'IpRanges': {},
     'PrefixListIds': {},
@@ -1159,12 +1166,7 @@ class IPPermission(SGPermission):
     schema = {
         'type': 'object',
         'additionalProperties': False,
-        'properties': {
-            'type': {'enum': ['ingress']},
-            'match-operator': {'type': 'string', 'enum': ['or', 'and']},
-            'Ports': {'type': 'array', 'items': {'type': 'integer'}},
-            'SelfReference': {'type': 'boolean'}
-        },
+        'properties': {'type': {'enum': ['ingress']}},
         'required': ['type']}
     schema['properties'].update(SGPermissionSchema)
 
@@ -1176,11 +1178,7 @@ class IPPermissionEgress(SGPermission):
     schema = {
         'type': 'object',
         'additionalProperties': False,
-        'properties': {
-            'type': {'enum': ['egress']},
-            'match-operator': {'type': 'string', 'enum': ['or', 'and']},
-            'SelfReference': {'type': 'boolean'}
-        },
+        'properties': {'type': {'enum': ['egress']}},
         'required': ['type']}
     schema['properties'].update(SGPermissionSchema)
 


Test patch: