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

Add NIST policy assignment off by default #350

Merged
merged 5 commits into from
Aug 25, 2021
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
1 change: 1 addition & 0 deletions src/docs/command-line-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ deploy.sh: create all the configuration and deploy Terraform resources with mini
--write-output -w [OPTIONAL] Tier 3 Deployment requires Terraform output, use this flag to write terraform output
--no-bastion [OPTIONAL] when present, do not create a Bastion Host and Jumpbox VM
--no-sentinel [OPTIONAL] when present, do not create an Azure Sentinel solution
--policy [OPTIONAL] when present, create Policy Assignments for built-in NIST initiative
--no-service-principal [OPTIONAL] when present, do not create an Azure Service Principal, instead use the credentials in the environment variables '$ARM_CLIENT_ID' and '$ARM_CLIENT_SECRET'
--help -h Print this message
```
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions src/docs/policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Mission Landing Zone Regulatory Compliance - NIST Policies
shawngib marked this conversation as resolved.
Show resolved Hide resolved

As part of Mission Landing Zone (MLZ) it's been a goal to ensure deployments have the tools and resources available that allow it to be compliant with most regulations across industries. This does not mean that workloads are compliant, but it does mean that the technologies in use can be compliant. This is caused by not only the varying number of compliance bodies involved and and the regulations they mandate but also caused by the decisions required by how and what controls are followed.

For the purposes of this documentation we created an example method in which the MLZ deployment can be audited for current National Institute of Standards and Technology (NIST) controls and requirements using [Azure Policies built in initiative](https://docs.microsoft.com/en-us/azure/governance/policy/samples/nist-sp-800-53-r4) for NIST 800-53. _Note: this is focused on NIST controls that have built in policies in Azure clouds._

By adding the `--policy` switch to the deployment command the script will multiple assignments to the deployment final architecture. The result is for each Tier (Hub, Tier0, Tier1, and Tier2) there will be an additional policy/initiative assigned scoped to those recourse groups. This will not impact other policies/initiatives assigned that are deployed at different scopes either prior to deploying MLZ or post deployment.

![](images/20210419_missionlz_as_of_Aug2021_Policy.png)

## Known Issues

Currently there are a set of known issues with this approach. The first and somewhat important detail is that these policies are based on built in policies available in the different Azure environments. There are some variances currently between clouds. This will always happen when separate isolated environments have different deployment cycles but also can be based on preview testing versus generally available components in one cloud environment versus another.

A secondary issue comes from the method in which the assignment is deployed. This results in 'out of band' requirements for customers. In particular, the current built-in NIST initiative has a couple policies attached that modify and/or deploy if a resource doesn't exist. Example, VM extensions for guest policy configuration would be deployed if they don't exist in the VM. These types of policies require a managed identity be created that the Policy engine can use to take these actions. This managed identity must have contributor access to the resources but deploying as a contributor and not owner limits the ability. The terraform MLZ deployment as it is today using service principles with contributor rights cannot make this role assignment but the managed identity is created. This is by design for security purposes.

The final note is that these are audits based on NIST controls and recommendations that will require out of band work. As an example, storage account redundancy and encryption will require a decision process on what MLZ is using as temporary storage for logs versus requirements for the workloads. For example, encryption can be accomplished with multiple key models, which one is required for what category of data?

## Deploying

Deploying policy assignments for NIST along with a standard deployment of MLZ is as simple as adding the –policy switch to the deployment script command. This will add a separate assignment of the built in NIST initiative per resource group in the deployment, excluding the resource groups used as deployment artifacts like state and config.
shawngib marked this conversation as resolved.
Show resolved Hide resolved

Example:
`src/scripts/deploy.sh -s <subscriptionID> -l usgovvirginia --tf-environment usgovernment –policy`

After the resources are deployed, you will need to go into go into each assignment and retrieve the managed identity and modify its role access to contributor scoped to the associated resource group. This is due to the initiative including modify and deploy policies that act on resources, like deploying the require policy guest configuration extensions to VMs.

Modifying

This model uses an additional custom terraform module called 'policy-assignments'. This can be modified for adding additional initiatives if desired. The module deployments retrieve their parameter values from a local json file stored in the module directory named 'nist-parameter-values' and named after the cloud environment they are deploying to, public or usgovernment.

Example parameters file snippet:
```
{
"listOfMembersToExcludeFromWindowsVMAdministratorsGroup":
{
"value": "admin"
},
"listOfMembersToIncludeInWindowsVMAdministratorsGroup":
{
"value": "azureuser"
},
"logAnalyticsWorkspaceIdforVMReporting":
{
"value": ${jsonencode(laws_instance_id)}
},
"IncludeArcMachines":
{
"value": "true"
}
```

In the above example the 'logAnalyticsWorkspaceIdforVMReporting' is retrieved from the running terraform deployment variables. This could be modified to use a central logging workspace if desired.

What's Next

While this is only a start, the NIST controls included in the built-in initiatives are a good start to understanding requirements on top of MLZ for compliance. In the near future the hopes are for this to be expanded with additional built-in initiatives as well as offering an option to create your own initiative and custom policies. Potential additions will be server baselines, IL compliances, and custom policies.

Also scripts to assist in these out-of-band processes will be added.
8 changes: 7 additions & 1 deletion src/scripts/config/create_mlz_config_resources.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,19 @@ else
# Assign Contributor Role to Subscriptions
for sub in "${subs[@]}"
do
echo "INFO: setting Contributor role assignment for ${sp_client_id} on subscription ${sub}..."
echo "INFO: setting Contributor and Policy Contributor role assignments for ${sp_client_id} on subscription ${sub}..."
az role assignment create \
--role Contributor \
--assignee-object-id "${sp_object_id}" \
--scope "/subscriptions/${sub}" \
--assignee-principal-type ServicePrincipal \
--output none
az role assignment create \
--role 'Resource Policy Contributor' \
--assignee-object-id "${sp_object_id}" \
--scope "/subscriptions/${sub}" \
--assignee-principal-type ServicePrincipal \
--output none
done
else
error_log "ERROR: A service principal named ${mlz_sp_name} already exists. This must be a unique service principal for your use only. Try again with a new mlz-env-name. Exiting script."
Expand Down
6 changes: 5 additions & 1 deletion src/scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ show_help() {
print_formatted "--no-bastion" "" "[OPTIONAL] when present, do not create a Bastion Host and Jumpbox VM"
print_formatted "--no-sentinel" "" "[OPTIONAL] when present, do not create an Azure Sentinel solution"
print_formatted "--no-service-principal" "" "[OPTIONAL] when present, do not create an Azure Service Principal, instead use the credentials in the environment variables '\$ARM_CLIENT_ID' and '\$ARM_CLIENT_SECRET'"
print_formatted "--policy" "" "[OPTIONAL] when present, create Policy Assignments for built-in NIST initiative"
print_formatted "--help" "-h" "Print this message"
}

Expand Down Expand Up @@ -155,7 +156,7 @@ create_mlz_resources() {

create_terraform_variables() {
echo "INFO: creating terraform variables at ${tfvars_file_path}..."
"${this_script_path}/terraform/create_tfvars_from_config.sh" "${tfvars_file_path}" "${mlz_config_file_path}" "${create_bastion_jumpbox}" "${create_sentinel}"
"${this_script_path}/terraform/create_tfvars_from_config.sh" "${tfvars_file_path}" "${mlz_config_file_path}" "${create_bastion_jumpbox}" "${create_sentinel}" "${create_assignment}"
}

apply_terraform() {
Expand Down Expand Up @@ -194,6 +195,7 @@ default_env_name="mlz${timestamp}"
create_bastion_jumpbox=true
create_sentinel=true
create_service_principal=true
create_assignment=false

mlz_config_subid="${default_config_subid}"
mlz_config_location="${default_config_location}"
Expand Down Expand Up @@ -239,6 +241,8 @@ while [ $# -gt 0 ] ; do
create_sentinel=false ;;
--no-service-principal)
create_service_principal=false ;;
--policy)
create_assignment=true ;;
-h | --help)
show_help
exit 0 ;;
Expand Down
4 changes: 3 additions & 1 deletion src/scripts/terraform/create_tfvars_from_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ error_log() {

usage() {
echo "create_tfvars_from_config.sh: generate a terraform tfvars file given an MLZ config and a desired tfvars file name"
echo "create_tfvars_from_config.sh: <destination file path> <mlz config file path> <create bastion host> <create sentinel>"
echo "create_tfvars_from_config.sh: <destination file path> <mlz config file path> <create bastion host> <create sentinel> <create_assignment>"
show_help
}

Expand All @@ -30,6 +30,7 @@ file_to_create=$1
mlz_config=$2
create_bastion_jumpbox=${3:-true}
create_sentinel=${4:-true}
create_assignment=${5:-false}

# source config
. "${mlz_config}"
Expand All @@ -55,6 +56,7 @@ append_kvp "mlz_cloud" "${mlz_cloudname}"
append_kvp "mlz_tenantid" "${mlz_tenantid}"
append_kvp "mlz_location" "${mlz_config_location}"
append_kvp "mlz_metadatahost" "${mlz_metadatahost}"
append_kvp "create_assignment" "${create_assignment}"

append_kvp "hub_subid" "${mlz_saca_subid}"
append_kvp "hub_rgname" "rg-saca-${mlz_env_name}"
Expand Down
52 changes: 52 additions & 0 deletions src/terraform/mlz/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -513,3 +513,55 @@ module "jumpbox" {
DeploymentName = var.deploymentname
}
}

#####################################
### STAGE 4: Compliance example ###
#####################################

module "hub-policy-assignment" {
count = var.create_assignment ? 1 : 0

providers = { azurerm = azurerm.hub }
source = "../modules/policy-assignments"
depends_on = [azurerm_resource_group.hub, azurerm_log_analytics_workspace.laws]
resource_group_name = azurerm_resource_group.hub.name
laws_instance_id = azurerm_log_analytics_workspace.laws.workspace_id
environment = var.tf_environment # Example "usgovernment"
log_analytics_workspace_resource_id = azurerm_log_analytics_workspace.laws.id
}

module "tier0-policy-assignment" {
count = var.create_assignment ? 1 : 0

providers = { azurerm = azurerm.tier0 }
source = "../modules/policy-assignments"
depends_on = [azurerm_resource_group.tier0, azurerm_log_analytics_workspace.laws]
resource_group_name = azurerm_resource_group.tier0.name
laws_instance_id = azurerm_log_analytics_workspace.laws.workspace_id
environment = var.tf_environment # Example "usgovernment"
log_analytics_workspace_resource_id = azurerm_log_analytics_workspace.laws.id
}

module "tier1-policy-assignment" {
count = var.create_assignment ? 1 : 0

providers = { azurerm = azurerm.tier1 }
source = "../modules/policy-assignments"
depends_on = [azurerm_resource_group.tier1, azurerm_log_analytics_workspace.laws]
resource_group_name = azurerm_resource_group.tier1.name
laws_instance_id = azurerm_log_analytics_workspace.laws.workspace_id
environment = var.tf_environment # Example "usgovernment"
log_analytics_workspace_resource_id = azurerm_log_analytics_workspace.laws.id
}

module "tier2-policy-assignment" {
count = var.create_assignment ? 1 : 0

providers = { azurerm = azurerm.tier2 }
source = "../modules/policy-assignments"
depends_on = [azurerm_resource_group.tier2, azurerm_log_analytics_workspace.laws]
resource_group_name = azurerm_resource_group.tier2.name
laws_instance_id = azurerm_log_analytics_workspace.laws.workspace_id
environment = var.tf_environment # Example "usgovernment"
log_analytics_workspace_resource_id = azurerm_log_analytics_workspace.laws.id
}
6 changes: 6 additions & 0 deletions src/terraform/mlz/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ variable "mlz_objectid" {
sensitive = true
}

variable "create_assignment" {
description = "Create an Azure Policy assignement for defaul NIST initiative."
type = bool
default = false
}

#################################
# Hub Configuration
#################################
Expand Down
19 changes: 19 additions & 0 deletions src/terraform/modules/policy-assignments/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) Microsoft Corporation.
shawngib marked this conversation as resolved.
Show resolved Hide resolved
# Licensed under the MIT License.
data "azurerm_resource_group" "rg" {
name = var.resource_group_name
}

resource "azurerm_resource_group_policy_assignment" "policy_assign" {
name = "NIST Assignment - ${data.azurerm_resource_group.rg.name}"
resource_group_id = data.azurerm_resource_group.rg.id
policy_definition_id = var.policy_id
location = data.azurerm_resource_group.rg.location
identity {
type = "SystemAssigned"
}
# Define parameters for value template file directed to environment
parameters = templatefile("${path.module}/nist-parameter-values/${var.environment}.json.tmpl", {
laws_instance_id = var.laws_instance_id
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"listOfMembersToExcludeFromWindowsVMAdministratorsGroup":
{
"value": "admin"
},
"listOfMembersToIncludeInWindowsVMAdministratorsGroup":
{
"value": "azureuser"
},
"logAnalyticsWorkspaceIdforVMReporting":
{
"value": ${jsonencode(laws_instance_id)}
},
"IncludeArcMachines":
{
"value": "true"
},
"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112":
{
"value": "1.2"
},
"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40":
{
"value": "Compliant"
},
"requiredRetentionDays":
{
"value": "365"
},
"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6":
{
"value": "NetworkWatcherRG"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"listOfMembersToExcludeFromWindowsVMAdministratorsGroup":
{
"value": "admin"
},
"listOfMembersToIncludeInWindowsVMAdministratorsGroup":
{
"value": "azureuser"
},
"logAnalyticsWorkspaceIdforVMReporting":
{
"value": ${jsonencode(laws_instance_id)}
},
"IncludeArcMachines":
{
"value": "true"
},
"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112":
{
"value": "1.2"
},
"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40":
{
"value": "Compliant"
},
"requiredRetentionDays":
{
"value": "365"
},
"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6":
{
"value": "NetworkWatcherRG"
}
}
Empty file.
29 changes: 29 additions & 0 deletions src/terraform/modules/policy-assignments/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (c) Microsoft Corporation.
shawngib marked this conversation as resolved.
Show resolved Hide resolved
# Licensed under the MIT License.
variable "policy_id" {
description = "The Azure policy ID for the NIST 800-53 R4 policy initiative."
type = string
default = "/providers/Microsoft.Authorization/policySetDefinitions/cf25b9c1-bd23-4eb6-bd2c-f4f3ac644a5f"
}

variable "resource_group_name" {
description = "Resource group name for policy assignment."
type = string
}

variable "environment" {
description = "The Terraform backend environment e.g. public or usgovernment. It defaults to public."
type = string
default = "public"
}

variable "laws_instance_id" {
description = "The log analytics workspace ID which will be provided to the underlying policy rules via the policy parameters."
type = string
}

# Full resource ID used if enabling activity diagnostic logging
variable "log_analytics_workspace_resource_id" {
description = "The resource id of the Log Analytics Workspace"
type = string
}