Giter VIP home page Giter VIP logo

cloud-guardrails's Introduction

Azure Guardrails

Azure Guardrails allows you to rapidly cherry-pick cloud security guardrails by generating Terraform files that create Azure Policy Initiatives.

Continuous Integration Tests Downloads Twitter

Table of contents

Overview

Azure Policies - similar to AWS Service Control Policies (SCPs) - allows Azure customers to enforce organizational standards and enforce security policies at scale. You can use Azure Policies to evaluate the overall state of your environment, and drill down to the security status per resource and per policy. For example, you can prevent users from creating any unencrypted resources or security group rules that allow SSH/RDP Access to 0.0.0.0/0.

Azure provides 400+ built-in security policies. This presents an incredible opportunity for customers who want to enforce preventative security guardrails from the start. However, deciding which of the 400+ built-in policies you want to enforce, and which stages you want to roll them out in can be a bit intimidating at the start.

To help maximize coverage and ease the rollout process, I created this tool so that you can:

  • Cherry-pick and bulk-select the security policies you want according to your specific criteria
  • Enforce low-friction policies within minutes
  • Easily roll back policies that you don't want

Cheatsheet

Quick start

# Install Terraform (prerequisite)
brew install tfenv
tfenv install 0.12.31

# Install via pip
pip3 install cloud-guardrails --user

# Generate Terraform to create security guardrails (audit mode only)
cloud-guardrails generate-terraform --no-params --subscription example

# Generate Terraform to prevent insecure resources from being created (deny mode)
cloud-guardrails generate-terraform --no-params --subscription example --enforce

# Log into Azure and set your subscription
az login
az account set --subscription example

# Apply the policies
terraform init
terraform plan
terraform apply -auto-approve

Writing Policies

# No Parameters
cloud-guardrails generate-terraform --no-params --subscription example

# Optional Parameters (i.e., all the policies have default parameter values)
cloud-guardrails generate-terraform --params-optional --subscription example

# Required Parameters
cloud-guardrails generate-terraform --params-required \
    --service Kubernetes \
    --subscription example

# Create Config file
cloud-guardrails create-config-file --output config.yml

# Create Parameters file
cloud-guardrails create-parameters-file --output parameters.yml

Querying Policy Data

# list-services: List all the services supported by Azure built-in Policies
cloud-guardrails list-services

# list-policies: List all the existing built-in Azure Policies
cloud-guardrails list-policies --service "Kubernetes" --all-policies
cloud-guardrails list-policies --service "Kubernetes" --no-params
cloud-guardrails list-policies --service "Kubernetes" --audit-only

# describe-policy: Describe a specific policy based on display name or the short policy ID
cloud-guardrails describe-policy --id 7c1b1214-f927-48bf-8882-84f0af6588b1
cloud-guardrails describe-policy --name "Storage accounts should use customer-managed key for encryption"

Installation

Option 1: Homebrew

brew tap salesforce/cloud-guardrails https://github.com/salesforce/cloud-guardrails
brew install cloud-guardrails

Option 2: Pip3

pip3 install --user cloud-guardrails

Terraform

  • Install Terraform if you haven't already. I recommend using tfenv, a Terraform version manager:
brew install tfenv
tfenv install 0.12.31

Now you can follow the rest of the tutorial.

Tutorial

Example: No Parameters

  • First, log into Azure and set your subscription
az login
az account set --subscription my-subscription
  • Then generate the Terraform files:
cloud-guardrails generate-terraform --no-params \
    --subscription example
  • Navigate to the Terraform directory and apply the policies:
cd examples/terraform-demo/
terraform init
terraform plan
terraform apply -auto-approve

The output will look like this:

Example output

It will generate a file called no_params.tf. The file contents will look like this: examples/terraform-demo-no-params/main.tf.

Example: Single Service

You can also generate policies for a single service. Consider the example below where we generate Terraform for Key Vault only:

cloud-guardrails generate-terraform --no-params \
    --service "Key Vault" \
    --subscription example
Click to expand!

