Continuous Compliance on AWS using the NZISM Conformance Pack
Posted by Chris McKinnel - 13 October 20217 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!
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.
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.
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:
Actual 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.
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.
Digging into the rules, it looks like there is only a Security Group rule failure on the default VPC. Good to see!
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.