Skip to content

Commit

Permalink
PLA-24844 - Remediation job to restrict default security group access (
Browse files Browse the repository at this point in the history
…#85)

* PLA-24844 - Remediation job to restrict default security group access

* PLA-24844 - Remediation job to restrict default security group access

* Updated the remediation job code
  • Loading branch information
kshrutik authored Jul 29, 2021
1 parent a0a2d04 commit b82785d
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Configure default Security Group to restrict all access

This job removes all the Ingress and Egress Rules of a default Security Group to restrict all access.

### Applicable Rule

##### Rule ID:
5c8c25f37a550e1fb6560bca

##### Rule Name:
EC2 VPC default security group should restrict all access

## Getting Started

### Prerequisites

The provided AWS credential must have access to `ec2:DescribeSecurityGroupRules`, `ec2:RevokeSecurityGroupIngress` and `ec2:RevokeSecurityGroupEgress`.

You may find the latest example policy file [here](minimum_policy.json)

### Running the script

You may run this script using following commands:
```shell script
pip install -r ../../requirements.txt
python3 aws_ec2_default_security_group_traffic.py
```

## Running the tests
You may run test using following command under vss-remediation-worker-job-code-python directory:
```shell script
python3 -m pytest test
```

## Deployment
1. Provision a Virtual Machine
Create an EC2 instance to use for the worker. The minimum required specifications are 128 MB memory and 1/2 Core CPU.
2. Setup Docker
Install Docker on the newly provisioned EC2 instance. You can refer to the [docs here](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html) for more information.
3. Deploy the worker image
SSH into the EC2 instance and run the command below to deploy the worker image:
```shell script
docker run --rm -it --name worker \
-e VSS_CLIENT_ID={ENTER CLIENT ID}
-e VSS_CLIENT_SECRET={ENTER CLIENT SECRET} \
vmware/vss-remediation-worker:latest-python
```


## Contributing
The Secure State team welcomes welcomes contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a Pull Request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq).
All contributions to this repository must be signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on as an open-source patch.

For more detailed information, refer to [CONTRIBUTING.md](../../../CONTRIBUTING.md).

## Versioning

We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/vmware-samples/secure-state-remediation-jobs/tags).

## Authors

* **VMware Secure State** - *Initial work*