locals {
  name_no_params = "example_NP_Audit"
  subscription_name_no_params = "example"
  management_group_no_params = ""
  enforcement_mode_no_params = false
  policy_ids_no_params = [
    # -----------------------------------------------------------------------------------------------------------------
    # Key Vault
    # -----------------------------------------------------------------------------------------------------------------
    "c39ba22d-4428-4149-b981-70acb31fc383", # Azure Key Vault Managed HSM should have purge protection enabled
    "0b60c0b2-2dc2-4e1c-b5c9-abbed971de53", # Key vaults should have purge protection enabled
    "1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d", # Key vaults should have soft delete enabled
    "55615ac9-af46-4a59-874e-391cc3dfb490", # Firewall should be enabled on Key Vault
    "152b15f7-8e1f-4c1f-ab71-8c010ba5dbc0", # Key Vault keys should have an expiration date
    "98728c90-32c7-4049-8429-847dc0f4fe37", # Key Vault secrets should have an expiration date
    "587c79fe-dd04-4a5e-9d0b-f89598c7261b", # Keys should be backed by a hardware security module (HSM)
    "5f0bc445-3935-4915-9981-011aa2b46147", # Private endpoint should be configured for Key Vault
    "75262d3e-ba4a-4f43-85f8-9f72c090e5e3", # Secrets should have content type set

  ]
}

# ---------------------------------------------------------------------------------------------------------------------
# Azure Policy name lookups:
# Because the policies are built-in, we can just look up their IDs by their names.
# ---------------------------------------------------------------------------------------------------------------------
data "azurerm_policy_definition" "no_params" {
  count        = length(local.policy_ids_no_params)
  name         = element(local.policy_ids_no_params, count.index)
}

locals {
  no_params_policy_definitions = flatten([tolist([
    for definition in data.azurerm_policy_definition.no_params.*.id :
    map("policyDefinitionId", definition)
    ])
  ])
}

# ---------------------------------------------------------------------------------------------------------------------
# Conditional data lookups: If the user supplies management group, look up the ID of the management group
# ---------------------------------------------------------------------------------------------------------------------
data "azurerm_management_group" "no_params" {
  count = local.management_group_no_params != "" ? 1 : 0
  display_name  = local.management_group_no_params
}

### If the user supplies subscription, look up the ID of the subscription
data "azurerm_subscriptions" "no_params" {
  count                 = local.subscription_name_no_params != "" ? 1 : 0
  display_name_contains = local.subscription_name_no_params
}

locals {
  no_params_scope = local.management_group_no_params != "" ? data.azurerm_management_group.no_params[0].id : element(data.azurerm_subscriptions.no_params[0].subscriptions.*.id, 0)
}

# ---------------------------------------------------------------------------------------------------------------------
# Policy Initiative
# ---------------------------------------------------------------------------------------------------------------------
resource "azurerm_policy_set_definition" "no_params" {
  name                  = local.name_no_params
  policy_type           = "Custom"
  display_name          = local.name_no_params
  description           = local.name_no_params
  management_group_name = local.management_group_no_params == "" ? null : local.management_group_no_params
  policy_definitions    = tostring(jsonencode(local.no_params_policy_definitions))
  metadata = tostring(jsonencode({
    category = local.name_no_params
  }))
}

# ---------------------------------------------------------------------------------------------------------------------
# Azure Policy Assignments
# Apply the Policy Initiative to the specified scope
# ---------------------------------------------------------------------------------------------------------------------
resource "azurerm_policy_assignment" "no_params" {
  name                 = local.name_no_params
  policy_definition_id = azurerm_policy_set_definition.no_params.id
  scope                = local.no_params_scope
  enforcement_mode     = local.enforcement_mode_no_params
}

# ---------------------------------------------------------------------------------------------------------------------
# Outputs
# ---------------------------------------------------------------------------------------------------------------------
output "no_params_policy_assignment_ids" {
  value       = azurerm_policy_assignment.no_params.id
  description = "The IDs of the Policy Assignments."
}

output "no_params_scope" {
  value       = local.no_params_scope
  description = "The target scope - either the management group or subscription, depending on which parameters were supplied"
}

output "no_params_policy_set_definition_id" {
  value       = azurerm_policy_set_definition.no_params.id
  description = "The ID of the Policy Set Definition."
}

output "no_params_count_of_policies_applied" {
  description = "The number of Policies applied as part of the Policy Initiative"
  value       = length(local.policy_ids_no_params)
}

Example: Parameters

Background and Motivation

Azure Parameters are similar to fields on a form - name, address, city, state. These parameters always stay the same, however their values change based on the individual filling out the form. Parameters work the same way when building policies. By including parameters in a policy definition, you can reuse that policy for different scenarios by using different values.

However, the process that Microsoft lays out for managing Policy as Code workflows is less preferable to some, due to the amount of policy files and the lack of a single view of parameters. Their documentation suggests this file structure for laying out your policy files and parameters.

