InSpec for AWS
- Project State: Maintained
For more information on project states and SLAs, see this documentation.
This InSpec resource pack uses the AWS Ruby SDK v3 and provides the required resources to write tests for resources in AWS.
Prerequisites
AWS Credentials
Valid AWS credentials are required, see AWS Documentation
There are multiple ways to set AWS credentials as shown below:
1) Environment Variables
Set your AWS credentials in an .envrc
file or export them in your shell. (See example .envrc file)
# Example configuration
export AWS_ACCESS_KEY_ID="AKIAJUMP347SLS66IGCQ"
export AWS_SECRET_ACCESS_KEY="vD2lfoNvPdwsofqyuO9jRuWUkZIMqisdfeFmkHTy7ON+w"
export AWS_REGION="eu-west-3"
export AWS_AVAILABILITY_ZONE="eu-west-3a"
2) Configuration File
Set your AWS credentials in ~/.aws/config
and ~/.aws/credentials
file. (See example aws configure credentials)
Example ~/.aws/credentials
:
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[engineering]
aws_access_key_id=AKIAIOSFODNN7EXAMPLF
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY1
Example ~/.aws/config
:
[default]
region=us-west-2
[engineering]
region=us-east-2
AWS SDK will select the default credentials unless aws_profile
is set in an .envrc
# Example configuration
export AWS_PROFILE="engineering"
The credentials precedence is:
- Credentials set in
.envrc
OR as an Environment variable. - Credentials set in
~/.aws/credentials
AND~/.aws/config
ANDAWS_PROFILE
set as an Environment variable. - Credentials set in
~/.aws/credentials
AND~/.aws/config
ANDAWS_PROFILE
is NOT set as an Environment variable. Default credentials will be used.
AWS Region
The aws_region
parameter can be provided to query resources in a specific region. If not provided, the AWS region set in environment variables or configuration files will be used.
Example:
describe aws_ec2_instances(aws_region: 'us-west-2') do
its('count') { should eq 10 }
end
Assuming an IAM role
Assuming an IAM role allows an IAM user to gain additional/different permissions to perform actions in a different AWS account. (See example aws configure IAM role)
Example:
[profile example_profile]
role_arn = arn:aws:iam::123456789012:role/example_profile
source_profile = user1
Permissions
Each resource will require specific permissions to perform the operations required for testing. For example, to test an AWS EC2 instance, your service principal will require the ec2:DescribeInstances
and iam:GetInstanceProfile
permissions. You can find a comprehensive list of each resource's required permissions in the documentation.
Use the Resources
Since this is an InSpec resource pack, it only defines InSpec resources. It includes example tests only. To easily use the AWS resources in your tests do the following:
$ inspec init profile my-profile
Example inspec.yml:
name: my-profile
title: My own AWS profile
version: 0.1.0
inspec_version: '>= 4.6.9'
depends:
- name: inspec-aws
url: https://github.com/inspec/inspec-aws/archive/x.tar.gz
supports:
- platform: aws
(For available inspec-aws versions, see this list of inspec-aws versions.)
Add some tests and run the profile via:
$ inspec exec my-profile -t aws://
Resource documentation
This resouce pack allows the testing of the following AWS resources. If a resource you wish to test is not listed, please feel free to open an Issue. As an open source project, we also welcome public contributions via Pull Request.
- aws_alb
- aws_auto_scaling_group
- aws_auto_scaling_groups
- aws_cloudformation_stack
- aws_cloudtrail_trail
- aws_cloudtrail_trails
- aws_cloudwatch_alarm
- aws_cloudwatch_log_metric_filter
- aws_config_delivery_channel
- aws_config_recorder
- aws_dhcp_options
- aws_dynamodb_table
- aws_ebs_volume
- aws_ebs_volumes
- aws_ec2_instance
- aws_ec2_instances
- aws_ecr
- aws_ecs_cluster
- aws_ecs_clusters
- aws_eks_cluster
- aws_eks_clusters
- aws_elb
- aws_elbs
- aws_flow_log
- aws_hosted_zone
- aws_hosted_zones
- aws_iam_access_key
- aws_iam_access_keys
- aws_iam_account_alias
- aws_iam_group
- aws_iam_groups
- aws_iam_inline_policy
- aws_iam_password_policy
- aws_iam_policies
- aws_iam_policy
- aws_iam_role
- aws_iam_roles
- aws_iam_root_user
- aws_iam_saml_provider
- aws_iam_saml_providers
- aws_iam_user
- aws_iam_users
- aws_kms_key
- aws_kms_keys
- aws_lambda
- aws_lambdas
- aws_launch_configuration
- aws_organizations_member
- aws_rds_cluster
- aws_rds_clusters
- aws_rds_instance
- aws_rds_instances
- aws_region
- aws_regions
- aws_route_table
- aws_route_tables
- aws_s3_bucket
- aws_s3_bucket_object
- aws_s3_buckets
- aws_security_group
- aws_security_groups
- aws_sns_subscription
- aws_sns_topic
- aws_sns_topics
- aws_sqs_queue
- aws_sqs_queues
- aws_sts_caller_identity
- aws_subnet
- aws_subnets
- aws_transit_gateway
- aws_vpc
- aws_vpcs
Examples
Ensure Security Groups Disallow FTP
For disallowing FTP we check that there is no ingress from 0.0.0.0/0 on port 21. The below sample control loops across all regions, checking all security groups for the account:
title 'Test AWS Security Groups Across All Regions For an Account Disallow FTP'
control 'aws-multi-region-security-group-ftp-1.0' do
impact 1.0
title 'Ensure AWS Security Groups disallow FTP ingress from 0.0.0.0/0.'
aws_regions.region_names.each do |region|
aws_security_groups(aws_region: region).group_ids.each do |security_group_id|
describe aws_security_group(aws_region: region, group_id: security_group_id) do
it { should exist }
it { should_not allow_in(ipv4_range: '0.0.0.0/0', port: 21) }
end
end
end
end
Test that an EC2 instance is running & using the correct AMI
describe aws_ec2_instance(name: 'ProdWebApp') do
it { should be_running }
its('image_id') { should eq 'ami-27a58d5c' }
end
Ensure all AWS Users have MFA enabled
describe aws_iam_users.where( has_mfa_enabled: false) do
it { should_not exist }
end
Properties Applying to All InSpec AWS Resources
aws_region
In order to provide multi-region support, the aws_region
property may be specified to a resource. This will only have an effect on AWS resources that have a region dependency e.g. security groups. One special-case worth mentioning is the aws_s3_bucket
resource that updates its region based on the location returned from S3.
The aws_regions
resource can be used to loop across all regions e.g.
aws_regions.region_names.each do |region|
<use region in other resources here>
end
aws_endpoint
A custom endpoint URL can optionally be specified to resources for testing other compatible providers. This propagates to the AWS client configuration. An example is provided below for Minio S3 compatible buckets e.g.
title 'Test For Minio Buckets Existing at a Custom Endpoint'
endpoint = input(:minio_server, value: 'http://127.0.0.1:9000', description: 'The Minio server custom endpoint.')
control 'minio-buckets-1.0' do
impact 1.0
title 'Ensure Minio buckets exist.'
describe aws_s3_bucket(aws_endpoint: endpoint, bucket_name: 'miniobucket') do
it { should exist }
end
describe aws_s3_bucket(aws_endpoint: endpoint, bucket_name: 'notthere') do
it { should_not exist }
end
end
Note that InSpec AWS assumes full compatibility with the underlying AWS SDK and unsupported operations will cause failures. Therefore, depending on the external provider implementation your mileage may vary!
aws_retry_limit
and aws_retry_backoff
In certain cases AWS may implement rate limiting. In order to mitigate this issue the Retry Limit
and Retry Backoff
can be set in two ways:
1) Environment Variables
Setting AWS_RETRY_LIMIT
and AWS_RETRY_BACKOFF
environment variables will be implemented at session level.
export AWS_RETRY_LMIIT=5
export aws_retry_limit=5
Note environment variables are case insensitive.
2) Inspec Control
Inspec AWS resources now support setting the Retry Limit and Retry Backoff at control level as shown below.
describe aws_config_recorder(recorder_name: aws_config_recorder_name, aws_retry_limit=5, aws_retry_backoff=5) do
it { should exist }
its('recorder_name') { should eq aws_config_recorder_name }
end
#####The aws_retry_limit
and aws_retry_backoff
precedence:
- Set at Inspec control level.
- Set at Environment level.
Retry Limit and Retry Backoff documentation
Environment and Setup Notes
Train and InSpec Dependencies
InSpec AWS depends on version 3 of the AWS SDK that is provided via Train AWS. InSpec depends on Train AWS so this is not explicitly listed in the Gemfile here.
Running a Sample Profile Using Docker
A Dockerfile
is provided in the root of this resource pack repository.
cd inspec-aws
docker build -t inspec-aws -f Dockerfile .
docker run -it inspec-aws /bin/bash
export AWS_ACCESS_KEY_ID=<your creds here>
export AWS_SECRET_ACCESS_KEY=<your creds here>
bundle exec inspec exec sample_profile -t aws://
If successful, output similar to this should be seen:
# bundle exec inspec exec sample_profile -t aws://
Profile: AWS InSpec Profile (InSpec AWS Sample Profile)
Version: 0.1.0
Target: aws://us-east-1
✔ aws-vpcs-multi-region-status-check: Check AWS VPCs in all regions have status "available"
✔ VPC vpc-1234abcd in eu-north-1 should exist
✔ VPC vpc-1234abcd in eu-north-1 should be available
<curtailing> ...
Profile: Amazon Web Services Resource Pack (inspec-aws)
Version: 0.1.0
Target: aws://us-east-1
No tests executed.
Profile Summary: 1 successful control, 0 control failures, 0 controls skipped
Test Summary: 50 successful, 0 failures, 0 skipped
Running the unit and integration tests
Run the linting and unit tests via the below:
$ bundle exec rake
Running RuboCop...
Inspecting 2 files
..
2 files inspected, no offenses detected
/Users/spaterson/.rubies/ruby-2.4.3/bin/ruby -I"lib:libraries:test/unit" -I"/Users/spaterson/.rubies/ruby-2.4.3/lib/ruby/gems/2.4.0/gems/rake-12.3.1/lib" "/Users/spaterson/.rubies/ruby-2.4.3/lib/ruby/gems/2.4.0/gems/rake-12.3.1/lib/rake/rake_test_loader.rb" "test/unit/resources/aws_vpc_test.rb"
Run options: --seed 64195
# Running:
.................
Fabulous run in 0.253300s, 67.1141 runs/s, 51.3225 assertions/s.
17 runs, 13 assertions, 0 failures, 0 errors, 0 skips
bundle exec inspec check /Users/spaterson/Documents/workspace/aws/inspec-aws
Location: /Users/spaterson/Documents/workspace/aws/inspec-aws
Profile: inspec-aws
Controls: 0
Timestamp: 2018-11-29T15:02:33+00:00
Valid: true
! No controls or tests were defined.
Summary: 0 errors, 1 warnings
Conversely run using within a docker container, using the make file:
To run unit tests and linting:
make sure
Will result in...
make sure
docker-compose run --rm builder
Running RuboCop...
Inspecting 68 files
....................................................................
68 files inspected, no offenses detected
/usr/local/bin/ruby -I"lib:libraries:test/unit" -I"/usr/local/bundle/gems/rake-12.3.3/lib" "/usr/local/bundle/gems/rake-12.3.3/lib/rake/rake_test_loader.rb" "test/unit/resources/aws_alb_test.rb" "test/unit/resources/aws_auto_scaling_group_test.rb" "test/unit/resources/aws_cloudformation_stack_test.rb" "test/unit/resources/aws_cloudtrail_trail_test.rb" "test/unit/resources/aws_cloudtrail_trails_test.rb" "test/unit/resources/aws_cloudwatch_alarm_test.rb" "test/unit/resources/aws_cloudwatch_log_metric_filter_test.rb" "test/unit/resources/aws_config_delivery_channel_test.rb" "test/unit/resources/aws_config_recorder_test.rb" "test/unit/resources/aws_dynamodb_table_test.rb" "test/unit/resources/aws_ebs_volume_test.rb" "test/unit/resources/aws_ebs_volumes_test.rb" "test/unit/resources/aws_ec2_instance_test.rb" "test/unit/resources/aws_ec2_instances_test.rb" "test/unit/resources/aws_ecr_test.rb" "test/unit/resources/aws_ecs_cluster_test.rb" "test/unit/resources/aws_eks_cluster_test.rb" "test/unit/resources/aws_eks_clusters_test.rb" "test/unit/resources/aws_elb_test.rb" "test/unit/resources/aws_flow_log_test.rb" "test/unit/resources/aws_hosted_zones_test.rb" "test/unit/resources/aws_iam_account_alias_test.rb" "test/unit/resources/aws_iam_group_test.rb" "test/unit/resources/aws_iam_password_policy_test.rb" "test/unit/resources/aws_iam_policy_test.rb" "test/unit/resources/aws_iam_role_test.rb" "test/unit/resources/aws_iam_root_user_test.rb" "test/unit/resources/aws_iam_saml_provider_test.rb" "test/unit/resources/aws_iam_user_test.rb" "test/unit/resources/aws_kms_key_test.rb" "test/unit/resources/aws_kms_keys_test.rb" "test/unit/resources/aws_launch_configuration_test.rb" "test/unit/resources/aws_organizations_member_test.rb" "test/unit/resources/aws_rds_instance_test.rb" "test/unit/resources/aws_rds_instances_test.rb" "test/unit/resources/aws_region_test.rb" "test/unit/resources/aws_regions_test.rb" "test/unit/resources/aws_route_table_test.rb" "test/unit/resources/aws_route_tables_test.rb" "test/unit/resources/aws_s3_bucket_object_test.rb" "test/unit/resources/aws_s3_bucket_test.rb" "test/unit/resources/aws_s3_buckets_test.rb" "test/unit/resources/aws_security_group_test.rb" "test/unit/resources/aws_security_groups_test.rb" "test/unit/resources/aws_sns_subscription_test.rb" "test/unit/resources/aws_sns_topic_test.rb" "test/unit/resources/aws_sns_topics_test.rb" "test/unit/resources/aws_sqs_queue_test.rb" "test/unit/resources/aws_sts_caller_identity_test.rb" "test/unit/resources/aws_subnet_test.rb" "test/unit/resources/aws_subnets_test.rb" "test/unit/resources/aws_vpc_test.rb" "test/unit/resources/aws_vpcs_test.rb"
Run options: --seed 22010
# Running:
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Fabulous run in 4.613042s, 155.6457 runs/s, 172.3375 assertions/s.
718 runs, 795 assertions, 0 failures, 0 errors, 0 skips
To run the full suite of tests run
make doubly_sure
This will run the unit tests, create the target infrastructure and run the intergration tests. If successful, will automatically destroy everything. If it fails it will keep the environment up, testing then can be achieved by running:
make int_test
To keep things simple the aws credentials can either be supplied via environmental variables or files located on ./aws in this folder.
This just requires docker, docker-compose and make, see Three Musketeers Pattern for details
Running the integration tests (after setup_integration_tests
):
$ bundle exec rake test:run_integration_tests
----> Run
bundle exec inspec exec test/integration/verify --attrs test/integration/build/aws-inspec-attributes.yaml; rc=$?; if [ $rc -eq 0 ] || [ $rc -eq 101 ]; then exit 0; else exit 1; fi
Profile: Amazon Web Services Resource Pack (inspec-aws)
Version: 0.1.0
Target: aws://eu-west-2
✔ aws-vpc-1.0: Ensure AWS VPC has the correct properties.
✔ VPC vpc-0373aeb7284407ffd should exist
✔ VPC vpc-0373aeb7284407ffd should not be default
✔ VPC vpc-0373aeb7284407ffd cidr_block should eq "10.0.0.0/27"
✔ VPC vpc-0373aeb7284407ffd instance_tenancy should eq "dedicated"
✔ VPC vpc-0373aeb7284407ffd vpc_id should eq "vpc-0373aeb7284407ffd"
✔ VPC vpc-0373aeb7284407ffd state should eq "available"
✔ VPC vpc-0373aeb7284407ffd dhcp_options_id should eq "dopt-f557819d"
✔ VPC default should exist
✔ VPC default should be default
✔ VPC default vpc_id should eq "vpc-1ea06476"
✔ VPC vpc-0373aeb7284407ffd should exist
✔ VPC vpc-0373aeb7284407ffd should not be default
✔ VPC vpc-0373aeb7284407ffd vpc_id should eq "vpc-0373aeb7284407ffd"
...
Profile: Amazon Web Services Resource Pack (inspec-aws)
Version: 0.1.0
Target: aws://eu-west-2
No tests executed.
Profile Summary: 50 successful controls, 0 control failures, 3 controls skipped
Test Summary: 602 successful, 0 failures, 18 skipped
FAQ
Failure running "inspec exec" on my AWS profile
If an error such occurs when running "inspec exec" on a newly created AWS profile, check that the AWS transport is being specified as below:
$ inspec exec . -t aws://
If a method missing error occurs and all the steps documented above have been followed try running the following command within the profile directory:
inspec vendor --overwrite
Support
The InSpec AWS resources are community supported. For bugs and features, please open a github issue and label it appropriately.
Kudos
This work builds on the InSpec 2 AWS resources that were originally shipped as part of InSpec.