-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Azure security center enable ddos protection #27
Changes from 5 commits
dc0a5ac
46b489e
c5e1f32
665aa6b
99a2fe3
0dcfd93
2e546d4
a320a19
b8837aa
e4e5ea5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Enable DDos protection for Virtual Network | ||
|
||
This job enables DDos protection for a virtual network by listing all the available DDos protection plans and assigning any one to the virtual network. | ||
|
||
### Applicable Rule | ||
|
||
##### Rule ID: | ||
5c8c26997a550e1fb6560cd9 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check the Rule ID. I think you wanted to use this one |
||
|
||
##### Rule Name: | ||
DDos protection is enabled for virtual network | ||
|
||
## Getting Started | ||
### Prerequisites | ||
The provided Azure service principal must have the following permissions: | ||
`Microsoft.Network/virtualNetworks/read` | ||
`Microsoft.Network/virtualNetworks/write` | ||
`Microsoft.Network/ddosProtectionPlans/read` | ||
`Microsoft.Network/ddosProtectionPlans/join/action` | ||
|
||
A sample role with requisite permissions can be found [here](minimum_policy.json) | ||
|
||
More information about already builtin roles and permissions can be found [here](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles) | ||
|
||
### Running the script | ||
You may run this script using following commands: | ||
|
||
```shell script | ||
pip install -r requirements.txt | ||
python3 azure_security_center_enable_ddos_protection.py | ||
``` | ||
## Running the tests | ||
You may run test using following command under vss-remediation-worker-job-code-python directory: | ||
|
||
```shell script | ||
pip install -r requirements-dev.txt | ||
python3 -m pytest test | ||
``` | ||
## Deployment | ||
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. | ||
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. | ||
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 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 for versioning. For the versions available, see the tags on this repository. | ||
|
||
## Authors | ||
* **VMware Secure State** - *Initial work* | ||
See also the list of [contributors](https://github.com/vmware-samples/secure-state-remediation-jobs/graphs/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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import json | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the vmware copyright statement on top of the file |
||
import os | ||
import sys | ||
import logging | ||
|
||
from azure.mgmt.network import NetworkManagementClient | ||
from azure.mgmt.network.models import DdosProtectionPlanListResult, SubResource | ||
from azure.identity import ClientSecretCredential | ||
from azure.core.paging import ItemPaged | ||
from typing import List | ||
|
||
logging.basicConfig(level=logging.INFO) | ||
|
||
def logcall(f, *args, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this function as this is not being used in the file. |
||
logging.info( | ||
"%s(%s)", | ||
f.__name__, | ||
", ".join(list(args) + [f"{k}={repr(v)}" for k, v in kwargs.items()]), | ||
) | ||
logging.info(f(*args, **kwargs)) | ||
|
||
class VirtualNetworkEnableDdosProtection(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: KeyError, JSONDecodeError | ||
""" | ||
remediation_entry = json.loads(payload) | ||
|
||
object_id = remediation_entry["notificationInfo"]["FindingInfo"]["ObjectId"] | ||
|
||
region = remediation_entry["notificationInfo"]["FindingInfo"]["Region"] | ||
|
||
object_chain = remediation_entry["notificationInfo"]["FindingInfo"]["ObjectChain"] | ||
|
||
object_chain_dict = json.loads(object_chain) | ||
subscription_id = object_chain_dict["cloudAccountId"] | ||
|
||
properties = object_chain_dict["properties"] | ||
resource_group_name = "" | ||
for property in properties: | ||
if property["name"] == "ResourceGroup" and property["type"] == "string": | ||
resource_group_name = property["stringV"] | ||
break | ||
|
||
logging.info("parsed params") | ||
logging.info(f" resource_group_name: {resource_group_name}") | ||
logging.info(f" virtual_network_name: {object_id}") | ||
logging.info(f" subscription_id: {subscription_id}") | ||
logging.info(f" region: {region}") | ||
return { | ||
"resource_group_name": resource_group_name, | ||
"virtual_network_name": object_id, | ||
"subscription_id": subscription_id, | ||
"region": region, | ||
} | ||
|
||
def remediate(self, client, resource_group_name, virtual_network_name, subscription_id): | ||
"""Enable DDos protection for a Virtual Network | ||
:param client: Instance of the Azure NetworkManagementClient. | ||
:param resource_group_name: The name of the resource group to which the virtual network belongs | ||
:param virtual_network_name: The name of the Virtual Network. You must specify the vnet name in the request. | ||
:param subscription_id: The Subscription ID of the user. | ||
:type resource_group_name: str. | ||
:type virtual_network_name: str. | ||
:returns: Integer signaling success or failure | ||
:rtype: int | ||
:raises: msrestazure.azure_exceptions.CloudError | ||
""" | ||
|
||
ddos_plans_paged: ItemPaged[DdosProtectionPlanListResult] = client.ddos_protection_plans.list() | ||
ddos_plans_list: List[dict] = list(ddos_plans_paged) | ||
number_of_ddos: int = len(ddos_plans_list) | ||
print(number_of_ddos) | ||
|
||
if number_of_ddos > 0: | ||
resource_id = ddos_plans_list[0].id | ||
logging.info(f" Resource ID of Azure DDos Protection Plan={resource_id}") | ||
else: | ||
logging.error(f" Azure cloud user with subscription ID: {subscription_id} has no active Azure DDos protection plan available") | ||
return 1 | ||
|
||
virtual_network = client.virtual_networks.get( | ||
resource_group_name=resource_group_name, | ||
virtual_network_name=virtual_network_name, | ||
) | ||
|
||
virtual_network.enable_ddos_protection = True | ||
|
||
updated_SubResource = SubResource(id = resource_id) | ||
|
||
virtual_network.ddos_protection_plan = updated_SubResource | ||
|
||
logging.info("Enabling DDos protection for Virtual Network") | ||
try: | ||
logging.info(" executing client.virtual_networks.begin_create_or_update") | ||
logging.info(f" resource_group_name={resource_group_name}") | ||
logging.info(f" virtual_network_name={virtual_network_name}") | ||
|
||
client.virtual_networks.begin_create_or_update( | ||
resource_group_name=resource_group_name, | ||
virtual_network_name=virtual_network_name, | ||
parameters=virtual_network, | ||
) | ||
except Exception as e: | ||
logging.error(f"{str(e)}") | ||
raise | ||
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]) | ||
|
||
credentials = ClientSecretCredential( | ||
client_id=os.environ.get("AZURE_CLIENT_ID"), | ||
client_secret=os.environ.get("AZURE_CLIENT_SECRET"), | ||
tenant_id=os.environ.get("AZURE_TENANT_ID"), | ||
) | ||
|
||
client = NetworkManagementClient(credentials, params["subscription_id"]) | ||
return self.remediate( | ||
client, | ||
params["resource_group_name"], | ||
params["virtual_network_name"], | ||
) | ||
|
||
if __name__ == "__main__": | ||
sys.exit(VirtualNetworkEnableDdosProtection().run(sys.argv)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
adal==1.2.5 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
azure-common==1.1.25 | ||
azure-core==1.8.2 | ||
azure-identity==1.4.1 | ||
azure-mgmt-core==1.2.1 | ||
azure-mgmt-network==16.0.0 | ||
certifi==2020.6.20 | ||
cffi==1.14.3 | ||
chardet==3.0.4 | ||
cryptography==3.2.1 | ||
idna==2.10 | ||
isodate==0.6.0 | ||
msal==1.5.1 | ||
msal-extensions==0.2.2 | ||
msrest==0.6.19 | ||
msrestazure==0.6.4 | ||
oauthlib==3.1.0 | ||
portalocker==1.7.1 | ||
pycparser==2.20 | ||
PyJWT==1.7.1 | ||
python-dateutil==2.8.1 | ||
requests==2.24.0 | ||
requests-oauthlib==1.3.0 | ||
six==1.12.0 | ||
urllib3==1.25.11 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"properties": { | ||
"roleName": "remediate_enable_ddos_protection", | ||
"description": "This role has required permissions to make changes to the virtual network", | ||
"assignableScopes": [ | ||
], | ||
"permissions": [ | ||
{ | ||
"actions": [ | ||
"Microsoft.Network/virtualNetworks/read", | ||
"Microsoft.Network/virtualNetworks/write", | ||
"Microsoft.Network/ddosProtectionPlans/read", | ||
"Microsoft.Network/ddosProtectionPlans/join/action" | ||
], | ||
"notActions": [], | ||
"dataActions": [], | ||
"notDataActions": [] | ||
} | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
-r requirements.txt | ||
-c constraints.txt | ||
|
||
importlib-metadata==2.0.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pin the version with the hash |
||
iniconfig==1.1.1 | ||
mock==4.0.2 | ||
packaging==20.4 | ||
pluggy==0.13.1 | ||
py==1.9.0 | ||
pyparsing==2.4.7 | ||
pytest==6.1.2 | ||
toml==0.10.1 | ||
zipp==3.4.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
azure-identity==1.4.1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the hashes for the package versions. |
||
azure-mgmt-network==16.0.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# 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 typing import List | ||
from azure.mgmt.network.models import DdosProtectionPlanListResult, SubResource | ||
from remediation_worker.jobs.azure_security_center_enable_ddos_protection.azure_security_center_enable_ddos_protection import ( | ||
VirtualNetworkEnableDdosProtection, | ||
) | ||
|
||
@pytest.fixture | ||
def valid_payload(): | ||
return """ | ||
{ | ||
"notificationInfo": { | ||
"RuleId": "5c6cc5e103dcc90f363146cd", | ||
"Service": "Network", | ||
"FindingInfo": { | ||
"FindingId": "d0431afd-b82e-4021-8aa6-ba3cf5c60ef7", | ||
"ObjectId": "vnet_name", | ||
"ObjectChain": "{\\"cloudAccountId\\":\\"subscription_id\\",\\"entityId\\":\\"Azure.Network.d687b1a3-9b78-43b1-a17b-7de297fd1fce.resource_group_name.Network.virtual_network_name\\",\\"entityName\\":\\"virtual_network_name\\",\\"entityType\\":\\"Azure.Network.virtualnetwork\\",\\"lastUpdateTime\\":\\"2020-09-09T00:36:35.000Z\\",\\"partitionKey\\":\\"d687b1a3-9b78-43b1-a17b-7de297fd1fce\\",\\"provider\\":\\"Azure\\",\\"region\\":\\"eastus\\",\\"service\\":\\"Network\\", \\"properties\\":[{\\"name\\":\\"ResourceGroup\\",\\"stringV\\":\\"resource_group_name\\",\\"type\\":\\"string\\"}]}", | ||
"Region": "region" | ||
} | ||
} | ||
} | ||
""" | ||
|
||
class TestBlobRemovePublicAccess(object): | ||
def test_parse_payload(self, valid_payload): | ||
params = VirtualNetworkEnableDdosProtection().parse(valid_payload) | ||
assert params["virtual_network_name"] == "vnet_name" | ||
assert params["resource_group_name"] == "resource_group_name" | ||
assert params["subscription_id"] == "subscription_id" | ||
assert params["region"] == "region" | ||
|
||
def test_remediate_success(self): | ||
client = Mock() | ||
action = VirtualNetworkEnableDdosProtection() | ||
DdosProtectionPlanListResult = Mock() | ||
ddos_plans_list = [] | ||
ddos_plans_list.append(DdosProtectionPlanListResult) | ||
client.ddos_protection_plans.list.return_value = ddos_plans_list | ||
assert ( | ||
action.remediate(client, "resource_group", "virtual_network_name", "subscription_id") | ||
== 0 | ||
) | ||
assert client.virtual_networks.begin_create_or_update.call_count == 1 | ||
|
||
call_args = client.virtual_networks.begin_create_or_update.call_args | ||
updated_vnet = call_args[1]["parameters"] | ||
assert updated_vnet.enable_ddos_protection == True | ||
|
||
def test_remediate_with_exception(self): | ||
client = Mock() | ||
client.virtual_networks.begin_create_or_update.side_effect = Exception | ||
action = VirtualNetworkEnableDdosProtection() | ||
with pytest.raises(Exception): | ||
assert action.remediate(client, "security_group_id", "resource_group") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change DDos to DDoS