.
|
|- policies/  ________________________ # Root folder for policy resources
|  |- policy1/  ______________________ # Subfolder for a policy
|     |- policy.json _________________ # Policy definition
|     |- policy.parameters.json ______ # Policy definition of parameters
|     |- policy.rules.json ___________ # Policy rule
|     |- assign.<name1>.json _________ # Assignment 1 for this policy definition
|     |- assign.<name2>.json _________ # Assignment 2 for this policy definition
|  |- policy2/  ______________________ # Subfolder for a policy
|     |- policy.json _________________ # Policy definition
|     |- policy.parameters.json ______ # Policy definition of parameters
|     |- policy.rules.json ___________ # Policy rule
|     |- assign.<name1>.json _________ # Assignment 1 for this policy definition
|     |- assign.<name2>.json _________ # Assignment 2 for this policy definition
|

That's a lot of JSON, and a lot of clicks.

cloud-guardrails offers a more elegant solution. You can specify your paramters in the form of a YAML file. The YAML file allows you to modify the parameters per policy in a fine-grained manner. Additionally, you don't have to write the parameters file yourself! Just run the create-parameters-file command, and it will create the file for you. You just have to fill in the values like a form.

Let's take a tour.

Parameters tutorial

  • Generate the parameters file:
cloud-guardrails create-parameters-file \
    --optional-only \
    -o parameters-optional.yml

The generated parameters-optional.yml file will only contain policies that have parameters with default values. The policies are sorted by service for improved readability. Consider the snippet below from the Key Vault section:

# ---------------------------------------------------------------------------------------------------------------------
# Key Vault
# ---------------------------------------------------------------------------------------------------------------------
Key Vault:
  "Resource logs in Key Vault should be enabled":
    effect: AuditIfNotExists  # Allowed: ["AuditIfNotExists", "Disabled"]
    requiredRetentionDays: 365

  "[Preview]: Certificates should be issued by the specified integrated certificate authority":
    allowedCAs:
        - DigiCert
        - GlobalSign # Allowed: ["DigiCert", "GlobalSign"]
    effect: audit  # Allowed: ["audit", "deny", "disabled"]

  "[Preview]: Certificates should have the specified maximum validity period":
    maximumValidityInMonths: 12
    effect: audit  # Allowed: ["audit", "deny", "disabled"]

Notice how some parameters only allow specific values. For example, the policy named "Certificates should be issued by the specified integrated certificate authority" has a parameter called allowedCAs. However, you can't just provide any value to that parameter - it has to be one of two allowed values. cloud-guardrails simplifies this process by including the allowed values in the comments - # Allowed: ["DigiCert", "GlobalSign"].

  • Now let's generate Terraform using this parameters file. Run the following command:
cloud-guardrails generate-terraform --params-optional \
    -s "Key Vault" \
    --subscription example \
    -p parameters-optional.yml
  • Observe that the output will include the parameters that you supplied in your config file:
Click to expand!

locals {
  name_example_PO_Audit = "example_PO_Audit"
  subscription_name_example_PO_Audit = "example"
  management_group_example_PO_Audit = ""
  category_example_PO_Audit = "Testing"
  enforcement_mode_example_PO_Audit = false
  policy_ids_example_PO_Audit = [
    # -----------------------------------------------------------------------------------------------------------------
    # Key Vault
    # -----------------------------------------------------------------------------------------------------------------
    "a2a5b911-5617-447e-a49e-59dbe0e0434b", # Resource logs in Azure Key Vault Managed HSM should be enabled
    "cf820ca0-f99e-4f3e-84fb-66e913812d21", # Resource logs in Key Vault should be enabled
    "8e826246-c976-48f6-b03e-619bb92b3d82", # Certificates should be issued by the specified integrated certificate authority
    "0a075868-4c26-42ef-914c-5bc007359560", # Certificates should have the specified maximum validity period
    "1151cede-290b-4ba0-8b38-0ad145ac888f", # Certificates should use allowed key types
    "bd78111f-4953-4367-9fd5-7e08808b54bf", # Certificates using elliptic curve cryptography should have allowed curve names
    "75c4f823-d65c-4f29-a733-01d0077fdbcb", # Keys should be the specified cryptographic type RSA or EC
    "ff25f3c8-b739-4538-9d07-3d6d25cfb255", # Keys using elliptic curve cryptography should have the specified curve names

  ]
  policy_definition_map = {
    "Resource logs in Azure Key Vault Managed HSM should be enabled" = "/providers/Microsoft.Authorization/policyDefinitions/a2a5b911-5617-447e-a49e-59dbe0e0434b",
    "Resource logs in Key Vault should be enabled" = "/providers/Microsoft.Authorization/policyDefinitions/cf820ca0-f99e-4f3e-84fb-66e913812d21",
    "Certificates should be issued by the specified integrated certificate authority" = "/providers/Microsoft.Authorization/policyDefinitions/8e826246-c976-48f6-b03e-619bb92b3d82",
    "Certificates should have the specified maximum validity period" = "/providers/Microsoft.Authorization/policyDefinitions/0a075868-4c26-42ef-914c-5bc007359560",
    "Certificates should use allowed key types" = "/providers/Microsoft.Authorization/policyDefinitions/1151cede-290b-4ba0-8b38-0ad145ac888f",
    "Certificates using elliptic curve cryptography should have allowed curve names" = "/providers/Microsoft.Authorization/policyDefinitions/bd78111f-4953-4367-9fd5-7e08808b54bf",
    "Keys should be the specified cryptographic type RSA or EC" = "/providers/Microsoft.Authorization/policyDefinitions/75c4f823-d65c-4f29-a733-01d0077fdbcb",
    "Keys using elliptic curve cryptography should have the specified curve names" = "/providers/Microsoft.Authorization/policyDefinitions/ff25f3c8-b739-4538-9d07-3d6d25cfb255",
    }
}

