AWS Yarns

A blog about AWS things.

Continuous Compliance on AWS using the NZISM Conformance Pack

Posted by Chris McKinnel - 13 October 2021
7 minute read

A couple of years ago I reached out to a few people at AWS and asked them if there was anything they could share with me around mapping NZISM controls to AWS security features, and unfortunately at the time there was some in-progress work being done but nothing complete and nothing sharable.

I remember talking to a few colleagues and us all agreeing that, yes, it sure would be nice if there was some kind of list that mapped the NZISM controls so we didn't have to do it ourselves when a customer eventually wanted it, and it would be even better if there were a way to continuously monitor the controls... and then we all went on with our lives.

Fast forward two years, and many customer conversations around the work required to a) ensure that an AWS environment was compliant to NZISM controls and b) ensure that there were mechanisms in place that would give assurance that continuous compliance was being maintained, and AWS have released an NZISM conformance pack for AWS Config!

A cat celebrating.

Operational Best Practices for NZISM

It's called Operational Best Practices for NZISM and it's made up of (currently) 89 rules, with a split of 84 automated AWS Config rules and 5 Process Check rules.

A Process Check is a type of AWS Config Rule that you must check yourself, manually, and AWS Config provides a place to aggregate the status of your manual checks.

One of CCL's AWS Engineers, Matt Brandon, has recently deployed this for one of our health customers and remediated the findings so they are fully compliant. They now have a single pane of glass to view their NZISM compliance status for their AWS environment and the resources deployed into it.

Instead of Matt having all the fun, I decided that if I was going to write a blog post about the conformance pack, I would need to deploy it myself and see what all the fuss was about.

Deploying the NZISM conformance pack

For this mini-project, I deployed a completely fresh AWS Landing Zone using Control Tower. Let's put Control Tower to the test and see how compliant a completely green-field deployment is! For anyone who has used Control Tower before, the following account structure will look familiar.

Control Tower account structure.

It's been a while since I've been hands on keyboard deploying stuff into AWS, but I decided to use Terraform for this mini-project to see if I could still remember how to use it. I had to upgrade from 0.14 to 1.0.8, so it turns out it's been a really long time!

First things first, I created a project directory, created a providers.tf, and initialised terraform.

providers.tf

provider "aws" {
  region  = "ap-southeast-2"
  profile = var.aws_profile
}

mkdir aws-config-nzism
vim providers.tf (contents above)
terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.62.0...
- Installed hashicorp/aws v3.62.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Next, I downloaded the conformance pack definition from the AWS Labs GitHub and saved it to my project directory.

wget https://raw.githubusercontent.com/awslabs/aws-config-rules/master/aws-config-conformance-packs/Operational-Best-Practices-for-NZISM.yaml
--2021-10-12 09:41:05--  https://raw.githubusercontent.com/awslabs/aws-config-rules/master/aws-config-conformance-packs/Operational-Best-Practices-for-NZISM.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 41869 (41K) [text/plain]
Saving to: ‘Operational-Best-Practices-for-NZISM.yaml’

100%[==================================================================================================================================================>] 41,869      --.-K/s   in 0.001s  

2021-10-12 09:41:05 (77.4 MB/s) - ‘Operational-Best-Practices-for-NZISM.yaml’ saved [41869/41869]

So now I have the conformance pack and our provider in my project directory:

tree
.
├── Operational-Best-Practices-for-NZISM.yaml
└── providers.tf

0 directories, 2 files

Next I wrote some Terraform to deploy the conformance pack. Spoiler alert: it turns out this is ridiculously easy because Control Tower has already done the heavy lifting for us and enabled AWS Config in the Organisation member accounts.

If you're looking to do this on really fresh accounts that aren't being managed by Control Tower (or really old / immature accounts), you would have some extra steps to complete - you'd need to enable AWS Config, and create some prerequisite resources to do this (an IAM Role, S3 bucket, etc).

main.tf

resource "aws_config_organization_conformance_pack" "nzism" {
  name = "nzism-conformance-pack-v3-4"
  
  template_body = file("${path.module}/${var.conformance_pack_path}")
}

