<a name="readme-top"></a>
<div align="center">

# Ethiack Beacon 🔌
## Secure Network Tunnel for Internal Pentesting

[Quick Start](#quick-start) •
[Installation](#installation) •
[Development](#development) •
[Testing](#testing) •
[Docker](#docker) •
[Links](#links)

</div>

## Overview

Python CLI to configure, run, and monitor Ethiack Beacons - lightweight WireGuard tunnels that give the Ethiack Hackian Engine access to an internal network.

1. The CLI registers a beacon with the Ethiack API and receives a WireGuard config + rathole tunnel credentials.
2. WireGuard runs locally on your machine, creating an encrypted tunnel.
3. A [rathole](https://github.com/rathole-org/rathole) client punches through a NAT/firewall by forwarding the local WireGuard UDP port to Ethiack's server. **No port forwarding required**.

```
Local machine                         Ethiack Infrastructure
─────────────────────────────────     ──────────────────────────
WireGuard server ◄──UDP── rathole ◄── rathole server ◄── WireGuard client
                          client      (public endpoint)
```

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Quick Start

Run the install script - it detects Docker and guides you through both paths:

```bash
curl -fsSL https://raw.githubusercontent.com/ethiack/beacon/main/install.sh | sudo bash
```

Or pre-seed your credentials for a fully automated install (e.g. from the Ethiack Portal):

```bash
curl -fsSL https://raw.githubusercontent.com/ethiack/beacon/main/install.sh \
  | ETHIACK_API_KEY=your-key ETHIACK_API_SECRET=your-secret sudo -E bash
```

The script will:
1. Detect Docker - if available, recommend the Docker Compose path (simpler, fewer support issues)
2. Otherwise install WireGuard + the beacon CLI natively, then set up a systemd/launchd service
3. Interactively collect beacon name, CIDRs, and optionally a pentest slug to automatically scope the beacon's networks

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Prerequisites

- **Linux or macOS**
- **Python 3.11** (pinned in `.python-version`)
- **[uv](https://docs.astral.sh/uv/)** - Fast Python package manager
- **WireGuard** - `wg` and `wg-quick` must be installed
- **Root / sudo** - WireGuard requires elevated privileges
- **[dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html)** (optional) - DNS forwarding for clients connected through the beacon (`dnsmasq-base` on Debian/Ubuntu)

### Installing WireGuard

```bash
# Debian / Ubuntu
sudo apt install wireguard

# Fedora / RHEL
sudo dnf install wireguard-tools

# macOS
brew install wireguard-tools
```

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Installation

Install the pinned Python version and dependencies:

```bash
uv python install
uv sync
```

This reads `uv.lock`, creates a virtual environment at `.venv/`, and installs all dependencies with exact versions.

### Environment Configuration

Copy the example file and fill in your credentials:

```bash
cp .env.example .env
```

Key variables:

```bash
ETHIACK_API_KEY=your-key
ETHIACK_API_SECRET=your-secret
```

The CLI automatically loads `.env` on startup via `python-dotenv`. Variables already set in the environment are never overridden.

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Development

### Running Commands

Activate the virtual environment:

```bash
source .venv/bin/activate
beacon create
```

Or run directly without activating:

```bash
uv run beacon create
```

Most commands that interact with WireGuard require root:

```bash
sudo -E beacon create
sudo -E beacon start <id>
sudo -E beacon stop <id>
```

### Non-interactive / Automated Deployment

All parameters can be passed via environment variables for use in Kubernetes, Helm, or Ansible:

```bash
export ETHIACK_API_KEY=your-key
export ETHIACK_API_SECRET=your-secret
export ETHIACK_BEACON_NAME=prod-cluster
export ETHIACK_BEACON_CIDRS=10.0.0.0/8,172.16.0.0/12
export ETHIACK_PENTEST_SLUG=my-pentest   # optional: auto-scope CIDRs to this pentest

sudo -E beacon create --yes
```

### Commands

| Command | Description |
|---|---|
| `beacon create` | Create and start a new beacon (interactive or via env vars) |
| `beacon list` | List all beacons for your account |
| `beacon status <id>` | Show beacon details and local service status |
| `beacon start <id>` | Start services for an existing beacon |
| `beacon stop <id>` | Stop running services |
| `beacon restart <id>` | Restart services |
| `beacon delete <id>` | Delete beacon and stop services |
| `beacon update <id>` | Update beacon configuration |
| `beacon health` | Continuously report health (every 5 min by default) |
| `beacon test <id> <target>` | Test connectivity to an internal host/URL |
| `beacon version` | Show CLI version |

### Environment Variables

| Variable | Required | Default | Description |
|---|---|---|---|
| `ETHIACK_API_KEY` | Yes | - | API key |
| `ETHIACK_API_SECRET` | Yes | - | API secret |
| `ETHIACK_API_URL` | No | `https://api.ethiack.com` | API base URL |
| `ETHIACK_ORG_ID` | No | auto | Organization ID (auto-selected if your key has access to only one org) |
| `ETHIACK_BEACON_ID` | No | - | Beacon ID to start (Docker mode) |
| `ETHIACK_BEACON_NAME` | No | - | Beacon name (skips prompt) |
| `ETHIACK_BEACON_CIDRS` | No | - | Comma-separated CIDRs (skips prompt) |
| `ETHIACK_BEACON_WG_PORT` | No | auto | Local WireGuard UDP port |
| `ETHIACK_PENTEST_SLUG` | No | - | Pentest slug to add beacon CIDRs to as scope assets |
| `ETHIACK_BEACON_HEALTH_INTERVAL` | No | `300` | Health report interval (seconds) |
| `ETHIACK_STATE_DIR` | No | `~/.ethiack` | CLI state, rathole binary, and PID files |

### Working with Dependencies

Add a new dependency:

```bash
uv add package-name              # Latest version
uv add "package-name>=1.2.0"    # Minimum version
```

Remove a dependency:

```bash
uv remove package-name
```

> [!IMPORTANT]
> Always use `uv add` and `uv remove` instead of manually editing `pyproject.toml`. This ensures the lock file stays in sync.

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Testing

Run the full test suite:

```bash
uv run pytest
```

Run specific files or markers:

```bash
uv run pytest tests/test_api.py
uv run pytest -m unit
uv run pytest -m "not slow"
```

Coverage reports are written to `htmlcov/` and `tests_report/` after each run.

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Docker

The container runs `beacon create` or `beacon start` on startup, then keeps the health reporter running in the foreground.

### Building

```bash
docker compose build
```

### Running with Docker Compose

Copy the example env file and fill in your values:

```bash
cp .env.example beacon.env
```

> [!IMPORTANT]
> Use separate-line comments only in `beacon.env`. Docker's `env_file` parser does not strip inline comments - `KEY=value # comment` would set the variable to `value # comment`.

Start the container:

```bash
docker compose up -d
docker compose logs -f
```

### Two Deployment Modes

**Mode 1 - Start a pre-existing beacon** (recommended for production):

```bash
# beacon.env
ETHIACK_API_KEY=your-key
ETHIACK_API_SECRET=your-secret
ETHIACK_BEACON_ID=<beacon-id>
```

Create the beacon once with the CLI, then deploy the container with its ID. On every restart the container calls `beacon start <id>` and picks up from where it left off.

**Mode 2 - Create on first run** (provisioning):

```bash
# beacon.env
ETHIACK_API_KEY=your-key
ETHIACK_API_SECRET=your-secret
ETHIACK_BEACON_NAME=my-beacon
ETHIACK_BEACON_CIDRS=192.168.1.0/24,10.0.0.0/8
# ETHIACK_PENTEST_SLUG=my-pentest  # optional: auto-scope CIDRs to this pentest
```

The container calls `beacon create --yes` on startup. Multiple CIDRs can be provided as a comma-separated list. State is persisted in the `beacon-data` Docker volume so restarts are safe.

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>

## Links

- **Repository**: https://github.com/ethiack/beacon
- **Rathole**: https://github.com/rathole-org/rathole
- **WireGuard**: https://www.wireguard.com

<p align="right"><small>(<a href="#readme-top">back to top</a>)</small></p>
