Skip to content

Configuration Best Practices

This page provides production-tested best practices for configuring OWASP Amass deployments. It covers configuration hierarchy, resolver management, scope tuning, secrets handling, and performance optimization.

For basic installation and initial configuration, see Installation. For detailed information about the Configuration System architecture, see Configuration. For deployment strategies, see Docker Deployment.


Configuration Hierarchy and Loading

Amass follows a three-tier configuration hierarchy where later sources override earlier ones:

  1. YAML Configuration File - Primary configuration source
  2. Environment Variables - Override specific settings
  3. Command-Line Arguments - Highest priority overrides

Configuration File Resolution Order

The system searches for configuration files in the following order:

graph TD
    CLI["CLI --config flag"] --> EnvVar["AMASS_CONFIG environment variable"]
    EnvVar --> UserDir["$HOME/.config/amass/config.yaml"]
    UserDir --> SystemDir["/etc/amass/config.yaml (Unix only)"]
    SystemDir --> DefaultConfig["Use default in-memory config"]

    CLI -->|"Found"| LoadConfig["Load Configuration"]
    EnvVar -->|"Found"| LoadConfig
    UserDir -->|"Found"| LoadConfig
    SystemDir -->|"Found"| LoadConfig
    DefaultConfig --> LoadConfig

    LoadConfig --> ValidateScope["Validate Scope"]
    LoadConfig --> ValidateResolvers["Validate Resolvers"]
    LoadConfig --> ValidateTransforms["Validate Transformations"]

    ValidateScope --> ReadyToRun["Configuration Ready"]
    ValidateResolvers --> ReadyToRun
    ValidateTransforms --> ReadyToRun

Best Practice: Multi-Environment Configuration

Structure your configuration files for different environments:

$HOME/.config/amass/
├── config.yaml              # Default configuration
├── config.production.yaml   # Production overrides
├── config.staging.yaml      # Staging overrides
└── resolvers/
    ├── trusted.txt          # Custom trusted resolvers
    └── public.txt           # Additional public resolvers

Use the AMASS_CONFIG environment variable to switch between environments:

amass enum -d example.com

export AMASS_CONFIG=$HOME/.config/amass/config.production.yaml
amass enum -d example.com

amass enum -config $HOME/.config/amass/config.staging.yaml -d example.com

Resolver Configuration Best Practices

Understanding Resolver Types

Amass uses two distinct resolver pools with different characteristics:

Resolver Type Default Count Default QPS Purpose Source
Trusted (Baseline) 17 hardcoded 15 queries/sec Primary resolution, validation
Public Fetched dynamically 5 queries/sec Load distribution, diversity
graph LR
    subgraph "Resolver Configuration"
        ConfigYAML["config.yaml"] --> TrustedList["Trusted Resolvers<br/>17 baseline + custom"]
        ConfigYAML --> PublicList["Public Resolvers<br/>From public-dns.info"]

        TrustedList --> TrustedQPS["TrustedQPS: 15<br/>per resolver"]
        PublicList --> PublicQPS["ResolversQPS: 5<br/>per resolver"]

        TrustedQPS --> CalcMaxQPS["Config.CalcMaxQPS()"]
        PublicQPS --> CalcMaxQPS

        CalcMaxQPS --> MaxDNSQueries["Config.MaxDNSQueries<br/>= (trusted × 15) + (public × 5)"]
    end

    subgraph "Runtime Usage"
        MaxDNSQueries --> DNSEngine["DNS Resolution Engine"]
        DNSEngine --> WildcardDetector["Wildcard Detector<br/>Uses 8.8.4.4 only"]
    end

Best Practice: Custom Resolver Lists

For production deployments, provide custom resolver lists to avoid reliance on external services:

config.yaml:

options:
  resolvers:
    - 8.8.8.8                           # Direct IP address
    - 1.1.1.1                           # Direct IP address
    - ./resolvers/custom-resolvers.txt  # Relative path from config file
    - /etc/amass/resolvers/prod.txt     # Absolute path

resolvers/custom-resolvers.txt:

10.0.1.53
10.0.2.53
10.0.3.53

172.16.0.23

The configuration system handles both direct IP addresses and file paths, resolving relative paths from the config file location:

Best Practice: Resolver QPS Tuning

Adjust QPS rates based on your resolver infrastructure and rate limits:

trusted_resolvers:
  - 8.8.8.8
  - 1.1.1.1

resolvers:
  - ./resolvers/internal-dns.txt

Calculation formula:

MaxDNSQueries = (len(TrustedResolvers) × TrustedQPS) + (len(Resolvers) × ResolversQPS)

Best Practice: Resolver Reliability Filtering

The public resolver fetcher automatically filters by reliability score:

