diff --git a/SDKs/go-honeywire/go.mod b/SDKs/go-honeywire/go.mod index 90e563f..1e3f57f 100644 --- a/SDKs/go-honeywire/go.mod +++ b/SDKs/go-honeywire/go.mod @@ -1,3 +1,3 @@ module github.com/honeywire/sdk-go -go 1.22.1 +go 1.25.0 diff --git a/Sensors/official/FileCanary/README.md b/Sensors/official/FileCanary/README.md new file mode 100644 index 0000000..d384919 --- /dev/null +++ b/Sensors/official/FileCanary/README.md @@ -0,0 +1,44 @@ +# HoneyWire Official Sensor: File Canary + +The File Canary acts as both a Honeypot and a File Integrity Monitor (FIM). It watches a specified directory or file on the host machine. If an attacker modifies, deletes, or drops a file into the watched area, the sensor immediately fires an alert to the HoneyWire Hub. + +## Features +* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Go SDK. +* **Dual-Mode Operation:** Can monitor highly sensitive, real system files (FIM) or act as a standalone honeypot directory (Trap). +* **Safe Permissions Handling:** Uses Access Control Lists (ACLs) to securely read target directories without altering their original host ownership. +* **Failsafe Mounts:** Designed to halt deployment if the target directory doesn't exist, preventing false-positive monitoring. + +## Configuration + +Configuration is managed through an `.env` file located in the same directory as the `docker-compose.yml`. + +### Core Ecosystem Variables +| Variable | Description | Example | +|---|---|---| +| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://127.0.0.1:8080` | +| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | +| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `file-canary-01` | +| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `critical` | +### Sensor-Specific Variables +| Variable | Description | Default | +|---|---|---| +| `TRAP_PATH` | The physical path on the host machine to monitor. | `./trap_directory` or `/etc/passwd` | +## Deployment +It is highly recommended to deploy this sensor using the provided `docker-compose.yml` configuration and the provided `Dockerfile` (if building from source). The compose file orchestrates the required permission fixers and read-only mounts automatically. + +```bash +docker compose up -d +``` + +## Security Architecture + +This sensor is architected for extreme resilience against exploits. By utilizing a minimal attack surface and enforcing strict container sandboxing, it ensures the host filesystem remains protected. + +**Core Defense-in-Depth Measures:** +* **Unprivileged Execution:** Runs entirely as a non-root user (`UID 65532`), preventing system-level modifications even in the event of a container breach. +* **Read-Only Mounts:** The target directory is mounted with strict `read_only: true` flags, ensuring the container cannot write to or modify the host files it is monitoring under any circumstances. +* **ACL Integration:** Instead of changing host file ownership, a temporary initialization container uses `setfacl` to grant the non-root user specific, read-only traverse rights, keeping your original host permissions completely intact. +* **Kernel Capability Stripping:** Drops all default Linux kernel capabilities (`cap_drop: ALL`) via the Docker Compose configuration, neutralizing advanced kernel exploitation techniques. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or standard Linux utilities, leaving attackers with zero tools to pivot to the host. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime.* \ No newline at end of file diff --git a/Sensors/official/FileCanary/docker-compose.yml b/Sensors/official/FileCanary/docker-compose.yml index 8351f7c..c71dd10 100644 --- a/Sensors/official/FileCanary/docker-compose.yml +++ b/Sensors/official/FileCanary/docker-compose.yml @@ -1,3 +1,5 @@ +version: '3.8' + services: # 1. THE FIM SETTER: Only runs if the path exists. Simply grants the read-only ACL pass. permission-fixer: @@ -17,6 +19,9 @@ services: dockerfile: Sensors/official/FileCanary-GO/Dockerfile restart: unless-stopped + # Ensures uniform communication with a locally hosted Hub + network_mode: "host" + depends_on: permission-fixer: condition: service_completed_successfully diff --git a/Sensors/official/IcmpCanary/README.md b/Sensors/official/IcmpCanary/README.md new file mode 100644 index 0000000..47f6d3e --- /dev/null +++ b/Sensors/official/IcmpCanary/README.md @@ -0,0 +1,42 @@ +# HoneyWire Official Sensor: ICMP Canary + +The ICMP Canary (Ping Canary) is a simple, highly effective network tripwire. It listens for ICMP Echo Requests (pings) directed at the host machine. It is best deployed on isolated IPs, darknets, or unused subnets where any inbound ICMP traffic is inherently suspicious. + +## Features +* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Go SDK. +* **Raw Socket Listening:** Uses pure Go to listen directly for protocol 1 (ICMP) packets without external C-dependencies. +* **Low Overhead:** Requires minimal CPU and RAM to operate, making it ideal for widespread deployment. +* **Distroless Container:** Compiled as a statically-linked binary running inside a minimal Docker image. + +## Configuration + +Configuration is managed through Environment Variables. + +### Core Ecosystem Variables +| Variable | Description | Example | +|---|---|---| +| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://127.0.0.1:8080` | +| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | +| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `ping-canary-01` | +| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `high` | + +## Deployment + +It is highly recommended to deploy this sensor using the provided `docker-compose.yml` configuration and the provided `Dockerfile` (if building from source). The compose file automatically applies the strict network and kernel capability rules required for safe execution. + +```bash +docker compose up -d +``` + +## Security Architecture + +This sensor is architected for extreme resilience against exploits. By utilizing a minimal attack surface and enforcing strict container sandboxing, it safely intercepts raw ICMP traffic. + +**Core Defense-in-Depth Measures:** +* **Raw Socket Isolation:** Bypasses heavy NIDS frameworks by interacting directly with network packets in pure Go, eliminating external C-library vulnerabilities. +* **Least Privilege Execution:** Runs as container root strictly to bind the raw socket, relying on container boundaries to limit system access. +* **Kernel Capability Stripping:** Drops all default Linux kernel capabilities (`cap_drop: ALL`) and only adds back `NET_RAW`, ensuring the sensor can intercept pings but cannot modify the host filesystem or OS. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or standard Linux utilities, leaving attackers with zero tools to execute secondary payloads. +* **In-Memory Operation:** Processes all packet data exclusively in memory, ensuring zero malicious disk I/O operations occur on the host system. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime.* \ No newline at end of file diff --git a/Sensors/official/IcmpCanary/docker-compose.yml b/Sensors/official/IcmpCanary/docker-compose.yml index 36aba4e..dd6c06f 100644 --- a/Sensors/official/IcmpCanary/docker-compose.yml +++ b/Sensors/official/IcmpCanary/docker-compose.yml @@ -6,6 +6,9 @@ services: dockerfile: Sensors/official/IcmpCanary-GO/Dockerfile restart: unless-stopped + # Binds directly to the host machine to capture the true Source IPs + network_mode: "host" + # --------------------------------------------------------- # SECURITY HARDENING: Principle of Least Privilege # --------------------------------------------------------- @@ -15,7 +18,7 @@ services: - NET_RAW environment: - - HW_HUB_ENDPOINT=http://172.17.0.1:8080 + - HW_HUB_ENDPOINT=http://127.0.0.1:8080 - HW_HUB_KEY=change_this_to_a_secure_random_string - HW_SENSOR_ID=ping-canary-01 - HW_TEST_MODE=false \ No newline at end of file diff --git a/Sensors/official/NetworkScanDetector/README.md b/Sensors/official/NetworkScanDetector/README.md new file mode 100644 index 0000000..f3e98b7 --- /dev/null +++ b/Sensors/official/NetworkScanDetector/README.md @@ -0,0 +1,49 @@ +# HoneyWire Official Sensor: Network Scan Detector + +The Network Scan Detector is a low-overhead network sensor designed to silently detect horizontal port scans. By monitoring raw SYN packets directly on the network interface, it identifies scanning activity aimed at closed or unused ports before it ever reaches a firewall or application log. + +## Features +* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Go SDK. +* **In-Memory Parsing:** Analyzes raw TCP headers directly in memory. +* **Configurable Thresholds:** Easily adjust how many unique ports must be hit within a specific time window to trigger an alert. +* **Distroless Container:** Compiled as a statically-linked binary running inside a minimal Docker image. + +## Configuration + +All configuration is handled via Environment Variables. + +### Core Ecosystem Variables +| Variable | Description | Example | +|---|---|---| +| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://127.0.0.1:8080` | +| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | +| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `scan-detector-01` | +| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `critical` | + +### Sensor-Specific Variables +| Variable | Description | Default | +|---|---|---| +| `HW_SCAN_THRESHOLD` | Number of unique ports that must be hit to trigger an alert. | `5` | +| `HW_SCAN_WINDOW` | The time window (in seconds) to track the threshold. | `5` | +| `HW_IGNORE_PORTS` | Comma-separated ports to ignore (e.g., actual open services). | `80,443` | + +## Deployment + +It is highly recommended to deploy this sensor using the provided docker-compose.yml configuration and the provided Dockerfile (if building from source). The compose file automatically applies the strict network and kernel capability rules required for safe execution. + +```bash +docker compose up -d +``` + +## Security Architecture + +This sensor is architected for extreme resilience against exploits. By utilizing a minimal attack surface and enforcing strict container sandboxing, it safely handles raw network traffic. + +**Core Defense-in-Depth Measures:** +* **Raw Socket Isolation:** Bypasses heavy NIDS frameworks by interacting directly with network packets in pure Go, eliminating external C-library vulnerabilities. +* **Least Privilege Execution:** Runs as container root strictly to bind the raw socket, but relies on capability dropping to prevent privilege escalation. +* **Kernel Capability Stripping:** Drops all default Linux kernel capabilities (`cap_drop: ALL`) and only adds back `NET_RAW`, ensuring the sensor can read packets but cannot modify the system. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or standard Linux utilities, leaving attackers with zero tools to pivot to the host network. +* **In-Memory Operation:** Processes all payload data exclusively in memory, ensuring zero malicious disk I/O operations occur on the host system. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime.* \ No newline at end of file diff --git a/Sensors/official/NetworkScanDetector/docker-compose.yml b/Sensors/official/NetworkScanDetector/docker-compose.yml index 5a8a9fa..da91701 100644 --- a/Sensors/official/NetworkScanDetector/docker-compose.yml +++ b/Sensors/official/NetworkScanDetector/docker-compose.yml @@ -6,6 +6,9 @@ services: dockerfile: Sensors/official/NetworkScanDetector-GO/Dockerfile restart: unless-stopped + # Binds directly to the host machine to accurately capture true Source IPs + network_mode: "host" + # --------------------------------------------------------- # SECURITY HARDENING # --------------------------------------------------------- @@ -15,7 +18,7 @@ services: - NET_RAW environment: - - HW_HUB_ENDPOINT=http://172.17.0.1:8080 + - HW_HUB_ENDPOINT=http://127.0.0.1:8080 - HW_HUB_KEY=change_this_to_a_secure_random_string - HW_SENSOR_ID=scan-detector-01 - HW_TEST_MODE=false diff --git a/Sensors/official/NetworkScanDetector/go.mod b/Sensors/official/NetworkScanDetector/go.mod index 3f8a6e5..b709745 100644 --- a/Sensors/official/NetworkScanDetector/go.mod +++ b/Sensors/official/NetworkScanDetector/go.mod @@ -1,6 +1,6 @@ module github.com/honeywire/sensors/networkscandetector -go 1.22.1 +go 1.25.0 replace github.com/honeywire/sdk-go => ../../../SDKs/go-honeywire diff --git a/Sensors/official/TcpTarpit/Dockerfile b/Sensors/official/TcpTarpit/Dockerfile new file mode 100644 index 0000000..9c4007a --- /dev/null +++ b/Sensors/official/TcpTarpit/Dockerfile @@ -0,0 +1,21 @@ +# STAGE 1: Build +FROM golang:1.25 AS builder + +WORKDIR /src +COPY SDKs/go-honeywire ./SDKs/go-honeywire +COPY Sensors/official/TcpTarpit-GO ./Sensors/official/TcpTarpit-GO + +WORKDIR /src/Sensors/official/TcpTarpit-GO +RUN go mod download + +# Pure Go, fully static binary +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o tcp-tarpit ./main.go + +# STAGE 2: Distroless +# Back to the fully unprivileged nonroot container! +FROM gcr.io/distroless/static-debian12:nonroot + +COPY --from=builder /src/Sensors/official/TcpTarpit-GO/tcp-tarpit /app/tcp-tarpit + +USER 65532 +CMD ["/app/tcp-tarpit"] \ No newline at end of file diff --git a/Sensors/official/TcpTarpit/README.md b/Sensors/official/TcpTarpit/README.md new file mode 100644 index 0000000..fe0a2f0 --- /dev/null +++ b/Sensors/official/TcpTarpit/README.md @@ -0,0 +1,54 @@ +# HoneyWire Official Sensor: TCP Tarpit + +The TCP Tarpit is a high-fidelity, low-interaction honeypot designed to detect network reconnaissance and brute-force attempts. It can act as a "Tarpit," binding to decoy ports and intentionally stalling attackers to waste their time while silently extracting their IP and payload data to report to the HoneyWire Hub, or instantly close the connection and report the IP to the Hub. + +## Features +* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Go SDK. +* **Massive Concurrency:** Powered by Go routines and channels, capable of trapping thousands of automated bots simultaneously with microscopic memory overhead. +* **Tarpit Modes:** Supports `hold` (silent stall), `echo` (repeat data back), or `close` (immediate drop). +* **Forensic Capture:** Safely buffers up to 10 lines of payload data without risking memory exhaustion. +* **Distroless Container:** Compiled as a statically-linked binary running inside a hardened, unprivileged `:nonroot` Distroless Docker image to prevent container breakouts. + +## Configuration + +All configuration is handled via Environment Variables. Copy the `.env.example` file to `.env` before running. + +### Core Ecosystem Variables (Required) +| Variable | Description | Example | +|---|---|---| +| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://127.0.0.1:8080` | +| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | +| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `ssh-tarpit-01` | +| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `high` | + +### Sensor-Specific Variables +| Variable | Description | Default | +|---|---|---| +| `HW_DECOY_PORTS` | Comma-separated list of TCP ports to monitor. | `2222,3306` | +| `HW_TARPIT_MODE` | The behavior of the trap: `hold`, `echo`, or `close`. | `hold` | +| `HW_TARPIT_BANNER` | (Optional) A fake service banner to send on connect. | `SSH-2.0-OpenSSH_8.2p1\r\n` | + +## Tarpit Modes Explained +* **`hold` (Default):** The sensor accepts the connection but sends nothing. It holds the TCP socket open as long as possible (up to 1 hour), dripping empty bytes to drain the attacker's resources and slow down automated scanners like Nmap or brute-force tools. +* **`echo`:** The sensor acts as an echo server, repeating whatever the attacker sends back to them. Useful for confusing automated scripts. +* **`close`:** The sensor logs the connection, captures the initial payload, and forcefully closes the socket. + +## Deployment + +It is highly recommended to deploy this sensor using the provided docker-compose.yml configuration and the provided Dockerfile (if building from source). The compose file automatically applies the strict network and kernel capability rules required for safe execution. + +```Bash +docker compose up -d +``` + +## Security Architecture + +This sensor is architected for extreme resilience against exploitation. By adhering to the principle of least privilege and enforcing strict resource limits. + +**Core Defense-in-Depth Measures:** +* **Kernel Capability Stripping:** Drops all Linux kernel capabilities (`cap_drop: ALL`) via the Docker Compose configuration, neutralizing advanced kernel exploitation techniques. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or common Linux utilities (like `curl` or `wget`), leaving attackers with zero tools to pivot if they achieve Remote Code Execution. +* **Concurrency Capping:** Utilizes a native Go buffered channel (semaphore) to strictly cap concurrent connections at `1000`. This prevents attackers from launching a Denial of Service (DoS) attack designed to exhaust the host machine's File Descriptors or RAM. +* **In-Memory Operation:** Processes all payload data exclusively in memory, ensuring zero malicious disk I/O operations occur on the host system. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime.* \ No newline at end of file diff --git a/Sensors/official/TcpTarpit/docker-compose.yml b/Sensors/official/TcpTarpit/docker-compose.yml new file mode 100644 index 0000000..86cead2 --- /dev/null +++ b/Sensors/official/TcpTarpit/docker-compose.yml @@ -0,0 +1,32 @@ +services: + tcp-tarpit: + container_name: hw-tcp-tarpit + build: + context: ../../.. + dockerfile: Sensors/official/TcpTarpit-GO/Dockerfile + restart: unless-stopped + + # --------------------------------------------------------- + # NETWORKING: Host Mode + # --------------------------------------------------------- + # Binds directly to the host machine. + # 1. Preserves the real Source IP of the attacker. + # 2. Automatically exposes ports defined in HW_DECOY_PORTS. + network_mode: "host" + + # --------------------------------------------------------- + # SECURITY HARDENING + # --------------------------------------------------------- + cap_drop: + - ALL + + environment: + # Since we are on the host network, use the host's perspective to reach the Hub + - HW_HUB_ENDPOINT=http://127.0.0.1:8080 + - HW_HUB_KEY=change_this_to_a_secure_random_string + - HW_SENSOR_ID=tcp-tarpit-01 + - HW_TEST_MODE=false + + - HW_DECOY_PORTS=2222,3306 + - HW_TARPIT_MODE=hold + - HW_TARPIT_BANNER=SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.4\r\n \ No newline at end of file diff --git a/Sensors/official/TcpTarpit/go.mod b/Sensors/official/TcpTarpit/go.mod new file mode 100644 index 0000000..499b4ba --- /dev/null +++ b/Sensors/official/TcpTarpit/go.mod @@ -0,0 +1,7 @@ +module github.com/honeywire/sensors/tcptarpit + +go 1.25.0 + +replace github.com/honeywire/sdk-go => ../../../SDKs/go-honeywire + +require github.com/honeywire/sdk-go v0.0.0-00010101000000-000000000000 diff --git a/Sensors/official/TcpTarpit/main.go b/Sensors/official/TcpTarpit/main.go new file mode 100644 index 0000000..8efcdfd --- /dev/null +++ b/Sensors/official/TcpTarpit/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "fmt" + "log" + "net" + "os" + "strconv" + "strings" + "time" + + "github.com/honeywire/sdk-go" +) + +var ( + decoyPorts = parsePorts(getEnv("HW_DECOY_PORTS", "2222,3306")) + tarpitMode = strings.ToLower(getEnv("HW_TARPIT_MODE", "hold")) + banner = parseBanner(getEnv("HW_TARPIT_BANNER", "")) + concurrency = 1000 + maxBytes = 50 * 1024 // 50KB + maxLines = 10 + maxDuration = 3600 * time.Second +) + +func main() { + // 1. Initialize SDK + hw := sdk.NewSensor("tarpit") + hw.Start() + + log.Printf("[*] HoneyWire Tarpit | Mode: %s", strings.ToUpper(tarpitMode)) + if banner != "" { + log.Printf("[*] Banner loaded: %s", strconv.Quote(banner)) + } + + // 2. Concurrency Limiter (Semaphore) + semaphore := make(chan struct{}, concurrency) + errCh := make(chan error) + + // 3. Start a TCP Listener for each Decoy Port + for _, port := range decoyPorts { + go startListener(hw, port, semaphore, errCh) + } + + // Block main thread and watch for fatal listener errors + log.Fatal(<-errCh) +} + +func startListener(hw *sdk.Sensor, port int, semaphore chan struct{}, errCh chan error) { + addr := fmt.Sprintf("0.0.0.0:%d", port) + listener, err := net.Listen("tcp", addr) + if err != nil { + errCh <- fmt.Errorf("[!] FATAL: Failed to bind to port %d: %v", port, err) + return + } + defer listener.Close() + + log.Printf("[+] Tarpit listening on port %d", port) + + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("[-] Accept error on port %d: %v", port, err) + continue + } + + // Acquire semaphore slot (blocks if 1000 connections are active) + semaphore <- struct{}{} + + // Spawn a lightweight goroutine for the attacker + go func(c net.Conn) { + defer func() { <-semaphore }() // Release slot when done + handleConnection(hw, c, port) + }(conn) + } +} + +func handleConnection(hw *sdk.Sensor, conn net.Conn, port int) { + defer conn.Close() + + start := time.Now() + + // Extract the IP without the attacker's ephemeral port + remoteAddr := conn.RemoteAddr().String() + srcIP, _, err := net.SplitHostPort(remoteAddr) + if err != nil { + srcIP = remoteAddr + } + + var payload []string + consumedBytes := 0 + + // 1. Send Fake Banner (e.g., pretending to be SSH) + if banner != "" && tarpitMode != "close" { + conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) + conn.Write([]byte(banner)) + } + + // 2. The Tarpit Trap Loop + if tarpitMode != "close" { + buf := make([]byte, 1024) + for consumedBytes < maxBytes && time.Since(start) < maxDuration { + // Wait up to 5 minutes for the attacker to send data + conn.SetReadDeadline(time.Now().Add(300 * time.Second)) + n, err := conn.Read(buf) + + if err != nil { + // If we timed out waiting for them, send a Null byte to keep the connection alive! + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) + conn.Write([]byte{0}) + continue + } + break // Attacker dropped the connection or sent EOF + } + + if n > 0 { + consumedBytes += n + + // Safely decode to string and log up to maxLines + if len(payload) < maxLines { + text := strings.TrimSpace(strings.ToValidUTF8(string(buf[:n]), "?")) + if text != "" { + payload = append(payload, text) + } + } + + // If in echo mode, reflect their attack back at them + if tarpitMode == "echo" { + conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) + conn.Write(buf[:n]) + } + + // The Tar: Slow down our processing to waste their time + time.Sleep(500 * time.Millisecond) + } + } + } + + duration := time.Since(start).Seconds() + + // 3. Dispatch the Event via the SDK + hw.ReportEvent( + "tcp_connection", + "high", + map[string]any{ + "duration_sec": duration, + "payload": payload, + }, + tarpitMode, + srcIP, + fmt.Sprintf("Port %d", port), + ) +} + +// --- Helper Functions --- + +func parsePorts(raw string) []int { + var ports []int + for _, p := range strings.Split(raw, ",") { + p = strings.TrimSpace(p) + if p == "" { continue } + if val, err := strconv.Atoi(p); err == nil { + ports = append(ports, val) + } else { + log.Printf("[!] Invalid port in HW_DECOY_PORTS: %s", p) + } + } + if len(ports) == 0 { + return []int{2222, 3306} + } + return ports +} + +// Replaces "\n" string literals from env vars into actual newlines +func parseBanner(raw string) string { + raw = strings.ReplaceAll(raw, "\\r", "\r") + raw = strings.ReplaceAll(raw, "\\n", "\n") + return raw +} + +func getEnv(key, fallback string) string { + if val, exists := os.LookupEnv(key); exists { + return val + } + return fallback +} \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/.env.example b/Sensors/official/TcpTripWire/.env.example deleted file mode 100644 index ebe1fec..0000000 --- a/Sensors/official/TcpTripWire/.env.example +++ /dev/null @@ -1,19 +0,0 @@ -# ========================================== -# HONEYWIRE SENSOR: TCP TARPIT -# ========================================== - -# --- CORE ECOSYSTEM SETTINGS (Required) --- -HW_HUB_ENDPOINT=http://192.168.1.100:8080 -HW_HUB_KEY=change_this_to_a_secure_random_string -HW_SENSOR_ID=TripWire-01 -HW_SEVERITY=high - -# --- SENSOR SPECIFIC SETTINGS --- -# The fake ports this agent will listen on (Comma-separated) -HW_DECOY_PORTS=21,2222,3306 - -# Modes: 'echo' (repeat data), 'hold' (do nothing, keep open), 'close' (drop immediately) -HW_TARPIT_MODE=hold - -# The fake service banner to send upon connection -HW_TARPIT_BANNER=SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1\r\n \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/Dockerfile b/Sensors/official/TcpTripWire/Dockerfile deleted file mode 100644 index a1f85ab..0000000 --- a/Sensors/official/TcpTripWire/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -# --- Stage 1: Builder --- -FROM python:3.11-slim AS builder -WORKDIR /app - -RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* - -# 1. Copy the SDK source -COPY SDKs/python-honeywire /tmp/sdk_src - -# 2. Build a WHEEL (a static redistribution file) instead of installing directly. -# This ensures all code is physically packaged and not symlinked. -RUN pip install --no-cache-dir wheel && \ - pip wheel /tmp/sdk_src --wheel-dir /tmp/wheels - -# 3. Install the wheel and the requirements into a specific folder -# We use --target to ensure everything is in one flat, movable directory. -COPY Sensors/official/TcpTripWire/requirements.txt . -RUN pip install --no-cache-dir --target /app/lib /tmp/wheels/honeywire-*.whl -r requirements.txt - -# --- Stage 2: Distroless (Production) --- -FROM gcr.io/distroless/python3-debian12 -WORKDIR /app - -# 4. Copy the FLAT library folder. No symlinks, just code. -COPY --from=builder /app/lib /app/lib - -# 5. Copy the sensor script -COPY Sensors/official/TcpTripWire/TcpTripWire.py . - -# 6. Tell Python to look in our lib folder first. -ENV PYTHONPATH="/app/lib" -ENV PYTHONUNBUFFERED=1 - -# Distroless runs /usr/bin/python3 by default -CMD ["TcpTripWire.py"] \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/README.md b/Sensors/official/TcpTripWire/README.md deleted file mode 100644 index 27d1ee2..0000000 --- a/Sensors/official/TcpTripWire/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# HoneyWire Official Sensor: TCP TripWire 🕸️ - -The TCP TripWire is a high-fidelity, low-interaction honeypot designed to detect network reconnaissance and brute-force attempts. It acts as a "Tarpit," binding to decoy ports and intentionally stalling attackers to waste their time while silently extracting their IP and payload data to report to the HoneyWire Hub. - -## Features -* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Python SDK. -* **Tarpit Modes:** Supports `hold` (silent stall), `echo` (repeat data back), or `close` (immediate drop). -* **Forensic Capture:** Safely buffers up to 10 lines of payload data without risking memory exhaustion. -* **Distroless Container:** Runs as a hardened, Distroless Docker image to prevent container breakouts. - -## Configuration - -All configuration is handled via Environment Variables. Copy the `.env.example` file to `.env` before running. - -### Core Ecosystem Variables (Required) -| Variable | Description | Example | -|---|---|---| -| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://192.168.1.100:8080` | -| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | -| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `dmz-ssh-tarpit-01` | - -### Sensor-Specific Variables -| Variable | Description | Default | -|---|---|---| -| `HW_DECOY_PORTS` | Comma-separated list of TCP ports to monitor. | `2222,3306` | -| `HW_TARPIT_MODE` | The behavior of the trap: `hold`, `echo`, or `close`. | `hold` | -| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `high` | -| `HW_TARPIT_BANNER` | (Optional) A fake service banner to send on connect. | `SSH-2.0-OpenSSH_8.2p1` | - -## Tarpit Modes Explained -* **`hold` (Default):** The sensor accepts the connection but sends nothing. It holds the TCP socket open as long as possible, draining the attacker's resources and slowing down automated scanners like Nmap. -* **`echo`:** The sensor acts as an echo server, repeating whatever the attacker sends back to them. Useful for confusing automated scripts. -* **`close`:** The sensor logs the connection, captures the initial payload, and forcefully closes the socket. - -## Deployment - -The easiest way to deploy this sensor is via Docker Compose. Note that if you intend to bind to privileged ports (any port under 1024, like Port 22 or 80), the container must run as `root` (which is the default). - -### 1. Using Docker Compose -Create a `docker-compose.yml` file: - -```yaml -services: - tcp-tripwire: - image: ghcr.io/andreicscs/honeywire-tcptripwire:latest - container_name: hw-tcp-tripwire - restart: unless-stopped - - # CRITICAL: Binds directly to the host machine's network. - # 1. Preserves the real source IP of the attacker. - # 2. Automatically exposes whatever ports are set in HW_DECOY_PORTS. - network_mode: "host" - - env_file: - - .env -``` -Run it in the background: -```Bash -docker-compose up -d -``` - -### 2. Testing Locally (Build from Source) -If you are developing or testing locally: -```Bash -docker build -t honeywire-tcptripwire . -docker run --rm --network host --env-file .env honeywire-tcptripwire -``` - -## Security Note -This sensor uses asyncio with strict memory limits (50KB or 10 lines per connection) and a global semaphore to limit concurrent connections. This ensures the sensor cannot be easily DOS'd or used to exhaust the host machine's File Descriptors. \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/TcpTripWire.py b/Sensors/official/TcpTripWire/TcpTripWire.py deleted file mode 100644 index 709ef6b..0000000 --- a/Sensors/official/TcpTripWire/TcpTripWire.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -HoneyWire Tarpit Agent (Standard Port Tripwire) -Sensor type: tarpit - -Custom Environment variables: - HW_DECOY_PORTS Comma-separated ports (default: 2222,3306) - HW_TARPIT_MODE hold | echo | close (default: hold) - HW_TARPIT_BANNER Optional service banner -""" - -import asyncio -import os -import time -from typing import List, Optional - -from honeywire import HoneyWireSensor - - -DEFAULT_DECOY_PORTS = [2222, 3306] -DEFAULT_TARPIT_MODE = "hold" -DEFAULT_MAX_BYTES = 1024 * 50 -DEFAULT_MAX_LINES = 10 -DEFAULT_MAX_DURATION = 3600 -DEFAULT_CONCURRENCY = 1000 - - -class TcpTarpitSensor(HoneyWireSensor): - def __init__(self): - super().__init__(sensor_type="tarpit") - - self.tarpit_mode = os.getenv("HW_TARPIT_MODE", DEFAULT_TARPIT_MODE).lower() - self.decoy_ports = self._parse_ports(os.getenv("HW_DECOY_PORTS", ",".join(map(str, DEFAULT_DECOY_PORTS)))) - self.tarpit_banner = self._load_banner(os.getenv("HW_TARPIT_BANNER", "")) - - self.max_bytes = DEFAULT_MAX_BYTES - self.max_lines = DEFAULT_MAX_LINES - self.max_duration = DEFAULT_MAX_DURATION - self.semaphore = asyncio.Semaphore(DEFAULT_CONCURRENCY) - - @staticmethod - def _parse_ports(raw_ports: str) -> List[int]: - ports = [] - for item in raw_ports.split(","): - item = item.strip() - if not item: - continue - try: - ports.append(int(item)) - except ValueError: - print(f"[!] Invalid port in HW_DECOY_PORTS: {item}") - return ports or DEFAULT_DECOY_PORTS - - @staticmethod - def _load_banner(raw_banner: str) -> bytes: - if not raw_banner: - return b"" - return raw_banner.encode("utf-8").decode("unicode_escape").encode("utf-8") - - async def _capture_connection(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, port: int): - peer = writer.get_extra_info("peername") - source_ip = peer[0] if peer else "Unknown" - - start_time = time.time() - captured_payload = [] - consumed_bytes = 0 - - try: - if self.tarpit_banner and self.tarpit_mode != "close": - writer.write(self.tarpit_banner) - await writer.drain() - - if self.tarpit_mode != "close": - while consumed_bytes < self.max_bytes and (time.time() - start_time) < self.max_duration: - try: - timeout = 300.0 - data = await asyncio.wait_for(reader.read(1024), timeout=timeout) - if not data: - break - - decoded = data.decode("utf-8", errors="replace").strip() - if decoded and len(captured_payload) < self.max_lines: - captured_payload.append(decoded) - - consumed_bytes += len(data) - - if self.tarpit_mode == "echo": - writer.write(data) - await writer.drain() - - await asyncio.sleep(0.5) - - except asyncio.TimeoutError: - if not writer.is_closing(): - writer.write(b"\0") - await writer.drain() - - except Exception as exc: - print(f"[!] Connection processing error from {source_ip}:{port} - {exc}") - - finally: - if not writer.is_closing(): - try: - writer.close() - await writer.wait_closed() - except Exception: - pass - - duration = time.time() - start_time - event_payload = { - "duration_sec": round(duration, 2), - "payload": captured_payload, - } - - await asyncio.to_thread( - self.report_event, - event_type="tcp_connection", - severity=self.severity, - details=event_payload, - action_taken=self.tarpit_mode, - source=source_ip, - target=f"Port {port}", - ) - - async def _serve_connection(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, port: int): - async with self.semaphore: - await self._capture_connection(reader, writer, port) - - async def monitor(self): - print(f"[*] HoneyWire Agent | Mode: {self.tarpit_mode.upper()} | Severity: {self.severity}") - - listeners = [] - for port in self.decoy_ports: - listener = await asyncio.start_server( - lambda r, w, p=port: self._serve_connection(r, w, p), - "0.0.0.0", - port, - ) - listeners.append(listener.serve_forever()) - print(f"[+] Tarpit listening on port {port}") - - await asyncio.gather(*listeners) - - -if __name__ == "__main__": - sensor = TcpTarpitSensor() - try: - asyncio.run(sensor.start()) - except KeyboardInterrupt: - print("\n[*] Shutting down HoneyWire Tarpit...") \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/docker-compose.yml b/Sensors/official/TcpTripWire/docker-compose.yml deleted file mode 100644 index c19ea01..0000000 --- a/Sensors/official/TcpTripWire/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -services: - tcp-tripwire: - # Option A: Pull the pre-built image from GHCR (Uncomment lines below to use) - # image: ghcr.io/andreicscs/honeywire-tcptripwire:latest - - # Option B: Build locally from source (Comment lines below if you want to use the pre-built image) - build: - context: ../../../ - dockerfile: Sensors/official/TcpTripWire/Dockerfile - - container_name: hw-tcp-tripwire - restart: unless-stopped - - # CRITICAL: Binds directly to the host machine's network to capture real source IPs - network_mode: "host" - - env_file: - - .env \ No newline at end of file diff --git a/Sensors/official/TcpTripWire/requirements.txt b/Sensors/official/TcpTripWire/requirements.txt deleted file mode 100644 index 44805cb..0000000 --- a/Sensors/official/TcpTripWire/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -# requirements.txt -aiohttp==3.13.3 -requests==2.31.0 \ No newline at end of file diff --git a/Sensors/official/WebRouterDecoy/.env.example b/Sensors/official/WebRouterDecoy/.env.example deleted file mode 100644 index 1ced583..0000000 --- a/Sensors/official/WebRouterDecoy/.env.example +++ /dev/null @@ -1,9 +0,0 @@ -# Core HoneyWire configuration -HW_HUB_ENDPOINT=http://192.168.1.100:8080 -HW_HUB_KEY=change_this_to_a_secure_random_string -HW_SENSOR_ID=fake-router-admin-01 -HW_SEVERITY=critical - -# Router deception settings -HW_BIND_PORT=8080 -HW_ROUTER_BRAND=Netgear diff --git a/Sensors/official/WebRouterDecoy/Dockerfile b/Sensors/official/WebRouterDecoy/Dockerfile index cbb16df..bf48692 100644 --- a/Sensors/official/WebRouterDecoy/Dockerfile +++ b/Sensors/official/WebRouterDecoy/Dockerfile @@ -1,27 +1,20 @@ -# --- Stage 1: Builder --- -FROM python:3.11-slim AS builder -WORKDIR /app +# STAGE 1: Build +FROM golang:1.25 AS builder -RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* +WORKDIR /src +COPY SDKs/go-honeywire ./SDKs/go-honeywire +COPY Sensors/official/WebRouterDecoy-GO ./Sensors/official/WebRouterDecoy-GO -# 1. Copy the SDK source from the repository root. -COPY SDKs/python-honeywire /tmp/sdk_src +WORKDIR /src/Sensors/official/WebRouterDecoy-GO +RUN go mod download -# 2. Build a wheel for the SDK. -RUN pip install --no-cache-dir wheel && \ - pip wheel /tmp/sdk_src --wheel-dir /tmp/wheels +# Pure Go, fully static binary +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o web-decoy ./main.go -# 3. Install the wheel and sensor-specific dependencies. -COPY Sensors/official/WebRouterDecoy/requirements.txt . -RUN pip install --no-cache-dir --target /app/lib /tmp/wheels/honeywire-*.whl -r requirements.txt +# STAGE 2: Distroless +FROM gcr.io/distroless/static-debian12:nonroot -# --- Stage 2: Distroless --- -FROM gcr.io/distroless/python3-debian12 -WORKDIR /app -COPY --from=builder /app/lib /app/lib -COPY Sensors/official/WebRouterDecoy/WebRouterDecoy.py . +COPY --from=builder /src/Sensors/official/WebRouterDecoy-GO/web-decoy /app/web-decoy -ENV PYTHONPATH="/app/lib" -ENV PYTHONUNBUFFERED=1 - -CMD ["WebRouterDecoy.py"] +USER 65532 +CMD ["/app/web-decoy"] \ No newline at end of file diff --git a/Sensors/official/WebRouterDecoy/README.md b/Sensors/official/WebRouterDecoy/README.md index a3bde09..bdad852 100644 --- a/Sensors/official/WebRouterDecoy/README.md +++ b/Sensors/official/WebRouterDecoy/README.md @@ -1,55 +1,46 @@ # HoneyWire Official Sensor: Web Router Decoy -The Web Router Decoy sensor emulates a consumer router login panel and captures credentials when an attacker (or curious insider) submits the form. It returns an HTTP 401 response to keep the attacker guessing. +The Web Router Decoy is a web honeypot designed to detect credential stuffing, automated web scanners, and targeted administrative panel attacks. It serves a deceptive, router login page. When an attacker attempts to log in, the sensor captures their IP, user agent, and attempted credentials, silently reports them to the HoneyWire Hub, and safely returns a "401 Unauthorized" response to keep them guessing. ## Features -* **Web canary:** Low-resource trap that looks like a router admin page. -* **Credential capture:** Logs attempted username and password. -* **Deceptive response:** Always returns `401 Unauthorized` to preserve the illusion. -* **HoneyWire-native reporting:** Sends standardized JSON events to the Hub. +* **Zero-Setup SDK Integration:** Natively built on the HoneyWire Go SDK. +* **Dynamic Brand Variable:** Automatically injects the specified router brand (e.g., Cisco, Netgear, ASUS) directly into the HTML template to make the trap more convincing. +* **Distroless Container:** Compiled as a statically-linked binary running inside a hardened, unprivileged `:nonroot` Distroless Docker image to prevent container breakouts. ## Configuration -Copy `.env.example` to `.env` and update the core HoneyWire settings. +All configuration is handled via Environment Variables. Copy the `.env.example` file to `.env` before running. + +### Core Ecosystem Variables (Required) +| Variable | Description | Example | +|---|---|---| +| `HW_HUB_ENDPOINT` | The URL of your central HoneyWire Hub. | `http://127.0.0.1:8080` | +| `HW_HUB_KEY` | The shared secret API key to authenticate with the Hub. | `super_secret_key_123` | +| `HW_SENSOR_ID` | A unique identifier for this specific trap. | `web-decoy-01` | +| `HW_SEVERITY` | Alert severity sent to the Hub (`info` to `critical`). | `critical` | + +### Sensor-Specific Variables | Variable | Description | Default | |---|---|---| -| `HW_HUB_ENDPOINT` | HoneyWire Hub API endpoint | `http://192.168.1.100:8080` | -| `HW_HUB_KEY` | HoneyWire API key | `super_secret_key_123` | -| `HW_SENSOR_ID` | Unique sensor identifier | `web-router-decoy-01` | -| `HW_SEVERITY` | Alert severity | `critical` | -| `HW_BIND_PORT` | Container port to listen on | `8080` | -| `HW_ROUTER_BRAND` | Display name for the fake router UI | `Netgear` | +| `HW_BIND_PORT` | The TCP port the fake web server will listen on. | `8080` | +| `HW_ROUTER_BRAND` | The brand name injected into the fake login page. | `Netgear` | ## Deployment -This sensor can use host networking for direct access to the network interface. When using host mode, set HW_BIND_PORT to 80 to listen on the standard web port. Alternatively, use standard Docker bridging and map host port 80 to container port 8080. -Example `docker-compose.yml` (host mode): - -```yaml -services: - web-router-decoy: - build: - context: ../../../ - dockerfile: Sensors/official/WebRouterDecoy/Dockerfile - container_name: hw-web-router-decoy - network_mode: "host" - env_file: - - .env +It is highly recommended to deploy this sensor using the provided docker-compose.yml configuration and the provided Dockerfile (if building from source). The compose file automatically applies the strict network and kernel capability rules required for safe execution. +```Bash +docker compose up -d ``` -Ensure HW_BIND_PORT=80 in your .env for host mode. +## Security Architecture -## Event Example -```json -{ - "event_type": "web_login_attempt", - "severity": "critical", - "source": "192.168.1.15", - "target": "Web Interface", - "details": { - "user_agent": "Mozilla/5.0...", - "attempted_username": "admin", - "attempted_password": "password123" - } -} -``` +This sensor is architected for extreme resilience against web-based exploits. By utilizing a minimal attack surface and enforcing strict container sandboxing. + +**Core Defense-in-Depth Measures:** +* **Framework-Free Execution:** Built purely on Go's native `net/http` library, eliminating the massive attack surface and supply-chain risks associated with heavy third-party web frameworks (like FastAPI, Flask, or Express). +* **Unprivileged Execution:** Runs entirely as a non-root user (`UID 65532`), preventing system-level modifications even in the event of a container breach. +* **Kernel Capability Stripping:** Drops all Linux kernel capabilities (`cap_drop: ALL`) via the Docker Compose configuration, neutralizing advanced kernel exploitation techniques. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or standard Linux utilities (like `curl` or `wget`), leaving attackers with zero tools to download secondary payloads or pivot to the host network. +* **In-Memory Operation:** Processes all payload data exclusively in memory, ensuring zero malicious disk I/O operations occur on the host system. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime.* diff --git a/Sensors/official/WebRouterDecoy/WebRouterDecoy.py b/Sensors/official/WebRouterDecoy/WebRouterDecoy.py deleted file mode 100644 index db58335..0000000 --- a/Sensors/official/WebRouterDecoy/WebRouterDecoy.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -HoneyWire Official Sensor: Web Router Decoy -Sensor type: web_honeypot - -This sensor serves a deceptive router administration login page and captures credentials when an attacker submits the form. -""" - -import asyncio -import os -from typing import Dict - -from fastapi import FastAPI, Form, Request, status -from fastapi.responses import HTMLResponse, PlainTextResponse -from fastapi.middleware.cors import CORSMiddleware -from honeywire import HoneyWireSensor -import uvicorn - - -LOGIN_PAGE = """ - - - - - - Router Login - - - -
-

