Skip to content
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

PLA-20589: Enable Azure SQL Server Auditing remediation job #34

Merged
merged 5 commits into from
Dec 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions remediation_worker/jobs/azure_sql_auditing_on_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Enable SQL Server Auditing

This job enables server blob auditing policy for the SQL Database Server by creating a Storage Account and assigning a Storage Blob Data Contributer role to the server.

### Applicable Rule

##### Rule ID:
5c8c268a7a550e1fb6560cb9

##### Rule Name:
SQL server auditing is not enabled

## Getting Started
### Prerequisites
The provided Azure service principal must have the following permissions:
`Microsoft.Sql/servers/read`
`Microsoft.Sql/servers/write`
`Microsoft.Sql/servers/auditingSettings/read`
`Microsoft.Sql/servers/auditingSettings/write`
`Microsoft.Storage/storageAccounts/write`
`Microsoft.Storage/storageAccounts/read`
`Microsoft.Authorization/roleAssignments/write`

A sample role with requisite permissions can be found [here](minimum_permissions.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_sql_auditing_on_server.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
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import json
import os
import sys
import logging
import random
import string
import uuid

from azure.identity import ClientSecretCredential
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.authorization import AuthorizationManagementClient
from azure.mgmt.sql import SqlManagementClient
from azure.mgmt.storage import StorageManagementClient
from azure.mgmt.sql.models import (
ServerBlobAuditingPolicy,
BlobAuditingPolicyState,
ResourceIdentity,
IdentityType,
Server,
)
from azure.mgmt.storage.models import (
StorageAccountCreateParameters,
NetworkRuleSet,
Sku,
SkuName,
SkuTier,
DefaultAction,
)
from azure.mgmt.authorization.models import (
RoleAssignmentCreateParameters,
PrincipalType,
)

logging.basicConfig(level=logging.INFO)


def get_random_string(length, prefix):
letters = string.ascii_lowercase
random_str = "".join(random.choice(letters) for i in range(length))
prefix = "".join(i for i in prefix if i != "-")
result_str = prefix + random_str
return result_str


def create_storage_account(
resource_group_name, name, region, client_storage,
):
create_params = StorageAccountCreateParameters(
location=region,
sku=Sku(name=SkuName.STANDARD_LRS, tier=SkuTier.STANDARD),
kind="StorageV2",
enable_https_traffic_only=True,
network_rule_set=NetworkRuleSet(default_action=DefaultAction.DENY),
)
poller = client_storage.storage_accounts.begin_create(
resource_group_name=resource_group_name,
account_name=name,
parameters=create_params,
)
return poller.result


def create_role_assignment(
stg_account_name, subscription_id, client_authorization, guid, Scope, principalId,
):
client_authorization.role_assignments.create(
scope=Scope,
role_assignment_name=guid,
parameters=RoleAssignmentCreateParameters(
role_definition_id=f"/subscriptions/{subscription_id}/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe",
principal_id=principalId,
principal_type=PrincipalType.service_principal,
),
)


class SqlServerEnableBlobAuditingPolicy(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" sql_server_name: {object_id}")
logging.info(f" subscription_id: {subscription_id}")
logging.info(f" region: {region}")
return {
"resource_group_name": resource_group_name,
"sql_server_name": object_id,
"subscription_id": subscription_id,
"region": region,
}

def remediate(
nandeshguru marked this conversation as resolved.
Show resolved Hide resolved
self,
client,
client_storage,
client_authorization,
resource_group_name,
sql_server_name,
region,
subscription_id,
):
"""Enable Server blob auditing policy for Azure SQL Server
:param client: Instance of the Azure SqlManagementClient.
:param resource_group_name: The name of the resource group to which the SQL Server belongs.
:param sql_server_name: The name of the SQL Server.
:type resource_group_name: str.
:type sql_server_name: str.
:returns: Integer signaling success or failure
:rtype: int
:raises: msrestazure.azure_exceptions.CloudError
"""

try:
server = client.servers.get(
resource_group_name=resource_group_name, server_name=sql_server_name,
)
if server.identity is None:
logging.info(
f"Assigning Azure Active Directory Identity to the SQL Database Server {sql_server_name}"
)
logging.info("executing client.servers.update")
logging.info(f" resource_group_name={resource_group_name}")
logging.info(f" server_name={sql_server_name}")
updated_server = client.servers.update(
resource_group_name=resource_group_name,
server_name=sql_server_name,
parameters=Server(
location=region,
identity=ResourceIdentity(type=IdentityType.system_assigned),
),
).result()
principalId = updated_server.identity.principal_id
else:
principalId = server.identity.principal_id

stg_account_name = get_random_string(6, sql_server_name)
logging.info(f"Creating a storage account with name {stg_account_name}")
logging.info("executing client_storage.storage_accounts.begin_create")
logging.info(f" resource_group_name={resource_group_name}")
logging.info(f" storage_account_name={stg_account_name}")

create_storage_account(
resource_group_name, stg_account_name, region, client_storage
)

guid = uuid.uuid4()
Scope = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.Storage/storageAccounts/{stg_account_name}"

logging.info(
f"Creating a Role Assignment for Storage Account {stg_account_name} and assigning Storage Blob Data Contributer Role to the SQL Database Server {sql_server_name}"
)
logging.info("executing client_authorization.role_assignments.create")
logging.info(f" scope={Scope}")
logging.info(f" role_assignment_name={guid}")

create_role_assignment(
stg_account_name,
subscription_id,
client_authorization,
guid,
Scope,
principalId,
)

logging.info("Enabling Server blob auditing policy for Azure SQL Server")
logging.info(
" executing client.server_blob_auditing_policies.create_or_update"
)
logging.info(f" resource_group_name={resource_group_name}")
logging.info(f" server_name={sql_server_name}")

client.server_blob_auditing_policies.create_or_update(
resource_group_name=resource_group_name,
server_name=sql_server_name,
parameters=ServerBlobAuditingPolicy(
state=BlobAuditingPolicyState.enabled,
storage_endpoint=f"https://{stg_account_name}.blob.core.windows.net/",
),
)
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 = ServicePrincipalCredentials(
client_id=os.environ.get("AZURE_CLIENT_ID"),
secret=os.environ.get("AZURE_CLIENT_SECRET"),
tenant=os.environ.get("AZURE_TENANT_ID"),
)
credentials1 = 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 = SqlManagementClient(
credentials, params["subscription_id"], base_url=None
)
client_storage = StorageManagementClient(
credentials1, params["subscription_id"]
)
client_authorization = AuthorizationManagementClient(
credentials, params["subscription_id"]
)
return self.remediate(
client,
client_storage,
client_authorization,
params["resource_group_name"],
params["sql_server_name"],
params["region"],
params["subscription_id"],
)


if __name__ == "__main__":
sys.exit(SqlServerEnableBlobAuditingPolicy().run(sys.argv))
Loading