// Only resolvers with >= 85% reliability are included
const minResolverReliability = 0.85

This prevents unstable resolvers from polluting results. For custom resolver lists, validate reliability before adding:

dig @8.8.8.8 example.com +time=2 +tries=3

Scope Management Best Practices

Rigid vs Flexible Boundaries

Amass supports two scope enforcement modes:

graph TB
    subgraph "Flexible Mode (rigid: false)"
        FlexInput["Input: example.com"]
        FlexInput --> FlexDNS["DNS Discovery"]
        FlexDNS --> FlexSubdomain["mail.example.com"]
        FlexDNS --> FlexIP["203.0.113.10"]
        FlexIP --> FlexReverse["PTR: shared.hosting.com"]
        FlexReverse --> FlexExpand["Explore shared.hosting.com<br/>(out of original scope)"]
    end

    subgraph "Rigid Mode (rigid: true)"
        RigidInput["Input: example.com"]
        RigidInput --> RigidDNS["DNS Discovery"]
        RigidDNS --> RigidSubdomain["mail.example.com"]
        RigidDNS --> RigidIP["203.0.113.10"]
        RigidIP --> RigidReverse["PTR: shared.hosting.com"]
        RigidReverse --> RigidStop["Discard shared.hosting.com<br/>(out of scope)"]
    end

Best Practice: Scope Definition for Different Use Cases

Targeted Assessment (Rigid Boundaries):

rigid_boundaries: true
scope:
  domains:
    - example.com
    - example.net
  cidrs:
    - 203.0.113.0/24
  asns:
    - 64496
  ports:
    - 80
    - 443
    - 8080
    - 8443

Comprehensive Discovery (Flexible Boundaries):

rigid_boundaries: false
seed:
  domains:
    - example.com

Best Practice: Blacklist Management

Use blacklists to exclude specific subdomains from enumeration:

scope:
  domains:
    - example.com
  blacklist:
    - cdn.example.com           # Third-party CDN
    - legacy-*.example.com      # Pattern matching not supported
    - test.example.com          # Internal testing domain

Important: Blacklist entries must be exact matches. Pattern matching is not currently supported.


API Key and Secrets Management

Architecture for Credential Storage

graph TD
    subgraph "Configuration Sources"
        ConfigFile["config.yaml<br/>(API keys in plain text)"]
        EnvVars["Environment Variables<br/>AMASS_CONFIG"]
        SecretManager["Secret Manager<br/>(HashiCorp Vault, AWS Secrets)"]
    end

    subgraph "Amass Configuration Loading"
        LoadConfig["Config.LoadSettings()"]
        DataSrcConfigs["Config.DataSrcConfigs"]

        LoadConfig --> ParseYAML["Parse YAML"]
        ParseYAML --> LoadDataSourceSettings["loadDataSourceSettings()"]
        LoadDataSourceSettings --> DataSrcConfigs
    end

    ConfigFile --> LoadConfig
    EnvVars -->|"Override path"| LoadConfig
    SecretManager -->|"Inject at runtime"| EnvVars

    DataSrcConfigs --> Plugins["API Integration Plugins<br/>(GLEIF, Aviato, etc.)"]

Best Practice: Externalized Secrets

Development (config.yaml):

datasources:
  aviato:
    api_key: "dev-api-key-1234567890"
  gleif:
    api_key: "dev-gleif-key-abcdefg"

Production (Environment Variables + Template):

config.production.yaml (template):

datasources:
  aviato:
    # api_key loaded from environment
  gleif:
    # api_key loaded from environment

Deployment script:

#!/bin/bash
export AVIATO_API_KEY=$(vault kv get -field=api_key secret/amass/aviato)
export GLEIF_API_KEY=$(vault kv get -field=api_key secret/amass/gleif)

envsubst < config.production.yaml.template > config.production.yaml

amass enum -config config.production.yaml -d example.com

rm config.production.yaml

Best Practice: Minimal Permission API Keys

Configure API keys with minimal required permissions:

Data Source Required Permissions Rate Limit Considerations
GLEIF Public read access No key required for basic search
Aviato Company search, employee read 1000 requests/day on free tier
RDAP No authentication Rate limited by registry
BGP.Tools WHOIS No authentication Aggressive rate limiting

Performance Tuning Best Practices

DNS Query Rate Optimization

