diff --git a/README.md b/README.md index 40d790a..58ab1a5 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ With any engineering project, design decisions are made based on the requirement - Never pass `-u`, `-l`, `-json`, or `-o` flag to this lambda function but you can pass any other nuclei arguments you like - Nuclei refuses to not write to `$HOME/.config` so the `HOME`, which is not a writable filesystem with lambda, is set to `/tmp` which can cause warm starts to have the same filesystem and perhaps poison future configurations - Lambda function in golang is rebuilt on every apply for ease of development +- When configuration files are updated, you might have to destroy and recreate the infrastructure ### Event Json diff --git a/artifacts.tf b/artifacts.tf index 069afed..9281e6f 100644 --- a/artifacts.tf +++ b/artifacts.tf @@ -1,3 +1,7 @@ +provider "github" { + token = var.github_token +} + # Download nuclei binary and templates resource "null_resource" "download_nuclei" { triggers = { @@ -9,13 +13,21 @@ resource "null_resource" "download_nuclei" { } } +# Private templates download from github +data "github_release" "templates" { + repository = var.github_repository + owner = var.github_owner + retrieve_by = "tag" + release_tag = var.release_tag +} + resource "null_resource" "download_templates" { triggers = { - version = var.nuclei_templates_url + version = var.release_tag } provisioner "local-exec" { - command = "curl -o ${path.module}/src/nuclei-templates.zip -L ${var.nuclei_templates_url}" + command = "curl -o ${path.module}/src/nuclei-templates.zip -L ${data.github_release.templates.zipball_url}" } } @@ -36,18 +48,18 @@ resource "aws_s3_object" "upload_templates" { source = "${path.module}/src/nuclei-templates.zip" } - -# Nuclei Config File `-config /opt/nuclei-config.yaml` -data "archive_file" "report_config" { +# Nuclei configuration files +data "archive_file" "nuclei_config" { type = "zip" - source_file = "config/report-config.yaml" - output_path = "report-config.zip" + source_dir = "${path.module}/config" + output_path = "nuclei-configs.zip" } resource "aws_s3_object" "upload_config" { + depends_on = [data.archive_file.nuclei_config] bucket = aws_s3_bucket.bucket.id - key = "report-config.zip" - source = "${path.module}/report-config.zip" + key = "nuclei-configs.zip" + source = "${path.module}/nuclei-configs.zip" } # Build the lambda function to execute binary diff --git a/bucket.tf b/bucket.tf index 2528c17..a322daf 100644 --- a/bucket.tf +++ b/bucket.tf @@ -3,9 +3,8 @@ resource "aws_s3_bucket" "bucket" { bucket = "${var.project_name}-artifacts" tags = var.tags - lifecycle { - prevent_destroy = true - } + # Delete all objects in the bucket before deleting the bucket + force_destroy = true } #tfsec:ignore:aws-s3-encryption-customer-key diff --git a/config/nuclei-config.yaml b/config/nuclei-config.yaml new file mode 100644 index 0000000..3d91261 --- /dev/null +++ b/config/nuclei-config.yaml @@ -0,0 +1,32 @@ +# Headers to include with all HTTP request +header: + - 'X-BugBounty-Hacker: github/nuclearpond' + +# Directory based template execution +templates: + - dns/ + +# Tags based template execution +# tags: exposures,cve + +# Template Filters +# tags: exposures,cve +# author: geeknik,pikpikcu,dhiyaneshdk +# severity: critical,high,medium + +# Template Allowlist +# include-tags: dos,fuzz # Tag based inclusion (allows overwriting nuclei-ignore list) +# include-templates: # Template based inclusion (allows overwriting nuclei-ignore list) +# - vulnerabilities/xxx +# - misconfiguration/xxxx + +# Template Denylist +# exclude-tags: info # Tag based exclusion +# exclude-templates: # Template based exclusion +# - vulnerabilities/xxx +# - misconfiguration/xxxx + +# Rate Limit configuration +rate-limit: 500 +bulk-size: 50 +concurrency: 50 \ No newline at end of file diff --git a/main.tf b/main.tf index 845f73b..81ba2fe 100644 --- a/main.tf +++ b/main.tf @@ -1,10 +1,11 @@ # tfsec:ignore:aws-lambda-enable-tracing resource "aws_lambda_function" "function" { + depends_on = [aws_lambda_layer_version.layer, aws_lambda_layer_version.templates_layer, aws_lambda_layer_version.configs_layer] filename = "lambda.zip" function_name = "${var.project_name}-function" role = aws_iam_role.lambda_role.arn - layers = [aws_lambda_layer_version.layer.arn, aws_lambda_layer_version.templates_layer.arn] + layers = [aws_lambda_layer_version.layer.arn, aws_lambda_layer_version.templates_layer.arn, aws_lambda_layer_version.configs_layer.arn] handler = "main" runtime = "go1.x" @@ -47,19 +48,14 @@ resource "aws_lambda_layer_version" "templates_layer" { compatible_runtimes = ["go1.x"] } -# Trigger -# resource "aws_cloudwatch_event_rule" "trigger" { -# name = "${var.project_name}-trigger" -# description = "Trigger lambda function for ${var.alert_name} at ${var.cron_expression}" -# schedule_expression = "cron(${var.cron_expression})" -# } - -# resource "aws_lambda_permission" "allow_cloudwatch" { -# action = "lambda:InvokeFunction" -# function_name = aws_lambda_function.function.arn -# principal = "events.amazonaws.com" -# source_arn = aws_cloudwatch_event_rule.trigger.arn -# } +# Layer for nuclei configs +resource "aws_lambda_layer_version" "configs_layer" { + depends_on = [aws_s3_object.upload_config] + layer_name = "${var.project_name}-nuclei-config-layer" + s3_bucket = aws_s3_bucket.bucket.id + s3_key = "nuclei-configs.zip" + compatible_runtimes = ["go1.x"] +} # tfsec:ignore:aws-cloudwatch-log-group-customer-key resource "aws_cloudwatch_log_group" "log_group" { diff --git a/src/main.go b/src/main.go index 06a3adc..5622ea1 100644 --- a/src/main.go +++ b/src/main.go @@ -147,7 +147,7 @@ func runNuclei(args []string) (string, error) { cmd := exec.Command(nucleiBinary, args...) output, err := cmd.CombinedOutput() if err != nil { - return "", err + return string(output), err } return string(output), nil } diff --git a/variables.tf b/variables.tf index ef23db9..c062aae 100644 --- a/variables.tf +++ b/variables.tf @@ -2,18 +2,10 @@ variable "project_name" { description = "Name of the project to create and must be unique as S3 bucket names are global" } -# You should check the latest version of Nuclei -# https://github.com/projectdiscovery/nuclei/releases/ +# Nuclei binary configuration variable "nuclei_version" { description = "Nuclei version to use" - default = "2.8.6" -} - -# You can also use private templates by download zip of your repo, copy url from downloaded file, and paste the url in here including the token -variable "nuclei_templates_url" { - description = "Nuclei templates url to use" - sensitive = true - default = "https://github.com/projectdiscovery/nuclei-templates/archive/refs/tags/v9.3.4.zip" + default = "2.8.7" } variable "nuclei_arch" { @@ -21,6 +13,28 @@ variable "nuclei_arch" { default = "linux_amd64" } +# Private Templates +variable "github_repository" { + description = "Github repository to use for templates" + default = "nuclei-templates" +} + +variable "github_owner" { + description = "Github owner to use for templates" + default = "projectdiscovery" +} + +variable "release_tag" { + description = "Github release tag to use for templates" + default = "v9.3.4" +} + +variable "github_token" { + description = "Github token to use for private templates, leave empty if you don't need private templates" + default = "" + sensitive = true +} + variable "nuclei_timeout" { type = number description = "Lambda function timeout" diff --git a/versions.tf b/versions.tf index 8ea838b..25df02d 100644 --- a/versions.tf +++ b/versions.tf @@ -3,7 +3,19 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = "4.50.0" + } + null = { + source = "hashicorp/null" + version = "3.2.1" + } + archive = { + source = "hashicorp/archive" + version = "2.2.0" + } + github = { + source = "hashicorp/github" + version = "5.14.0" } } } \ No newline at end of file