Decoys as Code (Terraform)

Manage your decoy estate the same way you manage the rest of your infrastructure. The Tripwire Terraform provider declares canary tokens and honeypots — and stands up a fully populated honeypot VM — in code, against the SaaS API or your self-hosted control server.

Configure the provider

Point the provider at your API and give it a scoped API key. Both fall back to TRIPWIRE_ENDPOINT / TRIPWIRE_API_KEY.

terraform {
  required_providers {
    tripwire = { source = "levantar-ai/tripwire" }
  }
}

provider "tripwire" {
  endpoint = "https://api.gettripwires.com"   # or your self-host control server
  api_key  = var.tripwire_api_key             # a scoped key
}

The tripwire_tripwire resource

A single decoy — a bait credential, a canary document, a token — minted through the same API the console and CLI use. Tag it with the instance it defends, and pair it with a lease (TTL) so the decoy self-reaps when the infrastructure it protects is gone.

resource "tripwire_tripwire" "pg" {
  name       = "prod-db-primary-creds"
  technology = "postgresql"
  ttl        = "720h"
  tags = {
    env           = "prod"
    "instance-id" = aws_instance.db.id
    "managed-by"  = "terraform"
  }
}

output "bait_username" { value = tripwire_tripwire.pg.tcp_username }

Because it's just Terraform, use for_each to blanket a fleet, modules to stamp a standard set per environment, and plan review so a human sees every change. Remove the block and run apply — the decoy is reaped.

Turnkey honeypot VMs — tripwire_honeypot_vm

A decoy is only half a trap; it isn't a trap until it sits somewhere an attacker will look. The tripwire_honeypot_vm resource does both halves in one apply: it launches an EC2 instance from the maintained Tripwire honeypot image, creates the decoys the box should serve, and writes each decoy's live connection into a believable file on the instance — a .pgpass, a Rails database.yml, an ~/.aws/credentials profile, an SSH config entry. Destroying the resource terminates the instance and reaps its decoys.

resource "tripwire_honeypot_vm" "app_tier" {
  region        = "eu-west-2"
  ami           = "ami-0honeypot0maintained"   # shared to your account by Tripwire
  instance_type = "t3.small"
  subnet_id     = aws_subnet.private.id
  security_group_ids = [aws_security_group.honeypot.id]   # must open the decoy ports

  tripwire {
    technology = "postgresql"
    name       = "prod-db-primary"
    place { path = "/srv/app/config/database.yml"  format = "rails_db" }
    place { path = "/home/deploy/.pgpass"          format = "pgpass"   }
  }

  tripwire {
    technology = "ssh"
    name       = "bastion"
    place { path = "/root/.ssh/config" format = "ssh_config" }
  }

  plant { type = "aws-credentials" name = "prod-deploy" }   # decoy + ~/.aws/credentials
}

output "decoys" { value = tripwire_honeypot_vm.app_tier.tripwire_ids }

The image is private — shared straight to your account, never public — so the box doesn't announce itself as a honeypot the way a public AMI would. The provider needs AWS credentials in its environment (standard AWS_* / profile chain) to launch the instance.

Three ways to declare the decoys

  1. Explicit — inline tripwire { place { … } } and plant { … } blocks (above).
  2. Manifestmanifest = file("fleet.yaml"), the same YAML shape the CLI consumes.
  3. AI (reserved)ai { profile = "finance-db-server" }; not yet implemented.

place formats

Each place renders the decoy's connection into the file format that matches where it lives:

Format Renders
pgpasshost:port:db:user:password line for a .pgpass
connstringscheme://user:pass@host:port/db URL
dotenvKEY=value lines (DB URL, AWS keys, or token URL by technology)
rails_dba production: stanza for config/database.yml
ssh_configa Host entry for ~/.ssh/config
aws_credentialsa named profile block for ~/.aws/credentials
templatea Go template you supply, evaluated against the connection (.Host, .Port, .Username, .Password, .URL, …)

plant types

A plant is shorthand for a decoy plus its conventional breadcrumb file:

  • aws-credentials → an AWS key decoy in ~/.aws/credentials
  • env-file → a token in /srv/app/.env
  • ssh-config → a DNS-token host in ~/.ssh/config
  • bash-history → a token referenced from ~/.bash_history

One apply, one destroy

The resource owns the whole lifecycle: apply creates the decoys, renders their lures and launches the box; destroy terminates the instance and reaps every decoy it created — so a failed apply leaves nothing behind, and a destroy leaves no orphaned bait.