graph LR
    subgraph "Configuration Inputs"
        TrustedCount["Trusted Resolvers<br/>Count: N"]
        PublicCount["Public Resolvers<br/>Count: M"]
        TrustedQPS["TrustedQPS<br/>Default: 15"]
        PublicQPS["ResolversQPS<br/>Default: 5"]
    end

    subgraph "Calculation"
        CalcMaxQPS["MaxDNSQueries =<br/>(N × TrustedQPS) + (M × PublicQPS)"]
    end

    subgraph "Impact"
        EnumSpeed["Enumeration Speed"]
        ResolverLoad["Resolver Load"]
        FalsePositives["False Positive Rate"]
    end

    TrustedCount --> CalcMaxQPS
    PublicCount --> CalcMaxQPS
    TrustedQPS --> CalcMaxQPS
    PublicQPS --> CalcMaxQPS

    CalcMaxQPS -->|"Higher = Faster"| EnumSpeed
    CalcMaxQPS -->|"Higher = More Load"| ResolverLoad
    CalcMaxQPS -->|"Too High = Errors"| FalsePositives

Best Practice: Resource Allocation

Small Scope (1-10 domains):

trusted_resolvers:
  - 8.8.8.8
  - 1.1.1.1

Medium Scope (10-100 domains):

trusted_resolvers:
  - 8.8.8.8
  - 1.1.1.1
  - 9.9.9.9
  - 208.67.222.222
options:
  resolvers:
    - ./resolvers/additional-20.txt

Large Scope (100+ domains):

trusted_resolvers:
  - ./resolvers/corporate-dns-pool.txt  # 10 resolvers
options:
  resolvers:
    - ./resolvers/public-validated.txt  # 50 resolvers

Best Practice: TTL Configuration

The MinimumTTL setting controls how long discovered data is cached:

options:
  minimum_ttl: 1440  # 24 hours (default)
  # minimum_ttl: 60  # 1 hour for rapidly changing infrastructure
  # minimum_ttl: 10080  # 7 days for stable environments

Trade-offs:

TTL Value Pros Cons
Low (60 min) Fresh data, detects changes quickly More DNS queries, slower enumeration
Medium (1440 min) Balanced performance May miss recent changes
High (10080 min) Faster enumeration, reduced load Stale data risk

Transformation Configuration

Understanding the Transformation System

Transformations control how discovered assets flow through the processing pipeline:

graph TD
    subgraph "Transformation Configuration"
        DefaultTransform["DefaultTransformations<br/>TTL: 1440<br/>Confidence: 50<br/>Priority: 5"]
        CustomTransform["Custom Transformations<br/>Per asset type"]
    end

    subgraph "Asset Processing"
        IncomingAsset["Incoming Asset<br/>(FQDN, IP, etc.)"]
        CheckTransform["Check Transformations map"]
        ApplyDefault["Apply Default"]
        ApplyCustom["Apply Custom"]

        IncomingAsset --> CheckTransform
        CheckTransform -->|"No match"| ApplyDefault
        CheckTransform -->|"Match found"| ApplyCustom
    end

    subgraph "Transformation Properties"
        TTL["TTL<br/>(cache duration)"]
        Confidence["Confidence<br/>(trust score)"]
        Priority["Priority<br/>(processing order)"]
    end

    DefaultTransform --> ApplyDefault
    CustomTransform --> ApplyCustom

    ApplyDefault --> TTL
    ApplyDefault --> Confidence
    ApplyDefault --> Priority

    ApplyCustom --> TTL
    ApplyCustom --> Confidence
    ApplyCustom --> Priority

Best Practice: Asset-Specific Transformations

Configure different processing rules for different asset types:

transformations:
  fqdn:
    ttl: 1440        # 24 hours for domain names
    confidence: 80   # High confidence in DNS data
    priority: 1      # Process domains first

  ip_address:
    ttl: 2880        # 48 hours for IP addresses (more stable)
    confidence: 90   # Very high confidence
    priority: 3      # Process after domains

  organization:
    ttl: 10080       # 7 days for organization data
    confidence: 70   # Medium confidence (fuzzy matching)
    priority: 5      # Lower priority

Production Deployment Patterns

Pattern 1: Engine-as-a-Service

Run the engine as a persistent service, with ephemeral enumeration clients:

graph TB
    subgraph "Production Infrastructure"
        EngineService["amass engine<br/>(systemd/docker service)<br/>GraphQL API: :8080"]
        GraphDB["Graph Database<br/>$HOME/.config/amass/"]

        EngineService --> GraphDB
    end

    subgraph "Enumeration Clients"
        CronJob["Cron Job<br/>Daily enum"]
        ManualRun["Manual Investigation"]
        CICD["CI/CD Pipeline"]

        CronJob -->|"HTTP"| EngineService
        ManualRun -->|"HTTP"| EngineService
        CICD -->|"HTTP"| EngineService
    end

    subgraph "Analysis Tools"
        OAMAssoc["oam_assoc<br/>(graph queries)"]
        OAMTrack["oam_track<br/>(change detection)"]
        OAMViz["oam_viz<br/>(visualization)"]

        OAMAssoc --> GraphDB
        OAMTrack --> GraphDB
        OAMViz --> GraphDB
    end