Router Login

-

Sign in to the router administration panel.

-
- - - - - -
- -
- - -""" - - -class WebRouterDecoy(HoneyWireSensor): - def __init__(self): - super().__init__(sensor_type="web_honeypot") - self.port = int(os.getenv("HW_BIND_PORT", "8080")) - self.router_brand = os.getenv("HW_ROUTER_BRAND", "Netgear") - self.app = FastAPI() - - self.app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_methods=["GET", "POST"], - allow_headers=["*"] - ) - - self._register_routes() - - def _register_routes(self): - @self.app.get("/", response_class=HTMLResponse) - async def root(request: Request): - return HTMLResponse(LOGIN_PAGE) - - @self.app.post("/login", response_class=HTMLResponse) - async def login(request: Request, username: str = Form(""), password: str = Form("")): - user_agent = request.headers.get("user-agent", "Unknown") - source_ip = request.client.host if request.client else "Unknown" - - details = { - "user_agent": user_agent, - "attempted_username": username, - "attempted_password": password, - } - - await asyncio.to_thread( - self.report_event, - event_type="web_login_attempt", - severity="critical", - details=details, - action_taken="logged", - source=source_ip, - target="Web Interface", - ) - - return HTMLResponse( - content="

401 Unauthorized

Invalid Username or Password.

", - status_code=status.HTTP_401_UNAUTHORIZED, - ) - - async def monitor(self): - print(f"[*] HoneyWire Web Router Decoy Sensor | Port {self.port} | Severity: {self.severity}") - config = uvicorn.Config(self.app, host="0.0.0.0", port=self.port, log_level="warning") - server = uvicorn.Server(config) - await server.serve() - - -if __name__ == "__main__": - sensor = WebRouterDecoy() - try: - asyncio.run(sensor.start()) - except KeyboardInterrupt: - print("\n[*] Shutting down Web Router Decoy sensor...") diff --git a/Sensors/official/WebRouterDecoy/docker-compose.yml b/Sensors/official/WebRouterDecoy/docker-compose.yml index 98a9eab..16c4b79 100644 --- a/Sensors/official/WebRouterDecoy/docker-compose.yml +++ b/Sensors/official/WebRouterDecoy/docker-compose.yml @@ -1,9 +1,25 @@ services: - web-router-decoy: + web-decoy: + container_name: hw-web-decoy build: - context: ../../../ - dockerfile: Sensors/official/WebRouterDecoy/Dockerfile - container_name: hw-web-router-decoy + context: ../../.. + dockerfile: Sensors/official/WebRouterDecoy-GO/Dockerfile + restart: unless-stopped + + # Binds directly to the host machine to preserve Source IPs network_mode: "host" - env_file: - - .env + + # SECURITY HARDENING + user: "65532:65532" + cap_drop: + - ALL + + environment: + - HW_HUB_ENDPOINT=http://127.0.0.1:8080 + - HW_HUB_KEY=change_this_to_a_secure_random_string + - HW_SENSOR_ID=production-web-decoy-01 + - HW_TEST_MODE=false + + # Web Honeypot Configuration + - HW_BIND_PORT=8081 + - HW_ROUTER_BRAND=Cisco \ No newline at end of file diff --git a/Sensors/official/WebRouterDecoy/go.mod b/Sensors/official/WebRouterDecoy/go.mod new file mode 100644 index 0000000..01d37ff --- /dev/null +++ b/Sensors/official/WebRouterDecoy/go.mod @@ -0,0 +1,7 @@ +module github.com/honeywire/sensors/webrouterdecoy + +go 1.25.0 + +replace github.com/honeywire/sdk-go => ../../../SDKs/go-honeywire + +require github.com/honeywire/sdk-go v0.0.0-00010101000000-000000000000 diff --git a/Sensors/official/WebRouterDecoy/main.go b/Sensors/official/WebRouterDecoy/main.go new file mode 100644 index 0000000..5f702c2 --- /dev/null +++ b/Sensors/official/WebRouterDecoy/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/http" + "os" + + "github.com/honeywire/sdk-go" +) + +var ( + port = getEnv("HW_BIND_PORT", "8080") + routerBrand = getEnv("HW_ROUTER_BRAND", "Netgear") + hw *sdk.Sensor +) + +// The HTML template with %s placeholders for the router brand +const loginHTML = ` + + + + + %s Administration + + + +
+

%s Router

+

Sign in to the administration panel.

+
+ + + + + +
+ +
+ +` + +func main() { + // 1. Initialize SDK + hw = sdk.NewSensor("web_honeypot") + hw.Start() + + log.Printf("[*] HoneyWire Web Router Decoy | Brand: %s | Port: %s", routerBrand, port) + + // 2. Register Routes + http.HandleFunc("/", handleRoot) + http.HandleFunc("/login", handleLogin) + + // 3. Start the Server + addr := fmt.Sprintf("0.0.0.0:%s", port) + if err := http.ListenAndServe(addr, nil); err != nil { + log.Fatalf("[!] FATAL: Web server failed: %v", err) + } +} + +func handleRoot(w http.ResponseWriter, r *http.Request) { + // Only respond to GET requests on the root path + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + // Inject the brand name into the HTML + fmt.Fprintf(w, loginHTML, routerBrand, routerBrand) +} + +func handleLogin(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + + // Parse the submitted form data + if err := r.ParseForm(); err != nil { + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + username := r.FormValue("username") + password := r.FormValue("password") + userAgent := r.UserAgent() + if userAgent == "" { + userAgent = "Unknown" + } + + // Extract the real IP (strip the ephemeral port) + srcIP := r.RemoteAddr + if ip, _, err := net.SplitHostPort(srcIP); err == nil { + srcIP = ip + } + + log.Printf("[!] Login attempt from %s (User: %s)", srcIP, username) + + // Dispatch the Event via the SDK + hw.ReportEvent( + "web_login_attempt", + "critical", + map[string]any{ + "user_agent": userAgent, + "attempted_username": username, + "attempted_password": password, + }, + "logged", + srcIP, + "Web Interface", + ) + + // Always return 401 Unauthorized to keep them guessing + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprint(w, "

401 Unauthorized

Invalid Username or Password.

") +} + +func getEnv(key, fallback string) string { + if val, exists := os.LookupEnv(key); exists { + return val + } + return fallback +} \ No newline at end of file diff --git a/Sensors/official/WebRouterDecoy/requirements.txt b/Sensors/official/WebRouterDecoy/requirements.txt deleted file mode 100644 index e76a8fa..0000000 --- a/Sensors/official/WebRouterDecoy/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Sensor dependencies -fastapi -uvicorn[standard] -python-multipart diff --git a/Sensors/templates/go-sensor/Dockerfile b/Sensors/templates/go-sensor/Dockerfile new file mode 100644 index 0000000..f7a6612 --- /dev/null +++ b/Sensors/templates/go-sensor/Dockerfile @@ -0,0 +1,27 @@ +# STAGE 1: Build +FROM golang:1.25 AS builder + +WORKDIR /src + +# 1. Copy the shared SDK into the builder +COPY SDKs/go-honeywire ./SDKs/go-honeywire + +# 2. Copy the specific sensor code (Change 'custom/MySensor' to your folder name) +COPY Sensors/custom/MySensor ./Sensors/custom/MySensor + +# 3. Move into the sensor folder to build +WORKDIR /src/Sensors/custom/MySensor + +RUN go mod download + +# Compile the pure Go, fully static binary +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o custom-sensor ./main.go + +# STAGE 2: Distroless Sandbox +FROM gcr.io/distroless/static-debian12:nonroot + +COPY --from=builder /src/Sensors/custom/MySensor/custom-sensor /app/custom-sensor + +# Run as an unprivileged user +USER 65532 +CMD ["/app/custom-sensor"] \ No newline at end of file diff --git a/Sensors/templates/go-sensor/README.md b/Sensors/templates/go-sensor/README.md new file mode 100644 index 0000000..4126045 --- /dev/null +++ b/Sensors/templates/go-sensor/README.md @@ -0,0 +1,39 @@ +# HoneyWire Go Sensor Template + +Welcome to the HoneyWire ecosystem! This template contains everything you need to build a blazing-fast, strictly sandboxed custom Go security sensor that natively reports to the HoneyWire Hub. + +## How to Build Your Sensor + +1. **Copy this folder** to your desired location (e.g., `Sensors/custom/MyNewSensor`). +2. **Initialize the Go Module**: Link your new sensor to the local HoneyWire SDK by running these commands inside your new folder: + ```bash + go mod init github.com/honeywire/sensors/mynewsensor + go mod edit -replace github.com/honeywire/sdk-go=../../../SDKs/go-honeywire + go mod tidy + ``` +3. **Write your logic** inside `main.go`. The file is heavily commented and shows you exactly where to put your detection loops. +4. **Update `docker-compose.yml` and `Dockerfile`**: Ensure the paths point to your newly named directory. + +## Deployment + +It is highly recommended to deploy this sensor using the provided `docker-compose.yml` configuration and the provided `Dockerfile` (if building from source). The compose file orchestrates the required security capabilities, user permissions, and network modes automatically. + +```bash +docker compose up -d +``` + +## Security Architecture + +This template is architected for extreme resilience against exploits out-of-the-box. By utilizing a minimal attack surface and enforcing strict container sandboxing, it ensures your custom detection logic cannot be weaponized against the host. + +**Core Defense-in-Depth Measures:** +* **Unprivileged Execution:** Defaults to running entirely as a non-root user (`UID 65532`), preventing system-level modifications even in the event of a container breach. +* **Kernel Capability Stripping:** Drops all Linux kernel capabilities (`cap_drop: ALL`) via the Docker Compose configuration, neutralizing advanced kernel exploitation techniques. +* **Distroless Isolation:** Built on a statically-linked Distroless image. It completely lacks a shell (`/bin/sh`), package managers, or standard Linux utilities (like `curl` or `wget`), leaving attackers with zero tools to download secondary payloads or pivot to the host network. +* **In-Memory Operation:** Written in pure Go, encouraging in-memory packet or log processing without the need for vulnerable C-bindings or heavy framework dependencies. + +*Recommendation: For optimal security, always deploy this sensor using the official `docker-compose.yml` and `Dockerfile` to ensure these sandbox protections are strictly enforced by the container runtime. If your sensor doesn't follow this security standard there should be a clear reason or a feature that requires otherwise.* + +## CI/CD Requirement + +To ensure your sensor works, our GitHub Actions will run it with `HW_TEST_MODE=true`. The HoneyWire Go SDK handles this automatically! When `hw.Start()` is called in that mode, it fires a synthetic payload to the Hub and gracefully exits. You do not need to write any custom testing logic. \ No newline at end of file diff --git a/Sensors/templates/go-sensor/docker-compose.yml b/Sensors/templates/go-sensor/docker-compose.yml new file mode 100644 index 0000000..3217eb9 --- /dev/null +++ b/Sensors/templates/go-sensor/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.8' + +services: + custom-sensor: + container_name: hw-custom-sensor + build: + # Step back to the repo root so Docker can see both the Sensor and the SDK + context: ../../.. + dockerfile: Sensors/custom/MySensor/Dockerfile + restart: unless-stopped + + # Uncomment if you need to capture real Source IPs or bind to host ports + # network_mode: "host" + + # --------------------------------------------------------- + # SECURITY HARDENING + # --------------------------------------------------------- + # Non-root user for the Distroless container + user: "65532:65532" + + # Drops all kernel capabilities for maximum security + cap_drop: + - ALL + + # cap_add: + # - NET_RAW # Uncomment ONLY if your Go code requires raw network sockets + + environment: + - HW_HUB_ENDPOINT=http://127.0.0.1:8080 + - HW_HUB_KEY=change_this_to_a_secure_random_string + - HW_SENSOR_ID=custom-sensor-01 + - HW_TEST_MODE=false + - HW_SEVERITY=medium + + # Custom Variables + - HW_CUSTOM_TARGET=/tmp/honey \ No newline at end of file diff --git a/Sensors/templates/go-sensor/go.mod b/Sensors/templates/go-sensor/go.mod new file mode 100644 index 0000000..a2391b4 --- /dev/null +++ b/Sensors/templates/go-sensor/go.mod @@ -0,0 +1,7 @@ +module github.com/honeywire/sensors/custom-template + +go 1.25.0 + +replace github.com/honeywire/sdk-go => ../../../SDKs/go-honeywire + +require github.com/honeywire/sdk-go v0.0.0-00010101000000-000000000000 \ No newline at end of file diff --git a/Sensors/templates/go-sensor/main.go b/Sensors/templates/go-sensor/main.go new file mode 100644 index 0000000..03b1d9c --- /dev/null +++ b/Sensors/templates/go-sensor/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "log" + "os" + "time" + + "github.com/honeywire/sdk-go" +) + +var ( + // Load custom configuration from environment variables + severity = getEnv("HW_SEVERITY", "medium") + target = getEnv("HW_CUSTOM_TARGET", "/tmp/honey") +) + +func main() { + // 1. Initialize the HoneyWire SDK + // Change 'custom' to whatever category fits your sensor (e.g., 'network', 'file', 'auth') + hw := sdk.NewSensor("custom") + + // Start the SDK (This automatically handles Hub syncing and HW_TEST_MODE for CI/CD) + hw.Start() + + log.Printf("[*] Starting Custom Go Sensor | Target: %s | Severity: %s", target, severity) + + // 2. --- YOUR SENSOR LOGIC GOES HERE --- + // This is the main loop of your sensor. Do not let the main function exit! + for { + // Example: Waiting for an event... + time.Sleep(60 * time.Second) + + // Assume an attack happened! + log.Printf("[!] Attack detected! Gathering forensics...") + sourceIP := "192.168.1.100" // Replace with actual extracted data + + // 3. Send the alert to the Hub using the SDK's built-in method + hw.ReportEvent( + "custom_anomaly_detected", // Event Type + severity, // Severity + map[string]any{ // Details (Custom JSON payload) + "attack_type": "example_probe", + "raw_payload": "GET /etc/passwd HTTP/1.1", + }, + "logged", // Action Taken + sourceIP, // Source + "Custom Trap", // Target + ) + } +} + +// Helper function to easily grab environment variables with fallbacks +func getEnv(key, fallback string) string { + if val, exists := os.LookupEnv(key); exists { + return val + } + return fallback +} \ No newline at end of file