This lambda function is meant to allow you to execute InSpec profiles in a serverless fashion. It strives to be as similar as it can be to how you would normally run inspec exec
on your CLI, while also adding some useful functionality specific to AWS.
- How can I deploy this lambda function?
- Scan Configuration Examples
- What does the
results_buckets
event attribute do? - What does the
command
event attribute do? - What does the
resources
event attribute do? - What does the
env
event attribute do? - What does the
eval_tags
event attribute do? - What does the
results_name
event attribute do? - What does the
tmp_ssm_ssh_key
event attribute do? - What does the
ssm_port_forward
event attribute do? - How can I run profiles with dependencies in an offline environment?
- How do I set up an SSM managed instance?
- Scheduling Recurring Scans
For instructions on how to configure and deploy this Lambda function with Terraform, see TF_EXAMPLE.md
These are examples of the JSON that can be passed into the lambda event to obtain a successful scan.
You can find more details on these configurations and additional configuration options in this README.
Note that if you are running InSpec AWS scans, then the lambda's IAM profile must have suffient permissions to analyze your environment.
{
"command": "inspec exec https://github.com/mitre/aws-foundations-cis-baseline/archive/master.tar.gz -t aws://",
"results_name": "aws-foundations-cis-baseline",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,AwsCisBaseline,AWS"
}
{
"command": "inspec exec /tmp/redhat-enterprise-linux-7-stig-baseline-2.6.6.tar.gz -t ssh://ec2-user@ec2-15-200-235-74.us-gov-west-1.compute.amazonaws.com -i /tmp/id_rsa --sudo --input=disable_slow_controls=true",
"results_name": "redhat-enterprise-linux-7-stig-baseline-inspec-rhel7-test",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,RHEL7,inspec-rhel7-test,SSH",
"resources": [
{
"local_file_path": "/tmp/redhat-enterprise-linux-7-stig-baseline-2.6.6.tar.gz",
"source_aws_s3_bucket": "inspec-profiles-bucket-dev",
"source_aws_s3_key": "redhat-enterprise-linux-7-stig-baseline-2.6.6.tar.gz"
},
{
"local_file_path": "/tmp/id_rsa",
"source_aws_ssm_parameter_key": "/inspec/rhel-7-test/id_rsa"
}
]
}
The --proxy_command
command line argument is tunneling the session through SSM.
The tmp_ssm_ssh_key
defines attributes around a temporary SSH key that will be generated and disposed of during the lifetime of the function execution, which provides temporary access (in a 60 second window) to the SSM managed instance for the function.
{
"command": "inspec exec https://github.com/mitre/redhat-enterprise-linux-7-stig-baseline/archive/master.tar.gz -t ssh://ssm-user@i-00f1868f8f3b4eb03 -i /tmp/tmp_ssh_key --input=disable_slow_controls=true --proxy-command='sh -c \"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\"'",
"results_name": "redhat-enterprise-linux-7-stig-baseline-inspec-rhel7-test",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,RHEL7,inspec-rhel7-test,SSH-SSM",
"tmp_ssm_ssh_key": {
"host": "i-00f1868f8f3b4eb03",
"user": "ssm-user",
"key_name": "tmp_ssh_key"
}
}
{
"command": "inspec exec https://github.com/mitre/redhat-enterprise-linux-7-stig-baseline/archive/master.tar.gz -t awsssm://i-00f1868f8f3b4eb03 --input=disable_slow_controls=true",
"results_name": "redhat-enterprise-linux-7-stig-baseline-inspec-rhel7-test",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,RHEL7,inspec-rhel7-test,AWSSSM"
}
Note that the target is set to winrm://localhost
because port forwarding is being set up with the ssm_port_forward
event property.
{
"command": "inspec exec /tmp/microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz -t winrm://localhost --password $WIN_PASS",
"results_name": "windows-server-2019-stig-baseline-inspec-win2019-test",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,WinSvr2019,inspec-win2019-test,WinRM",
"resources": [
{
"local_file_path": "/tmp/microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz",
"source_aws_s3_bucket": "inspec-profiles-bucket-dev",
"source_aws_s3_key": "microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz"
},
{
"env_variable": "WIN_PASS",
"source_aws_secrets_manager_secret_name": "/inspec/inspec-win2019-test/password"
}
],
"ssm_port_forward": {
"instance_id": "i-0e35ab216355084ee",
"ports": [5985, 5986]
}
}
{
"command": "inspec exec /tmp/microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz -t awsssm://i-00f1868f8f3b4eb03",
"results_name": "windows-server-2019-stig-baseline-inspec-win2019-test",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,WinSvr2019,inspec-win2019-test,AWSSSM",
"resources": [
{
"local_file_path": "/tmp/microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz",
"source_aws_s3_bucket": "inspec-profiles-bucket-dev",
"source_aws_s3_key": "microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz"
},
],
}
{
"command": "inspec exec https://gitlab.dsolab.io/scv-content/inspec/kubernetes/baselines/k8s-cluster-stig-baseline/-/archive/master/k8s-cluster-stig-baseline-master.tar.gz -t k8s://",
"results_name": "k8s-cluster-stig-baseline-dev-cluster",
"results_buckets": [
"inspec-results-bucket-dev"
],
"eval_tags": "ServerlessInspec,k8s",
"resources": [
{
"local_file_path": "/tmp/kube/config",
"source_aws_s3_bucket": "inspec-profiles-bucket-dev",
"source_aws_s3_key": "kube-dev/config"
},
{
"local_file_path": "/tmp/kube/client.crt",
"source_aws_ssm_parameter_key": "/inspec/kube-dev/client_crt"
},
{
"local_file_path": "/tmp/kube/client.key",
"source_aws_ssm_parameter_key": "/inspec/kube-dev/client_key"
},
{
"local_file_path": "/tmp/kube/ca.crt",
"source_aws_ssm_parameter_key": "/inspec/kube-dev/ca_crt"
}
],
"env": {
"KUBECONFIG": "/tmp/kube/config"
}
}
Database scans have not been tested yet with this lambda function.
"https://github.com/mitre/aws-rds-crunchy-data-postgresql-9-stig-baseline"
The results_buckets
event attribute defines S3 buckets that the function will push JSON results to.
If you DO NOT specify the results_buckets
parameter in the lambda event, then the results will just be logged to CloudWatch. If you DO specify the results_buckets
parameter in the lambda event, then the lambda will attempt to save the results JSON to the S3 bucket under unprocessed/*
. The format of the JSON is meant to be a incomplete API call to push results to a Heimdall Server and looks like this:
{
"data": {}, // this contains the HDF results
"eval_tags": "<eval_tags from event attribute>"
}
The results_bucket
event parameter format looks like the following:
{
"...": "...",
"results_buckets": [
"inspec-results-bucket-dev"
]
}
The command
event attribute defines the inspec exec command that the lambda function will execute. Note that --show-progress --reporter cli json:<results_name>
will be appended to the end of the command
attribute before execution.
This MUST be an inspec exec
command. Read more about inspec exec here
{
"command": "inspec exec /tmp/redhat-enterprise-linux-7-stig-baseline-2.6.6.tar.gz -t ssh://root@host -i /tmp/id_rsa --sudo --input=disable_slow_controls=true",
"...": "..."
}
The resources
event attribute defines what files and/or environment variables are needed prior to executing the InSpec command. This might be a tar.gz of an InSpec profile stored in S3, a Windows password stored in SSM Parameter store, etc.
You may define a resources as needing to be downloaded as a local file on disk (must be located within /tmp/
), or as needing have their contents stored in an environment variable. Downloaded files and environment variable resources will be usable by your inspec exec ...
command.
{
"resources": [
{
"local_file_path": "/tmp/microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz",
"...": "...",
}
],
}
{
"resources": [
{
"env_variable": "WIN_PASS",
"...": "..."
}
],
}
The possible sources for the lamdba's resources
are defined below:
Resources may be downloaded from an S3 bucket. Ensure that you lambda's IAM role has s3:getObject
permissions for the desired bucket/object.
{
"resources": [
{
"...": "...",
"source_aws_s3_bucket": "inspec-profiles-bucket-dev",
"source_aws_s3_key": "microsoft-windows-server-2019-stig-baseline-1.3.10.tar.gz"
},
]
}
Resources may be fetched from AWS SSM Parameter Store. Ensure that you lambda's IAM role has kms:Decrypt
permissions for the secrets's KMS key and has ssm:GetParameter
permissions for the parameter.
{
"resources": [
{
"...": "...",
"source_aws_ssm_parameter_key": "/inspec/kube-dev/client_crt"
}
]
}
Resources may be fetched from AWS Secrets Manager. Ensure that you lambda's IAM role has kms:Decrypt
permissions for the secrets's KMS key and secretsmanager:GetSecretValue
permissions for the secret.
{
"resources": [
{
"...": "...",
"source_aws_secrets_manager_secret_name": "/inspec/kube-dev/client_crt"
}
]
}
The env
event attribute allows definition of static envrionment variables that are needed by the InSpec command. Note that you may not overwrite an existing environment varaible.
{
"...": "...",
"env": {
"KUBECONFIG": "/tmp/kube/config",
"OTHER_ENV": "value"
}
}
The eval_tags
event attribute allows definition of comma separated Heimdall eval_tags
that will be passed through to the results file.
{
"...": "...",
"eval_tags": "hostname,profile,etc"
}
The results_name
event attribute defines the filename for generated InSpec scan results. If this is not set, then the results_name
will default to unnamed_profile
.
{
"...": "...",
"results_name": "human-readable-name-of-my-results"
}
The tmp_ssm_ssh_key
event attribute allows the function to push temporary SSH keys to a linux-based SSM managed instance. These keys are generated as needed and are disposed of on the target machine after 60 seconds.
This method of InSpec scanning works with the following sequence of events:
- Generate a SSH key pair within the lambda function
- Use the train-awsssm plugin to send the public key material to
~/.ssh/authorized_keys
using SSM Send Command - Immedately queue another SSM Send Command to remove the key from
~/.ssh/authorized_keys
after 60 seconds - Start an SSH session using the generated key pair and execute the InSpec scan over SSH
Assumptions with this method:
- Scanning linux-based instances (i.e. not Windows)
- The instance has the following commands installed:
su
,mkdir
,touch
,echo
,sleep
,grep
,mv
- The user that runs "SSM Send Command" commands is priviledged to write to any user's
~/.ssh
directory (this should default to root unless explicitly changed)
This method is advantageous over using the awsssm://
transport by itself because invoking all InSpec commands over SSM Send Command is significantly slower than over SSH.
{
"...": "...",
"tmp_ssm_ssh_key": {
"host": "i-00f1868f8f3b4eb03",
"user": "ssm-user",
"key_name": "tmp_ssh_key"
}
}
The tmp_ssm_ssh_key
event attribute defines local ports to be forwarded to a specific SSM managed instance. This is useful if the lambda function does not have direct network access to the machine, but both the lambda and machine have access to SSM.
Note that forwarding local ports will mean that you will need to connect to localhost
for your inspec exec command (e.g., inspec exec ... -t winrm://localhost
)
{
"...": "...",
"ssm_port_forward": {
"instance_id": "i-0e35ab216355084ee",
"ports": [5985, 5986]
}
}
The recommendation for offline environments is to save vendored InSpec profiles to an S3 bucket.
git clone git@github.com:mitre/aws-foundations-cis-baseline.git
inspec vendor ./aws-foundations-cis-baseline && inspec archive ./aws-foundations-cis-baseline
# Then upload ./aws-foundations-cis-baseline.tar.gz to you S3 bucket
You can make an EC2 instance a SSM managed instance using this guide. This also requires that your EC2 instance has the SSM agent software installed on it. Some AWS-provided images already have this installed, but if it is not already installed on you instance then you can use this guide to get it installed.
The recommended way to set up recurring scans is to create an Event Rule within AWS CloudWatch.
You can do this via the AWS Console using the following steps:
- Navigate to the CloudWatch service and click
rules
on the left-hand side - Click the
Create Rule
button - Instead of
Event Pattern
, chosse theSchedule
option and either use CRON or a standard schedule confugration - Click
Add a Target
with the type ofLambda Function
and select the Serverless InSpec function - Expand
Configure Input
and chooseConstant (JSON text)
- Paste your configuration into the
Constant (JSON text)
field (this will be passed to the lambda event each time it is triggered)
© 2019-2021 The MITRE Corporation.
Approved for Public Release; Distribution Unlimited. Case Number 18-3678.
MITRE hereby grants express written permission to use, reproduce, distribute, modify, and otherwise leverage this software to the extent permitted by the licensed terms provided in the LICENSE.md file included with this project.
This software was produced for the U. S. Government under Contract Number W56KGU-18-D-0004, and is subject to Federal Acquisition Regulation Clause 52.227-14, Rights in Data-General.
No other use other than that granted to the U. S. Government, or to those acting on behalf of the U. S. Government under that Clause is authorized without the express written permission of The MITRE Corporation.