Notice we're telling Terraform to deploy an Organisational Conformance Pack, meaning that we want the conformance pack to be deployed to all member accounts in the organisation.

Because we deployed our accounts and Organisation with Control Tower, it is already configured to aggregate AWS Config findings in the Audit account. So, once our conformance pack is deployed, we should see all of our member accounts showing up in the Audit account.

variables.tf

variable "conformance_pack_path" {
  type = string
  description = "Filename of the conformance pack"
}

terraform.tfvars

aws_profile="landing_zone_master"
conformance_pack_path="Operational-Best-Practices-for-NZISM.yaml"

I'm almost ready to deploy the conformance pack, the directory structure is looking like this:

tree
.
├── main.tf
├── Operational-Best-Practices-for-NZISM.yaml
├── providers.tf
├── terraform.tfvars
└── variables.tf

0 directories, 5 files

Now all I needed to do was set my AWS profile credentials (note the profile called "landing_zone_master" in terraform.tfvars).

export AWS_PROFILE=landing_zone_master
export AWS_ACCESS_KEY_ID="XXXX"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxx"
export AWS_SESSION_TOKEN="xxxxxx"

Let's do it!

terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_config_organization_conformance_pack.nzism will be created
  + resource "aws_config_organization_conformance_pack" "nzism" {
      + arn           = (known after apply)
      + id            = (known after apply)
      + name          = "nzism-conformance-pack-v3-4"
      + template_body = <<-EOT
            Resources:
              AcmCertificateExpirationCheck:
                Controls: ['1667']
                Type: AWS::Config::ConfigRule
                Properties:
                  ConfigRuleName: acm-certificate-expiration-check
                  InputParameters:
                    daysToExpiration: '90'
                  Scope:
                    ComplianceResourceTypes:
                    - AWS::ACM::Certificate
                  Source:
                    Owner: AWS
                    SourceIdentifier: ACM_CERTIFICATE_EXPIRATION_CHECK
                  Description: "SHOULD 14.5.8.C.01[CID:1667]| Software security/Web Application Development/Web applications"
              # 
              # Lots of other rules!
              #
              Wafv2LoggingEnabled:
                Controls: ['2013']
                Type: AWS::Config::ConfigRule
                Properties:
                  ConfigRuleName: wafv2-logging-enabled
                  Source:
                    Owner: AWS
                    SourceIdentifier: WAFV2_LOGGING_ENABLED
                  Description: "SHOULD 16.6.10.C.02[CID:2013]| Access Control and Passwords/Event Logging and Auditing/Additional events to be logged"
        EOT
    }

Plan: 1 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

Great, we can see that Terraform has included the YAML that defines the conformance pack - let's go ahead with an apply.

terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_config_organization_conformance_pack.nzism will be created
  + resource "aws_config_organization_conformance_pack" "nzism" {
      + arn           = (known after apply)
      + id            = (known after apply)
      + name          = "nzism-conformance-pack-v3-4"
      + template_body = <<-EOT
            Resources:
              AcmCertificateExpirationCheck:
                Controls: ['1667']
                Type: AWS::Config::ConfigRule
                Properties:
                  ConfigRuleName: acm-certificate-expiration-check
                  InputParameters:
                    daysToExpiration: '90'
                  Scope:
                    ComplianceResourceTypes:
                    - AWS::ACM::Certificate
                  Source:
                    Owner: AWS
                    SourceIdentifier: ACM_CERTIFICATE_EXPIRATION_CHECK
                  Description: "SHOULD 14.5.8.C.01[CID:1667]| Software security/Web Application Development/Web applications"
              #
              # Lots of other rules!
              #
              Wafv2LoggingEnabled:
                Controls: ['2013']
                Type: AWS::Config::ConfigRule
                Properties:
                  ConfigRuleName: wafv2-logging-enabled
                  Source:
                    Owner: AWS
                    SourceIdentifier: WAFV2_LOGGING_ENABLED
                  Description: "SHOULD 16.6.10.C.02[CID:2013]| Access Control and Passwords/Event Logging and Auditing/Additional events to be logged"
        EOT
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_config_organization_conformance_pack.nzism: Creating...

The first thing I noticed about deploying the conformance pack was that it was very slow. My first attempt at deploying took 8 minutes and then timed out, and the destroy process took 16 minutes. I guess it's understandable as it's deploying 89 new config rules for each account in the organisation, and it's probably using CloudFormation behind the scenes, and maybe even StackSets.

aws_config_organization_conformance_pack.nzism: Creating...
aws_config_organization_conformance_pack.nzism: Still creating... [10s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [20s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [30s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [40s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [50s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m0s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m10s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m20s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m30s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m40s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [1m50s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m0s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m10s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m20s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m30s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m40s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [2m50s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m0s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m10s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m20s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m30s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m40s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [3m50s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [4m0s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [4m10s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [4m20s elapsed]
aws_config_organization_conformance_pack.nzism: Still creating... [4m30s elapsed]
╷
│ Error: error waiting for Config Organization Conformance Pack (nzism-conformance-pack-v3-4) to be created: InternalError: One or more member accounts failed to process your request. Use the GetOrganizationConformancePackDetailedStatus API to retrieve detailed error status for each member account.
│ 
│   with aws_config_organization_conformance_pack.nzism,
│   on main.tf line 1, in resource "aws_config_organization_conformance_pack" "nzism":
│    1: resource "aws_config_organization_conformance_pack" "nzism" {
│ 
╵

Once it finally timed out, I checked the AWS Config Dashboard in the Audit account and could see that the conformance packs deployed successfully - maybe there is some kind of Terraform bug with the AWS organisation conformance packs? At any rate, it seems like too much work to dig deep on this, let's keep moving.

Screen shot of NZISM conformance packs.

Notice that we've got multiple accounts showing up here, this gives us the single pane of glass into what's compliant and what's not, and AWS Config will do the leg-work of constantly checking and re-checking to ensure we're aware of whether we're compliant or not.

Not surprisingly, a default Control Tower deploy is not NZISM complaint. There are some obvious things that we need to do like setting up MFA on our root users, enabling GuardDuty, enabling Security Hub, etc.

Gotchas

Master account not included
If you're deploying an Organisation Conformance Pack, the master account is not included. This is common across the other Organisation deployments as well, and I'd bet money on it being because these things use StackSets in the backend and you can't deploy a StackSet into the account you're deploying it from. So, if you want your master account to be aggregated into your Audit account, then you need to enable Config, deploy the conformance pack into the master and aggregate its findings into the Audit account.

I'm sure this is possible, but I haven't covered it in this blog.

Aggregated rules drill-down
Another minor annoyance is you can't dig into the rule and which control it is referencing from the aggregators part of the console. You have to go into the account itself and find the rule that's not compliant, then the description shows up.

Aggregators rule drill-down:
Screen shot of aggregated rules drill-down.

Actual rules drill-down:
Screen shot of rules drill-down.

Data classification levels
The one thing I'm not sure about with this conformance pack is how it handles the different data classification levels of NZISM and the more strict controls for the higher levels. Next on my list is to have a deeper look into the controls that have been mapped and whether or not there are any that change when the classification level goes from "In Confidence" to "Sensitive" or "Top Secret".

Are new vended accounts compliant?

Next, I enrolled a new account into the Landing Zone using the Account Factory, and I kept the default VPC enabled. It'll be interesting to see if the default VPC deployed with new Control Tower accounts is compliant to NZISM standards or not.

Screen shot of a new account vended.

As expected, our conformance pack has been deployed to the new AWS account automatically as part of the Organisation account baseline. Neat! We can now see 3 conformance packs deployed.

Screen shot of a new account conformance pack deployed.

Digging into the rules, it looks like there is only a Security Group rule failure on the default VPC. Good to see!

Screen shot a failed security group rule.

Download the project repo

You can get a copy of the full code for this mini-project on my GitHub. Hopefully it helps you deploy the NZISM conformance pack!

AWS is investing in New Zealand

With the launch of the NZISM conformance pack, the increase in AWS headcount in New Zealand and the recent announcement of a fully functional AWS region by 2024, it's clear that AWS is investing in New Zealand and it's game on for New Zealand businesses and organisations to figure out how to unlock their data and take advantage of on-shore cloud services.