# ---------------------------------------------------------------------------------------------------------------------
# Conditional data lookups: If the user supplies management group, look up the ID of the management group
# ---------------------------------------------------------------------------------------------------------------------
data "azurerm_management_group" "example_PO_Audit" {
  count = local.management_group_example_PO_Audit != "" ? 1 : 0
  display_name  = local.management_group_example_PO_Audit
}

### If the user supplies subscription, look up the ID of the subscription
data "azurerm_subscriptions" "example_PO_Audit" {
  count                 = local.subscription_name_example_PO_Audit != "" ? 1 : 0
  display_name_contains = local.subscription_name_example_PO_Audit
}

locals {
  scope = local.management_group_example_PO_Audit != "" ? data.azurerm_management_group.example_PO_Audit[0].id : element(data.azurerm_subscriptions.example_PO_Audit[0].subscriptions.*.id, 0)
}

# ---------------------------------------------------------------------------------------------------------------------
# Azure Policy Definition Lookups
# ---------------------------------------------------------------------------------------------------------------------

data "azurerm_policy_definition" "example_PO_Audit_definition_lookups" {
  count = length(local.policy_ids_example_PO_Audit)
  name  = local.policy_ids_example_PO_Audit[count.index]
}

# ---------------------------------------------------------------------------------------------------------------------
# Azure Policy Initiative Definition
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_policy_set_definition" "example_PO_Audit" {
  name                  = local.name_example_PO_Audit
  policy_type           = "Custom"
  display_name          = local.name_example_PO_Audit
  description           = local.name_example_PO_Audit
  management_group_name = local.management_group_example_PO_Audit == "" ? null : local.management_group_example_PO_Audit
  metadata = tostring(jsonencode({
    category = local.category_example_PO_Audit
  }))
  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Resource logs in Azure Key Vault Managed HSM should be enabled")
    parameter_values = jsonencode({
        effect = { "value" : "AuditIfNotExists" }
        requiredRetentionDays = { "value" : "365" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Resource logs in Key Vault should be enabled")
    parameter_values = jsonencode({
        effect = { "value" : "AuditIfNotExists" }
        requiredRetentionDays = { "value" : "365" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Certificates should be issued by the specified integrated certificate authority")
    parameter_values = jsonencode({
        allowedCAs = { "value" : ["DigiCert", "GlobalSign"] }
        effect = { "value" : "audit" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Certificates should have the specified maximum validity period")
    parameter_values = jsonencode({
        maximumValidityInMonths = { "value" : 12 }
        effect = { "value" : "audit" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Certificates should use allowed key types")
    parameter_values = jsonencode({
        allowedKeyTypes = { "value" : ["RSA", "RSA-HSM"] }
        effect = { "value" : "audit" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Certificates using elliptic curve cryptography should have allowed curve names")
    parameter_values = jsonencode({
        allowedECNames = { "value" : ["P-256", "P-256K", "P-384", "P-521"] }
        effect = { "value" : "audit" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Keys should be the specified cryptographic type RSA or EC")
    parameter_values = jsonencode({
        allowedKeyTypes = { "value" : ["RSA", "RSA-HSM", "EC", "EC-HSM"] }
        effect = { "value" : "Audit" }
    })
    reference_id = null
  }

  policy_definition_reference {
    policy_definition_id = lookup(local.policy_definition_map, "Keys using elliptic curve cryptography should have the specified curve names")
    parameter_values = jsonencode({
        allowedECNames = { "value" : ["P-256", "P-256K", "P-384", "P-521"] }
        effect = { "value" : "Audit" }
    })
    reference_id = null
  }
}

# ---------------------------------------------------------------------------------------------------------------------
# Azure Policy Assignments
# Apply the Policy Initiative to the specified scope
# ---------------------------------------------------------------------------------------------------------------------
resource "azurerm_policy_assignment" "example_PO_Audit" {
  name                 = local.name_example_PO_Audit
  policy_definition_id = azurerm_policy_set_definition.example_PO_Audit.id
  scope                = local.scope
  enforcement_mode     = local.enforcement_mode_example_PO_Audit
}


# ---------------------------------------------------------------------------------------------------------------------
# Outputs
# ---------------------------------------------------------------------------------------------------------------------
output "example_PO_Audit_policy_assignment_ids" {
  value       = azurerm_policy_assignment.example_PO_Audit.id
  description = "The IDs of the Policy Assignments."
}

output "example_PO_Audit_scope" {
  value       = local.scope
  description = "The target scope - either the management group or subscription, depending on which parameters were supplied"
}

output "example_PO_Audit_policy_set_definition_id" {
  value       = azurerm_policy_set_definition.example_PO_Audit.id
  description = "The ID of the Policy Set Definition."
}

Tutorial: Selecting Policies using the Config File

Let's say that you want more granular control over which policies to apply. You can create a config file to manage this.

To create the config file, run the following command:

# Create Config file
cloud-guardrails create-config-file --output config.yml

This generates a file called config.yml with the following contents:

Click to expand!

####
# match_only_keywords: Use this to only apply policies that match any of these keywords
# exclude_keywords: Use this to skip policies that have any of these keywords in the display name
# exclude_services: Specify services that you want to exclude entirely.
# exclude_policies: Specify Azure Policy Definition displayNames that you want to exclude from the results, sorted by service
####

# Use this to only apply policies that match any of these keywords
# Example: "encrypt", "SQL", "HTTP"
match_only_keywords:
  - ""


exclude_keywords:
  - ""
  - "virtual network service endpoint"
  #- "private link"


# Specify services that you want to exclude entirely.
# Uncomment the services mentioned below if you want to exclude them.
exclude_services:
  - ""
  - "Guest Configuration"

  #- "API Management"
  #- "API for FHIR"
  #- "App Configuration"
  #- "App Platform"
  #- "App Service"
  #- "Attestation"
  #- "Automanage"
  #- "Automation"
  #- "Azure Active Directory"
  #- "Azure Data Explorer"
  #- "Azure Stack Edge"
  #- "Backup"
  #- "Batch"
  #- "Bot Service"
  #- "Cache"
  #- "Cognitive Services"
  #- "Compute"
  #- "Container Instance"
  #- "Container Registry"
  #- "Cosmos DB"
  #- "Custom Provider"
  #- "Data Box"
  #- "Data Factory"
  #- "Data Lake"
  #- "Event Grid"
  #- "Event Hub"
  #- "General"
  #- "HDInsight"
  #- "Internet of Things"
  #- "Key Vault"
  #- "Kubernetes"
  #- "Kubernetes service"
  #- "Lighthouse"
  #- "Logic Apps"
  #- "Machine Learning"
  #- "Managed Application"
  #- "Media Services"
  #- "Migrate"
  #- "Monitoring"
  #- "Network"
  #- "Portal"
  #- "SQL"
  #- "Search"
  #- "Security Center"
  #- "Service Bus"
  #- "Service Fabric"
  #- "SignalR"
  #- "Site Recovery"
  #- "Storage"
  #- "Stream Analytics"
  #- "Synapse"
  #- "Tags"
  #- "VM Image Builder"
  #- "Web PubSub"


# Specify Azure Policy Definition displayNames that you want to exclude from the results
exclude_policies:
  General:
    - "Allow resource creation only in Asia data centers"
    - "Allow resource creation only in European data centers"
    - "Allow resource creation only in India data centers"
    - "Allow resource creation only in United States data centers"
  Tags:
    - "Allow resource creation if 'department' tag set"
    - "Allow resource creation if 'environment' tag value in allowed values"
  API Management:
    # This collides with the same one from App Platform
    - "API Management services should use a virtual network"
  App Platform:
    # This collides with the same one from API Management
    - "Azure Spring Cloud should use network injection"
  Guest Configuration:
    # This outputs a parameter called "Cert:" that breaks the parameter yaml format
    - "Audit Windows machines that contain certificates expiring within the specified number of days"
  Network:
    # This one is overly cumbersome for most organizations
    - "Network interfaces should not have public IPs"



  API for FHIR:
    - ""

  App Configuration:
    - ""

  App Service:
    - ""

  Attestation:
    - ""

  Automanage:
    - ""

  Automation:
    - ""

  Azure Active Directory:
    - ""

  Azure Data Explorer:
    - ""

  Azure Stack Edge:
    - ""

  Backup:
    - ""

  Batch:
    - ""

  Bot Service:
    - ""

  Cache:
    - ""

  Cognitive Services:
    - ""

  Compute:
    - ""

  Container Instance:
    - ""

  Container Registry:
    - ""

  Cosmos DB:
    - ""

  Custom Provider:
    - ""

  Data Box:
    - ""

  Data Factory:
    - ""

  Data Lake:
    - ""

  Event Grid:
    - ""

  Event Hub:
    - ""

  HDInsight:
    - ""

  Internet of Things:
    - ""

  Key Vault:
    - ""

  Kubernetes:
    - ""

  Kubernetes service:
    - ""

  Lighthouse:
    - ""

  Logic Apps:
    - ""

  Machine Learning:
    - ""

  Managed Application:
    - ""

  Media Services:
    - ""

  Migrate:
    - ""

  Monitoring:
    - ""

  Portal:
    - ""

  SQL:
    - ""

  Search:
    - ""

  Security Center:
    - ""

  Service Bus:
    - ""

  Service Fabric:
    - ""

  SignalR:
    - ""

  Site Recovery:
    - ""

  Storage:
    - ""

  Stream Analytics:
    - ""

  Synapse:
    - ""

  VM Image Builder:
    - ""

  Web PubSub:
    - ""

It has a few different sections, which we cover below.

Matching Keywords

If you want to select policies based on the keywords of the policy name, you can insert those keywords in the match_only_keywords list in the config.yml file. For example, let's say you only want to apply the SQL-related policies:

match_only_keywords:
  - "SQL"

Don't know the names of the policies? If you want to show the names of all the policies, you can use the list-policies command to list all of them. You can also filter the results based on which service or whether parameters are required, optional, or not required:

cloud-guardrails list-policies --service "Kubernetes" --all-policies
cloud-guardrails list-policies --service "Kubernetes" --no-params
cloud-guardrails list-policies --service "Kubernetes" --audit-only

Excluding Policies based on Keywords

Let's say that you don't want to apply any of the policies that mention Private Link or Virtual Network Service Endpoints. You can insert those keywords in the exclude_keywords list to skip policies that have any of these keywords in the display name:

exclude_keywords:
  - "virtual network service endpoint"
  - "private link"

Don't know the names of the policies? If you want to show the names of all the policies, you can use the list-policies command to list all of them. You can also filter the results based on which service or whether parameters are required/optional/not required:

cloud-guardrails list-policies --service "Kubernetes" --all-policies
cloud-guardrails list-policies --service "Kubernetes" --no-params
cloud-guardrails list-policies --service "Kubernetes" --audit-only

Excluding specific policies

If you want to exclude specific policies entirely, you can specify the display names for those policies that you want to exclude using the exclude_policies list, like below:

exclude_policies:
  General:
    - "Allow resource creation only in Asia data centers"
    - "Allow resource creation only in European data centers"
    - "Allow resource creation only in India data centers"
    - "Allow resource creation only in United States data centers"

Don't know the names of the policies? If you don't know the names of the services to include, you can use the list-policies command to list all the services supported by Azure built-in Policies:

cloud-guardrails list-policies --service "Kubernetes" --all-policies
cloud-guardrails list-policies --service "Kubernetes" --no-params
cloud-guardrails list-policies --service "Kubernetes" --audit-only

Policy Characteristics and Support Statuses

Characteristic Support Description

Parameters

Policies with No Parameters โœ… These policies do not require any parameters. These are the easiest policies to apply.
Policies with Optional Parameters โœ… These policies allow the use of parameters, but the parameters have default values. These are easy to apply at first, but you will likely want to adjust the parameters throughout the lifecycle.
Policies with Required Parameters โœ… These policies require parameters which do not have default values. While rollout is significantly less time consuming than the Azure recommended workflow, finding the best values for your environment requires careful thinking.

Effects

Policies with "Deny" Effects โœ…
These Policies block bad actions, acting as true guardrails.
Policies with "Audit" Effects โœ… These Policies do not actually prevent bad actions, even if โ€œenforcement modeโ€ is set to True - they just flag the bad actions.
Policies with "Deploy" Effects โŒ Some Azure Policies have DeployIfNotExists effects, which create resources via an ARM Template when the condition is met. For example, the policy "Deploy network watcher when virtual networks are created" will create a resource group called networkWatcherRG.
Policies with "Modify" Effects โŒ Some Azure Policies have Policy Effects that allow the modification of resources, such as the "Modify" or "Append" effects.

Built-in vs. Custom

Built-in Policies โœ… Microsoft provides 400+ built-in policy definitions. cloud-guardrails leverages these Built-in Policy definitions so you can get rapidly started and maximize your coverage.
Custom Policies โŒ cloud-guardrails does not support creation of Custom Policies. Consider leveraging Custom Policies after you have built out your process and workflow using cloud-guardrails.

Contributing

Setup

  • Set up the virtual environment
# Set up the virtual environment
python3 -m venv ./venv && source venv/bin/activate
pip3 install -r requirements.txt
  • Build the package
# To build only
make build

# To build and install
make install

# To run tests
make test

# To clean local dev environment
make clean

Other tasks

  • Update with the latest Azure Compliance data
make update-data
  • Update the policy summary tables
make update-policy-table
  • Update the Azure Policy Git submodule and merge it
# Without merge
make update-submodule

# With merge
make update-submodule-with-merge

Authors and Contributors

References

cloud-guardrails's People

Contributors

actions-user avatar dependabot[bot] avatar kmcquade avatar svc-scm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cloud-guardrails's Issues

Error in function call

Anyone can help me?

Warning: "policy_definitions": [DEPRECATED] Deprecated in favour of policy_definition_reference

on no_params.tf line 72, in resource "azurerm_policy_set_definition" "no_params":
72: resource "azurerm_policy_set_definition" "no_params" {

Error: Error in function call

on no_params.tf line 66, in locals:
66: no_params_scope = local.management_group_no_params != "" ? data.azurerm_management_group.no_params[0].id : element(data.azurerm_subscriptions.no_params[0].subscriptions.*.id, 0)
|----------------
| data.azurerm_subscriptions.no_params[0].subscriptions is empty list of object

Call to function "element" failed: cannot use element function with an empty
list.

image

Shuffle the Terraform directory

The Jinja2 templates should be in their own folders.

Also, the Terraform module is in a completely separate folder at the moment.

Incorrectly formatted config file throws confusing error

Error:

2021-03-24 15:01:58,780 azure_guardrails.shared.config [CRITICAL] while parsing a block collection
  in "azure-guardrails-config.yml", line 38, column 5
expected <block end>, but found ','
  in "azure-guardrails-config.yml", line 40, column 85
Traceback (most recent call last):
  File "/Users/kmcquade/.venv/bin/azure-guardrails", line 8, in <module>
    sys.exit(main())
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/azure_guardrails/bin/cli.py", line 25, in main
    azure_guardrails()
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/azure_guardrails/command/generate_terraform.py", line 138, in generate_terraform
    config = get_config_from_file(
  File "/Users/kmcquade/.venv/lib/python3.9/site-packages/azure_guardrails/shared/config.py", line 215, in get_config_from_file
    cfg_exclude_policies = config_cfg.get("exclude_policies", None)
UnboundLocalError: local variable 'config_cfg' referenced before assignment

The UnboundLocalError is confusing. Instead of Logger.critical, it should just fail when the parsing error occurs

Command line option changes

Change

  • Remove --policy-set-name
  • Remove --target-name and --target-type and be opinionated about the Policy Initiative naming.

We can do this by offering --subscription and --management-group as mutually exclusive options. The name of the Policy initiative. Example names:

HotDogsAreSandwiches-SubscriptionName-NoParams
HotDogsAreSandwiches-SubscriptionName-Params
HotDogsAreSandwiches-ProdMG-ParamsWithDefaults
  • Summaries: By default, create the CSV and the MD summary files (summary-all-noparams.md, summary-all-noparams.csv; offer an option to turn this off, but have it on by default.

  • --config-file -> --config

Add

  • --parameters: For the yaml file that supplies the parameters

Keep

  • --exclude-services
  • --services
  • --enforce

Revisit

  • --with-parameters
  • --empty-defaults
  • --module-source

Enforcement issues

This policy should be enforced if we run cloud-guardrails generate-terraform --no-params:

{
  "properties": {
    "displayName": "Storage account encryption scopes should use customer-managed keys to encrypt data at rest",
    "policyType": "BuiltIn",
    "mode": "All",
    "description": "Use customer-managed keys to manage the encryption at rest of your storage account encryption scopes. Customer-managed keys enable the data to be encrypted with an Azure key-vault key created and owned by you. You have full control and responsibility for the key lifecycle, including rotation and management. Learn more about storage account encryption scopes at https://aka.ms/encryption-scopes-overview.",
    "metadata": {
      "version": "1.0.0",
      "category": "Storage"
    },
    "parameters": {
      "effect": {
        "type": "String",
        "metadata": {
          "displayName": "Effect",
          "description": "Enable or disable the execution of the audit policy"
        },
        "allowedValues": [
          "Audit",
          "Deny",
          "Disabled"
        ],
        "defaultValue": "Audit"
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Storage/storageAccounts/encryptionScopes"
          },
          {
            "field": "Microsoft.Storage/storageAccounts/encryptionScopes/source",
            "notEquals": "Microsoft.Keyvault"
          }
        ]
      },
      "then": {
        "effect": "[parameters('effect')]"
      }
    }
  },
  "id": "/providers/Microsoft.Authorization/policyDefinitions/b5ec538c-daa0-4006-8596-35468b9148e8",
  "name": "b5ec538c-daa0-4006-8596-35468b9148e8"
}

Notice how the default value for the Effect parameter is Audit, not Deny.

We intend for it to be enforced, because the only effect is Deny, Audit, or Disabled. The intended behavior of Cloud Guardrails is that if you specify --enforce, it should change the effect to Deny by default. We should be able to do this without modifying any parameters files.

This is a pretty large bug that absolutely needs to be addressed.

Terraform should include Policy IDs, not display names

Sometimes the display names change randomly (thanks, Microsoft). Need to include the Policy IDs instead. I will insert the human-readable Display names as comments so it is more readable.

The ones below gave me errors today, because the name in the Azure Portal changed, whereas it stayed the same in the Azure Policy Repo. https://github.com/Azure/azure-policy/

Policy ID GitHub Name in Azure Portal that changed 3/17/2021
89099bee-89e0-4b26-a5f4-165451757743 SQL Servers should retain audit data for at least 90 days SQL servers should be configured with 90 days auditing retention or higher
6edd7eda-6dd8-40f7-810d-67160c639cd9 Storage accounts should use private link Storage account should use a private link connection
0725b4dd-7e76-479c-a735-68e7ee23d5ca Cognitive Services accounts should disable public network access Public network access should be disabled for Cognitive Services accounts
72d11df1-dd8a-41f7-8925-b05b960ebafc Azure Synapse workspaces should use private link Private endpoint connections on Azure Synapse workspaces should be enabled
c251913d-7d24-4958-af87-478ed3b9ba41 Flow logs should be configured for every network security group Flow log should be configured for every network security group

Feature: Parameters config file should allow you to list by policy ID, not just display name

Notice how the Azure response says "The value '0' is not allowed for policy parameter 'minimumRSAKeySize' in policy definition 'cee51871-e572-4576-855c-047c820360f0'. The allowed values are '2048, 3072, 4096'."

If we allowed users to specify that policy ID, that could make their job a bit easier.

Error: creating Policy Set Definition "example_PR_Audit": policy.SetDefinitionsClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="PolicyParameterValueNotAllowed" Message="The value '0' is not allowed for policy parameter 'minimumRSAKeySize' in policy definition 'cee51871-e572-4576-855c-047c820360f0'. The allowed values are '2048, 3072, 4096'."

  on main.tf line 257, in resource "azurerm_policy_set_definition" "example_PR_Audit":
 257: resource "azurerm_policy_set_definition" "example_PR_Audit" {

Improve logging

It would be great to be able to gather logs on which policies are excluded, etc.

Add Terraform version check

Cloud-guardrails requires the use of Terraform 0.12.31, so it would be great if we can have the cloud-guardrails tool check that the proper Terraform version is installed and active.

Alternatively, if we added a Terraform versions.tf file to the output, that would work too.

Default values for Effect parameter should be Audit or AuditIfNotExists, not deny

The default value for the "Effect" parameter in the following policies are "Deny" instead of "Audit" or "AuditIfNotExists".

  • Require automatic OS image patching on Virtual Machine Scale Sets
  • SQL Managed Instances should avoid using GRS backup redundancy
  • SQL Database should avoid using GRS backup redundancy
  • Azure Cosmos DB accounts should have firewall rules
  • Require encryption on Data Lake Store accounts
  • Gateway subnets should not be configured with a network security group

Auto populate reference_id in Terraform

image

Right now it is just set to null. If we can populate it with something, then we can use the PolicyReferenceID in policy exemptions so the exemptions can be more granular.

If the policyAssignmentId is for an initiative assignment, the policyDefinitionReferenceIds property may be used to specify which policy definition(s) in the initiative the subject resource has an exemption to. As the resource may be exempted from one or more included policy definitions, this property is an array. The values must match the values in the initiative definition in the policyDefinitions.policyDefinitionReferenceId fields.

Per the docs here: https://docs.microsoft.com/en-us/azure/governance/policy/concepts/exemption-structure#policy-definition-ids

Automatically create provider file if it doesn't exist

Right now if you create the Terraform file and then try to apply it, you'll get this error:

Error: "features": required field is not set

It's a silly requirement by the Terraform Azure provider.

I should write something that greps through the .tf files in that directory and creates a file with that content if it doesn't exist. This will make it easier for people trying this out.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.