systemd service configuration:

[Unit]
Description=OWASP Amass Engine
After=network.target

[Service]
Type=simple
User=amass
Environment="AMASS_CONFIG=/etc/amass/config.production.yaml"
ExecStart=/usr/local/bin/amass engine
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Pattern 2: Containerized Deployment

Mount configuration and data directories as volumes:

version: '3.8'
services:
  amass-engine:
    image: owaspamass/amass:latest
    container_name: amass-engine
    volumes:
      - ./config:/root/.config/amass:ro
      - ./data:/root/.config/amass/data
      - ./resolvers:/etc/amass/resolvers:ro
    environment:
      - AMASS_CONFIG=/root/.config/amass/config.yaml
    ports:
      - "8080:8080"
    restart: unless-stopped

  amass-enum:
    image: owaspamass/amass:latest
    container_name: amass-enum
    depends_on:
      - amass-engine
    command: enum -d example.com
    volumes:
      - ./config:/root/.config/amass:ro
      - ./output:/output

Pattern 3: Configuration Validation Pipeline

Validate configuration before deployment:

#!/bin/bash

CONFIG_FILE="$1"

echo "Validating configuration: $CONFIG_FILE"

yamllint "$CONFIG_FILE" || exit 1

grep -E "^\s+- ./" "$CONFIG_FILE" | while read -r line; do
    path=$(echo "$line" | sed 's/.*- //')
    if [ ! -f "$path" ]; then
        echo "ERROR: Referenced file not found: $path"
        exit 1
    fi
done

if grep -q "resolvers:" "$CONFIG_FILE"; then
    echo "Testing resolver connectivity..."
    # Extract and test each resolver
fi

echo "Configuration validation passed"

Common Pitfalls and Solutions

Pitfall 1: Relative Path Resolution

Problem: Relative paths in config fail when running from different directories.

Solution: Use absolute paths or understand resolution order:

options:
  resolvers:
    - resolvers/custom.txt

options:
  resolvers:
    - ./resolvers/custom.txt

options:
  resolvers:
    - /etc/amass/resolvers/custom.txt

The system resolves ./ paths relative to the config file's directory, not the working directory:

Pitfall 2: Insufficient Resolver Pool

Problem: Using only default resolvers causes rate limiting and slow enumeration.

Solution: Provision adequate resolver infrastructure:

trusted_resolvers:
  - 8.8.8.8
  - 1.1.1.1
  # Add more trusted resolvers

options:
  resolvers:
    - ./resolvers/validated-public.txt  # 50+ resolvers

Pitfall 3: Scope Creep with Flexible Boundaries

Problem: Flexible boundaries discover too many unrelated assets.

Solution: Use seed/scope separation:

seed:
  domains:
    - example.com

scope:
  domains:
    - example.com
    - "*.example.com"
  cidrs:
    - 203.0.113.0/24
rigid_boundaries: true

Pitfall 4: Missing Output Directory Permissions

Problem: Amass fails to create database files due to permission errors.

Solution: Ensure output directory exists with correct permissions:

mkdir -p $HOME/.config/amass
chmod 755 $HOME/.config/amass

mkdir -p /var/lib/amass
chown amass:amass /var/lib/amass

The system automatically creates the output directory if it doesn't exist:

Pitfall 5: Transformation Map Misconfig

Problem: No transformations defined causes assets to be ignored.

Solution: Ensure default transformations cover all asset types:

transformations:
  default:
    ttl: 1440
    confidence: 50
    priority: 5

  # Override for specific types
  fqdn:
    ttl: 720
    confidence: 80
    priority: 1

If no transformation is defined for an asset type, it falls back to DefaultTransformations:


Configuration Checklist

Use this checklist before production deployment:

  • Configuration File
  • Valid YAML syntax
  • All file paths exist and are accessible
  • No hardcoded secrets in version control

  • Resolvers

  • At least 5 trusted resolvers configured
  • Resolver files validated (all valid IPs)
  • QPS tuned for infrastructure capacity
  • MaxDNSQueries calculated and appropriate

  • Scope

  • Domains explicitly listed
  • CIDRs and ASNs verified
  • Blacklist populated if needed
  • Rigid boundaries setting appropriate for use case

  • API Keys

  • Stored in external secret manager
  • Minimal permissions configured
  • Rate limits understood and monitored

  • Performance

  • MinimumTTL appropriate for update frequency
  • Transformation priorities set correctly
  • Resource allocation matches scope size

  • Infrastructure

  • Output directory exists with correct permissions
  • Sufficient disk space for graph database
  • Network access to required APIs
  • DNS resolver connectivity verified