See also the list of [contributors](https://github.com/vmware-samples/secure-state-remediation-jobs/contributors) who participated in this project.

## License

This project is licensed under the Apache License - see the [LICENSE](https://github.com/vmware-samples/secure-state-remediation-jobs/blob/master/LICENSE.txt) file for details
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright (c) 2020 VMware Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

import json
import logging
import sys

import boto3

logging.basicConfig(level=logging.INFO)


class DefaultSecurityGroupRemoveRules(object):
def parse(self, payload):
"""Parse payload received from Remediation Service.
:param payload: JSON string containing parameters received from the remediation service.
:type payload: str.
:returns: Dictionary of parsed parameters
:rtype: dict
:raises: Exception, JSONDecodeError
"""
remediation_entry = json.loads(payload)
notification_info = remediation_entry.get("notificationInfo", None)
finding_info = notification_info.get("FindingInfo", None)
security_group_id = finding_info.get("ObjectId", None)

object_chain = remediation_entry["notificationInfo"]["FindingInfo"][
"ObjectChain"
]
object_chain_dict = json.loads(object_chain)
cloud_account_id = object_chain_dict["cloudAccountId"]
region = finding_info.get("Region")

logging.info(f"security_group_id: {security_group_id}")
logging.info(f"region: {region}")
logging.info(f"cloud_account_id: {cloud_account_id}")

if security_group_id is None:
raise Exception(
"Missing parameters for 'payload.notificationInfo.ObjectId'."
)

return {
"security_group_id": security_group_id,
"region": region,
"cloud_account_id": cloud_account_id,
}

def remediate(self, client, security_group_id, region, cloud_account_id):
"""Restrict all access for EC2 VPC default security group.
:param client: Instance of the AWS boto3 client.
:param security_group_id: The ID of the security group.
:param region: Region in which the security group exists.
:param cloud_account_id: AWS Account no.
:type security_group_id: str.
:type region: str.
:type cloud_account_id: str.
:returns: Integer signaling success or failure
:rtype: int
:raises: botocore.exceptions.ClientError
"""
try:
logging.info(" executing client.describe_security_group_rules")
logging.info(f" group-id: {security_group_id}")
security_group_rules = client.describe_security_group_rules(
Filters=[{"Name": "group-id", "Values": [security_group_id]},],
MaxResults=1000,
)
for rule in security_group_rules["SecurityGroupRules"]:
if rule["IsEgress"]:
logging.info(" executing client.revoke_security_group_egress")
logging.info(f" GroupId: {security_group_id}")
logging.info(f" SecurityGroupRuleIds: {rule['SecurityGroupRuleId']}")
client.revoke_security_group_egress(
GroupId=security_group_id,
SecurityGroupRuleIds=[rule["SecurityGroupRuleId"]]
)
else:
logging.info(" executing client.revoke_security_group_ingress")
logging.info(f" GroupId: {security_group_id}")
logging.info(f" SecurityGroupRuleIds: {rule['SecurityGroupRuleId']}")
client.revoke_security_group_ingress(
GroupId=security_group_id,
SecurityGroupRuleIds=[rule["SecurityGroupRuleId"]]
)
logging.info("successfully executed remediation")
except Exception as e:
logging.error(f"{str(e)}")

return 0

def run(self, args):
"""Run the remediation job.
:param args: List of arguments provided to the job.
:type args: list.
:returns: int
"""
params = self.parse(args[1])
client = boto3.client("ec2", params["region"])
logging.info("acquired ec2 client and parsed params - starting remediation")
rc = self.remediate(client=client, **params)
return rc


if __name__ == "__main__":
logging.info("aws_ec2_default_security_group_traffic.py called - running now")
obj = DefaultSecurityGroupRemoveRules()
obj.run(sys.argv)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
attrs==20.1.0 \
--hash=sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a \
--hash=sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff
docutils==0.15.2 \
--hash=sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0 \
--hash=sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827 \
--hash=sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99
iniconfig==1.1.1 \
--hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \
--hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32
jmespath==0.10.0 \
--hash=sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9 \
--hash=sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f
more-itertools==8.4.0 \
--hash=sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5 \
--hash=sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2
packaging==20.4 \
--hash=sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8 \
--hash=sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181
pyparsing==2.4.7 \
--hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \
--hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b
python-dateutil==2.8.1 \
--hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \
--hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a
py==1.9.0 \
--hash=sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2 \
--hash=sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342
pluggy==0.13.1 \
--hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \
--hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d
s3transfer==0.3.4 \
--hash=sha256:1e28620e5b444652ed752cf87c7e0cb15b0e578972568c6609f0f18212f259ed \
--hash=sha256:7fdddb4f22275cf1d32129e21f056337fd2a80b6ccef1664528145b72c49e6d2
six==1.15.0 \
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced
toml==0.10.1 \
--hash=sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f \
--hash=sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88
urllib3==1.26.3 \
--hash=sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80 \
--hash=sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RemoveDefaultSecurityGroupRules",
"Effect": "Allow",
"Action": [
"ec2:DescribeSecurityGroupRules",
"ec2:RevokeSecurityGroupIngress",
"ec2:RevokeSecurityGroupEgress"
],
"Resource": "*"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-r requirements.txt
-c constraints.txt

mock==4.0.2 \
--hash=sha256:3f9b2c0196c60d21838f307f5825a7b86b678cedc58ab9e50a8988187b4d81e0 \
--hash=sha256:dd33eb70232b6118298d516bbcecd26704689c386594f0f3c4f13867b2c56f72
pytest==6.0.1 \
--hash=sha256:85228d75db9f45e06e57ef9bf4429267f81ac7c0d742cc9ed63d09886a9fe6f4 \
--hash=sha256:8b6007800c53fdacd5a5c192203f4e531eb2a1540ad9c752e052ec0f7143dbad
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
boto3==1.18.4 \
--hash=sha256:649ed1ca205f5ee0b0328d54580780aebc1a7a05681a24f6ee05253007ca48d8 \
--hash=sha256:7079b40bd6621c54a0385a8fc11240cff4318a4d487292653e393e18254f5d94
botocore==1.19.60 \
--hash=sha256:423a1a9502bd7bc5db8c6e64f9374f64d8ac18e6b870278a9ff65f59d268cd58 \
--hash=sha256:80dd615a34c7e2c73606070a9358f7b5c1cb0c9989348306c1c9ddff45bb6ebe
62 changes: 62 additions & 0 deletions test/unit/test_aws_ec2_default_security_group_traffic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright (c) 2020 VMware Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest
from mock import Mock

from remediation_worker.jobs.aws_ec2_default_security_group_traffic.aws_ec2_default_security_group_traffic import (
DefaultSecurityGroupRemoveRules,
)


@pytest.fixture
def valid_payload():
return """
{
"notificationInfo": {
"RuleId": "5c6cc5e103dcc90f363146cd",
"Service": "EC2",
"FindingInfo": {
"FindingId": "d0431afd-b82e-4021-8aa6-ba3cf5c60ef7",
"ObjectId": "security_group_id",
"ObjectChain": "{\\"cloudAccountId\\":\\"cloud_account_id\\",\\"entityId\\":\\"AWS.EC2.159636093902.us-west-2.Trail.test-remediation\\",\\"entityName\\":\\"remediation-cloudtrail\\",\\"entityType\\":\\"AWS.CloudTrail.Trail\\",\\"lastUpdateTime\\":\\"2020-09-09T00:36:35.000Z\\",\\"partitionKey\\":\\"153894897389\\",\\"provider\\":\\"AWS\\",\\"region\\":\\"us-west-2\\",\\"service\\":\\"EC2\\", \\"properties\\":[{\\"name\\":\\"S3BucketName\\",\\"stringV\\":\\"remediation-cloudtrail\\",\\"type\\":\\"string\\"}]}",
"Region": "region"
}
}
}
"""


class TestCloudtrailS3PublicAccess(object):
def test_parse_payload(self, valid_payload):
params = DefaultSecurityGroupRemoveRules().parse(valid_payload)
assert params["security_group_id"] == "security_group_id"
assert params["cloud_account_id"] == "cloud_account_id"
assert params["region"] == "region"

def test_remediate_success(self):
client = Mock()
action = DefaultSecurityGroupRemoveRules()

assert (
action.remediate(client, "security_group_id", "region", "cloud_account_id")
== 0
)
assert client.describe_security_groups.call_count == 1

def test_remediate_with_exception(self):
client = Mock()
action = DefaultSecurityGroupRemoveRules()
with pytest.raises(Exception):
assert action.remediate(client, "cloud_account_id")

0 comments on commit b82785d

Please sign in to comment.