Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .checkov.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://www.checkov.io/2.Basics/CLI%20Command%20Reference.html
framework:
- terraform
directory:
- terraform
compact: true
soft-fail: false
40 changes: 40 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: terraform

on:
push:
pull_request:

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Checkov
run: pip install --upgrade pip checkov

- name: Checkov
run: checkov --config .checkov.yaml

- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.9.0"

- name: Terraform fmt
working-directory: terraform
run: terraform fmt -check -recursive

- name: Terraform init
working-directory: terraform
run: terraform init -backend=false

- name: Terraform validate
working-directory: terraform
run: terraform validate
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Terraform
.terraform/
.terraform.lock.hcl
*.tfstate
*.tfstate.*
*.tfvars
!*.tfvars.example
!terraform/env/*.tfvars
crash.log
crash.*.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc

# OS
.DS_Store
61 changes: 60 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,61 @@
# honeynet
Develop a scalable, cloud-native honeypot deployment framework that leverages Terraform to provision and manage honeypot instances across multiple geographic regions.

Scalable, cloud-native honeypot deployment using Terraform. The goal is to provision isolated sensors in multiple regions—similar in spirit to [GeoDnsScanner](https://github.com/c2siorg/GeoDnsScanner)’s distributed layout, but for honeypots—so teams can collect behavioral telemetry and compare regional attack patterns.

## What’s in this repo

- **Terraform (AWS):** Dedicated VPC, EC2 sensor, security group exposing a Cowrie SSH honeypot on **port 2222**, optional VPC flow logs to CloudWatch, SSM-capable instance role for administration without a bastion.
- **Per-region vars:** `terraform/env/*.tfvars` for North America, Europe, and Asia-Pacific example regions.
- **Scripts:** `scripts/validate.sh` (fmt + validate), `scripts/deploy-region.sh` (apply with a chosen env file).

## Prerequisites

- Terraform `>= 1.5`, AWS CLI configured with credentials that can create VPC, EC2, IAM, and CloudWatch resources.
- Understand that a honeypot **will** receive untrusted traffic; use a dedicated AWS account or strict account guardrails.

## Quick start (single region)

```bash
cd terraform
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars (region, labels, tighten admin_ssh_cidr, optional key_name)

terraform init
terraform plan
terraform apply
```

Outputs include the public IP and the CloudWatch log group for flow logs (if enabled). Connect scanners to the honeypot on **TCP 2222** (Cowrie).

## Multi-region deployment

Deploy **once per region** with separate state (recommended: use a remote backend per region so state files do not overwrite each other):

```bash
./scripts/deploy-region.sh terraform/env/us-east-1.tfvars
./scripts/deploy-region.sh terraform/env/eu-west-1.tfvars
./scripts/deploy-region.sh terraform/env/ap-southeast-1.tfvars
```

Use different backend keys or directories so each region keeps its own `terraform.tfstate`.

## Contributing

1. Open or pick an issue on the project’s GitHub repository.
2. Branch from `main`, implement with focused commits.
3. Run `./scripts/validate.sh` before pushing. This runs **Checkov** (install: `pip install checkov`) on `terraform/` and then `terraform fmt` / `validate`. CI runs the same checks.
4. Open a PR describing behavior change and any new variables.

### IaC policy (Checkov)

Terraform is scanned with [Checkov](https://www.checkov.io/). Intentional honeypot exceptions use `# checkov:skip=...` comments with a short justification (for example global ingress on the honeypot port, public subnet, or wide egress for updates). Prefer fixing the underlying finding when it does not conflict with the sensor’s purpose.

## Security notes

- Restrict `admin_ssh_cidr` and prefer SSM Session Manager (`AmazonSSMManagedInstanceCore` is attached) instead of wide SSH.
- Review AWS costs (NAT not used; instances are in a public subnet with public IP for inbound honeypot traffic).
- This is **not** legal or compliance advice; ensure deployment aligns with your policies and local law.

## License

See [LICENSE](LICENSE).
9 changes: 9 additions & 0 deletions scripts/deploy-region.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Deploy one regional stack using env/*.tfvars (separate state file per region recommended).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_FILE="${1:?Usage: $0 terraform/env/<region>.tfvars [terraform plan|apply args]}"

cd "$ROOT/terraform"
terraform init
terraform apply -var-file="$ROOT/$ENV_FILE" "${@:2}"
13 changes: 13 additions & 0 deletions scripts/validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if command -v checkov >/dev/null 2>&1; then
(cd "$ROOT" && checkov --config .checkov.yaml)
else
echo "checkov not found; install with: pip install checkov" >&2
echo "Skipping Checkov; running Terraform only." >&2
fi
cd "$ROOT/terraform"
terraform init -backend=false
terraform fmt -recursive -check
terraform validate
12 changes: 12 additions & 0 deletions terraform/backend.tf.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Optional: copy to backend.tf and create the S3 bucket + DynamoDB table first.
# See docs/ISSUES.md (issue #4) for remote state setup.

# terraform {
# backend "s3" {
# bucket = "your-org-honeynet-tfstate"
# key = "honeynet/us-east-1/terraform.tfstate"
# region = "us-east-1"
# encrypt = true
# dynamodb_table = "honeynet-tf-locks"
# }
# }
2 changes: 2 additions & 0 deletions terraform/env/ap-southeast-1.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws_region = "ap-southeast-1"
region_label = "apac"
2 changes: 2 additions & 0 deletions terraform/env/eu-west-1.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws_region = "eu-west-1"
region_label = "eu"
2 changes: 2 additions & 0 deletions terraform/env/us-east-1.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws_region = "us-east-1"
region_label = "na"
13 changes: 13 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module "honeypot" {
source = "./modules/aws-honeypot"

name_prefix = var.name_prefix
region_label = var.region_label
instance_type = var.instance_type
admin_ssh_cidr = var.admin_ssh_cidr
key_name = var.key_name
honeypot_image = var.honeypot_image

enable_flow_logs = var.enable_flow_logs
flow_logs_retention_days = var.flow_logs_retention_days
}
42 changes: 42 additions & 0 deletions terraform/modules/aws-honeypot/compute.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
data "aws_ami" "al2023" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["al2023-ami-*-kernel-*-x86_64"]
}
}

resource "aws_instance" "honeypot" {
ami = data.aws_ami.al2023.id
instance_type = var.instance_type
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.honeypot.id]
iam_instance_profile = aws_iam_instance_profile.ec2.name

monitoring = true
ebs_optimized = true

key_name = var.key_name != null && var.key_name != "" ? var.key_name : null

user_data = templatefile("${path.module}/user_data.sh.tpl", {
honeypot_image = var.honeypot_image
})

metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
}

root_block_device {
volume_type = "gp3"
volume_size = 30
encrypted = true
}

tags = {
Name = "${local.name}-sensor"
Role = "honeypot"
}
}
66 changes: 66 additions & 0 deletions terraform/modules/aws-honeypot/flow_logs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
resource "aws_cloudwatch_log_group" "flow" {
count = var.enable_flow_logs ? 1 : 0

# checkov:skip=CKV_AWS_338: Retention is set via flow_logs_retention_days; one-year retention is optional and costly for high-volume flow logs.
# checkov:skip=CKV_AWS_158: CMK encryption can be added when an KMS key ARN is available; accept default encryption for baseline module.
name = "/aws/vpc/${local.name}/flow"
retention_in_days = var.flow_logs_retention_days
}

resource "aws_iam_role" "flow_logs" {
count = var.enable_flow_logs ? 1 : 0

name = "${local.name}-flowlogs-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "vpc-flow-logs.amazonaws.com"
}
}]
})
}

resource "aws_iam_role_policy" "flow_logs" {
count = var.enable_flow_logs ? 1 : 0

name = "${local.name}-flowlogs-policy"
role = aws_iam_role.flow_logs[0].id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
]
Resource = "${aws_cloudwatch_log_group.flow[0].arn}:*"
}, {
Effect = "Allow"
Action = [
"logs:DescribeLogGroups"
]
Resource = aws_cloudwatch_log_group.flow[0].arn
}]
})
}

resource "aws_flow_log" "this" {
count = var.enable_flow_logs ? 1 : 0

vpc_id = aws_vpc.this.id
traffic_type = "ALL"
iam_role_arn = aws_iam_role.flow_logs[0].arn
log_destination = aws_cloudwatch_log_group.flow[0].arn
log_destination_type = "cloud-watch-logs"
max_aggregation_interval = 60

tags = {
Name = "${local.name}-flow"
}
}
28 changes: 28 additions & 0 deletions terraform/modules/aws-honeypot/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
resource "aws_iam_role" "ec2" {
name = "${local.name}-ec2-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})

tags = {
Name = "${local.name}-ec2-role"
}
}

resource "aws_iam_role_policy_attachment" "ssm" {
role = aws_iam_role.ec2.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "ec2" {
name = "${local.name}-ec2-profile"
role = aws_iam_role.ec2.name
}
17 changes: 17 additions & 0 deletions terraform/modules/aws-honeypot/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
output "public_ip" {
value = aws_instance.honeypot.public_ip
description = "Public IP for honeypot traffic (SSH honeypot on port 2222)."
}

output "instance_id" {
value = aws_instance.honeypot.id
}

output "vpc_id" {
value = aws_vpc.this.id
}

output "flow_log_group_name" {
value = try(aws_cloudwatch_log_group.flow[0].name, null)
description = "CloudWatch log group for VPC flow logs."
}
7 changes: 7 additions & 0 deletions terraform/modules/aws-honeypot/user_data.sh.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash
set -euxo pipefail
dnf install -y docker
systemctl enable --now docker
docker pull ${honeypot_image}
docker rm -f cowrie 2>/dev/null || true
docker run -d --name cowrie --restart unless-stopped -p 2222:2222 ${honeypot_image}
33 changes: 33 additions & 0 deletions terraform/modules/aws-honeypot/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
variable "name_prefix" {
type = string
}

variable "region_label" {
type = string
}

variable "instance_type" {
type = string
}

variable "admin_ssh_cidr" {
type = string
description = "CIDR allowed for SSH to port 22 on the host (optional admin path)."
}

variable "key_name" {
type = string
default = null
}

variable "honeypot_image" {
type = string
}

variable "enable_flow_logs" {
type = bool
}

variable "flow_logs_retention_days" {
type = number
}
Loading