docs(i18n): 同步 24 篇旧英文文档与中文最新内容

guide 9 篇(quick-start/first-site/sso/troubleshooting/tunnel-usage/waf-usage/waf-ip-group-expr/credits/index)、deployment 7 篇(deployment/server/agent/relay/openflared/upgrade/index)、reference 3 篇(configuration/cli/index)、design 5 篇(architecture/agent-design/tunnel-design/waf-design/index)全部按中文最新版重写同步;waf-usage/waf-design 按新版 DAG 模型重写;修复 reference 中文锚点链接;vitepress 构建 43 个英文页面全绿
This commit is contained in:
ryan
2026-08-16 23:27:18 +08:00
parent 454542c1d0
commit e7b8fb2f99
24 changed files with 1985 additions and 2361 deletions
+14 -12
View File
@@ -1,27 +1,29 @@
# Credits
OpenFlare is essentially a solution integration project. During its design and implementation phases, it drew inspiration from the exceptional concepts, architectural designs, and technical achievements of numerous open-source projects. Below are the key upstream open-source projects OpenFlare relies on for its core engine, security mechanisms, and backend/frontend system frameworks, along with our sincere thanks to these projects and their active communities.
OpenFlare draws on the excellent ideas, architectures, and technical implementations of many open-source projects during design and development. Below are the key open-source projects referenced in OpenFlare's core underlying engines, security mechanisms, and frontend/backend frameworks. We thank these projects and their communities.
---
### 1. OpenResty
* **Project Positioning**: A high-performance Web platform based on Nginx and Lua.
* **Role in OpenFlare**: Acts as the edge gateway for the global Data Plane. All public web traffic is received by OpenResty first, where high-concurrency HTTPS handshakes, WAF security rule evaluations, and PoW CC verification are performed before executing reverse proxies.
* **Project Link**: [OpenResty Official Website](https://openresty.org/)
* **Positioning**: a high-performance web platform based on Nginx and Lua.
* **Role in OpenFlare**: the edge gateway of the global data plane. All public web traffic is first received by OpenResty, where high-concurrency HTTPS handshakes, WAF security rule matching, anti-CC human verification, and finally reverse proxy forwarding are performed.
* **Link**: [OpenResty official site](https://openresty.org/)
### 2. FRP (Fast Reverse Proxy)
* **Project Positioning**: A high-performance reverse proxy application focused on intranet penetration.
* **Role in OpenFlare**: Serves as the underlying tunnel engine for the intranet penetration subsystem. The relay-side manager `openflare-relay` is responsible for running and scheduling the `frps` engine, while the intranet client `openflared` is responsible for generating TOML configurations locally and running the multiplexed `frpc` subprocesses.
* **Project Link**: [fatedier/frp (GitHub)](https://github.com/fatedier/frp)
* **Positioning**: a high-performance reverse proxy application focused on intranet penetration.
* **Role in OpenFlare**: the underlying tunnel engine of the intranet penetration subsystem. The relay-side manager `openflare-relay` guards and schedules the `frps` engine, while the intranet client `openflared` auto-generates TOML config locally and guards multiplexed `frpc` child processes.
* **Link**: [fatedier/frp (GitHub)](https://github.com/fatedier/frp)
---
### 3. Anubis (PoW Solution)
* **Project Positioning**: A lightweight human-machine verification and protection solution based on Proof of Work (PoW).
* **Role in OpenFlare**: Provides the core **seamless PoW CC challenge** capabilities for the gateway WAF.
### 3. Anubis (PoW solution)
* **Positioning**: a lightweight human-verification protection solution based on Proof of Work.
* **Role in OpenFlare**: provides the core **invisible anti-CC human challenge** capability for the gateway WAF.
---
### 4. gin-template
* **Project Positioning**: A modern full-stack development boilerplate based on Go Gin and frontend builds.
* **Role in OpenFlare**: Provided the standard, unified backend/frontend system architecture baseline for the OpenFlare control plane (Server).
* **Positioning**: a modern full-stack development scaffold template based on Go Gin and frontend builds.
* **Role in OpenFlare**: provides a canonical, unified frontend/backend system architecture prototype for the OpenFlare control plane (Server).
---
+53 -81
View File
@@ -1,100 +1,72 @@
# Publishing Your First Site
# Publish First Configuration
You will learn: How to create your first website configuration, bind origins and certificates, publish the configuration version, and verify that the Agent applied it successfully.
You will learn: how to create the first reverse proxy rule in the simplest way, publish a config version, and confirm the Agent has pulled and applied the config.
The publishing pipeline of OpenFlare centers on a complete configuration version snapshot. After modifying website configurations in the management console, you need to publish and activate the new version to let the Agent pull and apply it in the next heartbeat.
OpenFlare's release chain centers on "immutable config versions". After you modify rules in the admin panel, you must publish and activate a new version for online Agents to auto-sync and apply.
## Pre-publish Checks
---
Verify that the following conditions are met:
## Pre-Release Checks
| Item | Expectation |
Before starting, make sure the following conditions are met:
| Check | Required State |
| --- | --- |
| Server | Management console is accessible and log-in succeeds |
| Agent | At least one node is online |
| Origin | The Agent node can reach the origin server address |
| Domain | Domain is resolved to the OpenResty node, or prepared to verify via local `hosts` / `curl` Host header |
| HTTPS | If HTTPS is required, the certificate is uploaded or hosted |
| **Server** | control panel started normally and you can log in to the admin panel |
| **Agent** | at least one Agent node online (confirmable in「Node Management」) |
| **Origin** | your backend origin service is reachable from the Agent host |
| **Domain/testing** | the domain's DNS resolves, or you're ready to test with local hosts / curl Host header on the client |
## Create Website Configuration
---
A new website configuration requires at least:
## Step 1: Create the First Website Config
| Field | Description |
| --- | --- |
| Website Name | Business unique identifier; the primary domain is used if left blank |
| Domain | At least one domain, where the first is treated as the primary domain |
| Origin Address | A valid `http://` or `https://` upstream address |
| Enabled Status | Only enabled website configurations will participate in publishing and rendering |
For a quick verification, deploy a basic HTTP reverse proxy site first:
Example:
1. Log in to the control panel, go to **「Website Management」->「Domain List」** in the left navigation, click **「Add Zone」**.
2. Fill in the domain config:
* **Domain**: enter the test domain (e.g. `first.example.com`).
* **Bind Certificate**: choose not to bind a certificate (for HTTP quick verification).
* Click save to complete domain registration.
3. Go to **「Rule Management」**, click **「New Rule」**:
* **Rule Name**: enter a simple identifier (e.g. `first-app-route`).
* **Domain Match**: fill in your test domain (e.g. `first.example.com`).
* In the **「Reverse Proxy」** tab below, set the origin mode to「Direct Upstream」.
* **Upstream Address**: fill in the backend service address (e.g. the test-only `http://httpbin.org`).
* Click save to create the rule.
| Field | Example |
| --- | --- |
| Website Name | `app` |
| Domain | `app.example.com` |
| Origin Address | `http://10.0.0.20:8080` |
> [!TIP]
> **About HTTPS and certificate preparation**
> This section only guides the quick deployment of a basic HTTP rule. To import an existing SSL certificate or auto-issue one from Let's Encrypt via ACME and enable HTTPS proxying on port 443, go to [Create a Reverse Proxy Config](./proxy-config.md) for detailed steps.
A single domain can belong to only one website configuration. Rate limiting, reverse proxy, and caching parameters are shared site-wide.
---
## Bind Certificate
## Step 2: Preview and Publish a Config Version
HTTPS certificates are bound by domain. Domains without a bound certificate will not be placed into `443 ssl` server blocks automatically.
The new website config is still a draft in the Server database and needs a released version to be distributed to the data plane:
If a website contains multiple domains, the rendering pipeline groups the HTTPS configurations by certificate while ensuring all domains belong to the same site snapshot.
1. Click the **「Preview and Publish」** button in the top-right of the control panel; the system shows the physical config file diff for the newly added route.
2. After confirming the rendered config is correct, click **「Confirm Publish」**.
3. The control plane generates a unique config version number (format `YYYYMMDD-NNN`).
## Publish & Activate
---
Standard Pipeline:
## Step 3: Verify the Agent Applied It
```text
Modify rules -> Preview / Diff -> Publish -> Generate complete version -> Activate version -> Agent pulls -> Local application -> Report result
```
After publishing, the control plane immediately notifies online Agents via WebSocket (if the WebSocket is offline, the Agent detects it as a diff in its heartbeat):
During publication, the Server reads all enabled website configurations, the main OpenResty config templates, performance and cache parameters, rendering the complete OpenResty configuration and calculating its `checksum`, saving to `config_versions`, and switching the active version.
## Verify Results
Verify in the management console after publishing:
| Position | Expected Result |
| --- | --- |
| Node List | Node status is online |
| Node Details | Current version matches active version |
| Apply Logs | Most recent application succeeded |
| Version Page | The new version is currently active |
Verify Agent logs on the node:
```bash
journalctl -u openflare-agent -n 100 --no-pager
```
Access via domain:
```bash
curl -I http://app.example.com
```
If the domain has not been officially resolved, you can verify by specifying the Host header against the node IP:
```bash
curl -I -H 'Host: app.example.com' http://NODE_IP
```
HTTPS Validation:
```bash
curl -I https://app.example.com
```
## Rollback
If a target version application fails and triggers a rollback, the Agent blocks repeated synchronization of the same failing `version + checksum` until the active version or checksum changes on the control plane.
Roll back to an older version:
1. Open the Configuration Versions page.
2. Locate the last known good historic version.
3. Re-activate that version.
4. Check the node application logs to verify that the Agent successfully applied the rollback.
1. **Admin-side verification**: go to「Node Management」-> click the node to open details; check that the**current version number** has changed to the just-published latest active version and the「Apply Records」show success.
2. **Edge node verification**: check application via logs on the Agent host:
```bash
# If the Agent is Docker-deployed
docker logs openflare-agent
# If the Agent is deployed with local systemd
journalctl -u openflare-agent -n 50 --no-pager
```
3. **Connectivity test**:
On the client machine, use `curl` with a test Host header against the Agent node's IP for final verification:
```bash
curl -I -H "Host: first.example.com" http://AGENT_NODE_IP
```
If the returned status code matches the backend origin's response, your first reverse proxy rule has successfully landed on the edge node!
+34 -27
View File
@@ -1,43 +1,50 @@
# Guide Overview
# Guide
You will learn: How the OpenFlare documentation is organized, which pages to read when running it for the first time, and where to start for deployment, usage, troubleshooting, and development.
You will learn: how the OpenFlare docs are organized, which pages to read on first run, and where to start for deployment, usage, and troubleshooting.
OpenFlare is a self-hosted OpenResty control plane. It integrates reverse proxy website configurations, configuration version publishing, Agent node synchronization, TLS certificates, and basic observability into a single management console, making it ideal for a single team or organization managing multiple proxy nodes.
OpenFlare is a self-hosted OpenResty control plane. It brings reverse proxy website configs, config version release, Agent node sync, TLS certificates, and basic observability into one admin panel — suitable for a single team or organization managing multiple proxy nodes.
## Recommended Reading Path
If you are new to OpenFlare, read the documents in the following order:
If you're new to OpenFlare, read in this order:
1. [Quick Start](./quick-start.md): Start the Server using Docker Compose, log into the management console, and connect your first Agent.
2. [Basic Usage](./usage.md): Learn common operations for website configs, origins, certificates, publishing, rollbacks, and observability.
3. [Tunnel & Intranet Penetration](./tunnel-usage.md): Learn to deploy Relay and Client to achieve secure, public IP-free reverse penetration.
4. [WAF Security Protection](./waf-usage.md): Master IP whitelisting/blacklisting, WAF auto IP group aggregation Expr rules, geographical restrictions, and PoW CC protection.
5. [WAF Auto IP Group Expressions](./waf-ip-group-expr.md): Write auto IP group Expr rules and learn keyword definitions and presets.
6. [Deployment Guide](../deployment/deployment.md): Deploy Server and Agent in closer-to-production environments.
7. [Configurations Reference](../reference/configuration.md): Check Server environment variables, runtime Options, and Agent configurations.
8. [Troubleshooting](./troubleshooting.md): Troubleshoot login, database, node sync, OpenResty application, and frontend build issues.
1. [Quick Start](./quick-start.md): start the Server with Docker Compose, log in to the admin panel, and connect your first Agent.
2. [Publish First Configuration](./first-site.md): quickly create a basic HTTP reverse proxy site rule and verify the node applied it.
3. [Create a Reverse Proxy Config](./proxy-config.md): step by step, from certificate import and application to HTTPS, upstream origins, and edge cache.
4. [Zone Domain Migration](./zone-domain-migration.md): upgrade from legacy managed domains / inline route domains to the Zone model (automatic goose import), with backup, acceptance, and rollback notes.
5. [Pages Static Hosting Usage](./pages-usage.md): static project ZIP upload limits, SPA Fallback, and built-in API reverse proxy config.
6. [Tunnel & Intranet Penetration](./tunnel-usage.md): deploy Relay and Client for secure reverse penetration without a public IP.
7. [WAF Security Protection](./waf-usage.md): configure WAF rule groups; master IP allow/block lists, auto/subscription IP groups, geo restrictions, and PoW CC protection.
8. [WAF Auto IP Group Expressions](./waf-ip-group-expr.md): write auto IP group Expr rules; understand keyword meanings and preset rules.
9. [Uptime Kuma Monitoring Sync](./uptime-kuma.md): configure Uptime Kuma auto differential sync and monitor scope control.
10. [SSO Login Configuration](./sso.md): configure OIDC for third-party single sign-on (SSO).
11. [Troubleshooting](./troubleshooting.md): troubleshoot login, database, node sync, OpenResty, and edge cache hit issues by symptom.
12. [Credits](./credits.md): the excellent open-source projects and community acknowledgments this system depends on.
## Role-Based Entrypoints
## Find by Role
| What do you want to do? | Recommended Entrance |
| What you want to do | Recommended Entry |
| --- | --- |
| Run the console in under 5 minutes | [Quick Start](./quick-start.md) |
| Publish your first reverse proxy configuration | [Publish First Configuration](./first-site.md) |
| Get the admin panel running in 5 minutes | [Quick Start](./quick-start.md) |
| Publish your first reverse proxy config | [Publish First Configuration](./first-site.md) |
| Configure domain certs, reverse proxy, and edge cache | [Create a Reverse Proxy Config](./proxy-config.md) (incl. cache notes) |
| Static assets not hitting cache | [Troubleshooting · Edge Cache](./troubleshooting.md#edge-cache-hit-rate-anomalies) |
| Host an SPA or static website | [Pages Static Hosting Usage](./pages-usage.md) |
| Configure intranet penetration mapping | [Tunnel & Intranet Penetration](./tunnel-usage.md) |
| Configure CC protection & IP group blocking | [WAF Security Protection](./waf-usage.md) |
| Write auto IP group aggregation rules | [WAF Auto IP Group Expressions](./waf-ip-group-expr.md) |
| Configure anti-CC and IP group blocking | [WAF Security Protection](./waf-usage.md) |
| Write auto IP group rules | [WAF Auto IP Group Expressions](./waf-ip-group-expr.md) |
| Auto-sync monitored site status | [Uptime Kuma Monitoring Sync](./uptime-kuma.md) |
| Connect or reinstall a node Agent | [Access Agent](../deployment/agent.md) |
| Start Server from source code | [Launch Server](../deployment/server.md) |
| Configure GitHub or OIDC SSO | [SSO Login Configuration](./sso.md) |
| Start the Server from source | [Start the Server](../deployment/server.md) |
| Configure OIDC login | [SSO Login Configuration](./sso.md) |
| Upgrade Server or Agent | [Upgrade & Maintenance](../deployment/upgrade.md) |
| Participate in development or bug fixing | [Local Development](../design/development.md) and [Development Constraints](../../guideline/Constraints.md) |
| Understand architecture and publishing | [System Architecture](../design/architecture.md) and [Agent & Publish Model](../design/agent-design.md) |
| View open-source references and credits | [Credits](./credits.md) |
| Understand the architecture and release model | [System Architecture](../design/architecture.md) and [Agent & Publish Model](../design/agent-design.md) |
| See open-source references and acknowledgments | [Credits](./credits.md) |
## Documentation Partitions
## Doc Sections
`guide/` is oriented toward users and deployers, providing actionable steps from installation to daily operations.
`guide/` targets users and deployers with executable steps from install to daily operations.
`reference/` collects stable facts such as configuration fields, commands, API response structures, and repository layout.
`reference/` consolidates stable facts: config fields, commands, API response conventions, and repository structure.
`design/` is oriented toward maintainers and contributors, describing product boundaries, system architecture, Agent & publishing models, and engineering constraints. Before adding capabilities or changing boundaries, update the corresponding design document first.
`design/` targets maintainers and contributors, describing product boundaries, system architecture, the Agent & publish model, and engineering constraints. Before adding capabilities or changing boundaries, update the corresponding design doc first.
+124 -102
View File
@@ -1,69 +1,94 @@
# Quick Start
You will learn: How to start OpenFlare Server using Docker Compose, complete your first login, connect your first Agent, and verify if a configuration has been published to the node.
You will learn: how to start the OpenFlare Server with Docker Compose, complete the first login, connect your first Agent, and verify that a config has been published to a node.
The minimum running unit of OpenFlare consists of:
OpenFlare's minimal runtime consists of:
| Component | Responsibility |
| --- | --- |
| Server | Admin UI, Admin API, Agent API, configuration rendering, version publishing, and state storage. |
| Agent | Runs on the proxy node, pulls configurations, writes files for OpenResty, executes validations, and triggers reloads. |
| OpenResty | Receives actual traffic and reverse proxies it to origin servers. |
| Server | admin UI, admin API, Agent API, config rendering, version release, and state storage |
| Agent | runs on proxy nodes; pulls config, writes OpenResty, executes validation and reload |
| OpenResty | actually receives traffic and reverse proxies to origins |
The Agent manages the runtime through the OpenResty binary. A local deployment requires the `openresty` executable to be already present on the node; a Docker deployment can directly run the Agent image containing built-in OpenResty.
The Agent uniformly controls the runtime via the OpenResty binary. Local deployment requires an `openresty` executable on the node; Docker deployment can directly run the Agent image with a built-in OpenResty.
## Environment Requirements
| Item | Requirement |
| --- | --- |
| Docker / Docker Compose | Used to start Server and PostgreSQL; also used to run the Agent if using the Docker Agent image |
| OpenResty | Required to have the `openresty` executable when installing the Agent locally, or specify its path in the installation script |
| Reachable Ports | The Server listens on port `3000` by default; the Agent node needs to be able to reach the Server address |
| Browser | Used to access the management console |
| Docker / Docker Compose | starts the Server and its PostgreSQL, Valkey dependencies; also runs the Agent if using the Docker Agent |
| OpenResty | local Agent installs need an executable `openresty`, or specify the path in the install script |
| Reachable port | Server listens on `3000` by default; Agent nodes must be able to reach the Server address |
* **Docker**: `20.10.0+`
* **Docker Compose**: `2.0.0+`
---
## 1. Start the Server
Create a `docker-compose.yml` file in an empty directory:
Quick start recommends the standard **PostgreSQL + Valkey** deployment.
Create a `docker-compose.yaml` in an empty directory:
```yaml
version: '3.8'
services:
openflare:
image: ghcr.io/rain-kl/openflare:latest
container_name: openflare-server
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- openflare_uploads:/app/uploads
environment:
TZ: Asia/Shanghai
APP_SESSION_SECRET: 'replace-with-a-long-random-string' # replace with a long random string in production
DB_ENABLED: "true"
DB_HOST: "postgres"
DB_PORT: "5432"
DB_USERNAME: "${DB_USERNAME:-openflare}"
DB_PASSWORD: "${DB_PASSWORD:-replace-with-strong-password}"
DB_NAME: "${DB_NAME:-openflare}"
REDIS_ENABLED: "true"
REDIS_ADDR: "redis:6379"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: openflare
POSTGRES_USER: openflare
POSTGRES_PASSWORD: replace-with-strong-password
POSTGRES_DB: ${DB_NAME:-openflare}
POSTGRES_USER: ${DB_USERNAME:-openflare}
POSTGRES_PASSWORD: ${DB_PASSWORD:-replace-with-strong-password}
volumes:
- postgres-data:/var/lib/postgresql/data
- openflare_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U openflare -d openflare"]
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-openflare} -d ${DB_NAME:-openflare}"]
interval: 10s
timeout: 5s
retries: 5
openflare:
image: ghcr.io/rain-kl/openflare:latest
redis:
image: valkey/valkey:8.0-alpine
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "3000:3000"
environment:
SESSION_SECRET: replace-with-a-long-random-string
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
GIN_MODE: release
LOG_LEVEL: info
command: ["valkey-server", "--appendonly", "yes"]
volumes:
- openflare-data:/data
- openflare_redis_data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres-data:
openflare-data:
openflare_uploads:
openflare_postgres_data:
openflare_redis_data:
```
Start the services:
@@ -72,46 +97,59 @@ Start the services:
docker compose up -d
```
Verify that the containers are running:
Confirm the containers are running:
```bash
docker compose ps
docker compose logs -f openflare
```
Once you see `server listening` in the logs and the `openflare` container status is running, access:
After seeing `server listening` and the `openflare-server` container status running, open in a browser:
```text
http://localhost:3000
```
Default credentials:
Default account:
| Username | Password |
| --- | --- |
| `root` | `123456` |
| `admin` | `12345678` |
Please change the default password immediately after your first login.
> [!WARNING]
> For your system's security, change the default password immediately after the first login.
## 2. Prepare Agent Token
If you forget the password and no password-recovery channel is configured, reset it with:
The Agent can be connected using one of two types of credentials:
```bash
go run main.go reset-passwd --user admin
```
| Credential | Applicable Scenario |
Without `--password`, the command auto-generates a random password and prints it to the terminal; you can also explicitly specify a new password with `--password`.
---
## 2. Prepare an Agent Token
Agents can connect with two credential types:
| Credential | Use Case |
| --- | --- |
| `discovery_token` | Automatically registers a node for the first time, which the Server exchanges for a node-specific Token |
| `agent_token` | Node has already been created/allocated in the management console, directly uses this node-specific Token |
| `discovery_token` | first-time auto registration; the Server exchanges it for a node-specific Token |
| `agent_token` | node already created or assigned in the admin panel; use the node-specific Token directly |
After preparing one of these credentials in the management console, proceed to the next step.
Prepare one of these credentials in the admin panel, then continue.
* **`discovery_token`** path: "System Settings" -> "Auto Registration"
* **`agent_token`** path: "Node Management" -> "Add Node"
- **`discovery_token`** menu path:「System Settings」->「OpenFlare」tab ->「Discovery Token & Deployment」→ Discovery Token
- **`agent_token`** menu path: after creating a node in「Node Management」, click into the node detail page to see its dedicated Token.
## 3. Install/Run the Agent
---
The recommended Agent deployment method is using Docker (which runs the Agent image with built-in OpenResty); deploying the Agent locally on the host using the installation script is also supported.
## 3. Install / Run the Agent
### Option A: Run Agent in Docker (Recommended)
Docker image deployment is recommended; you can also deploy to the local host via the install script.
### Option A: Run the Agent with Docker (recommended)
Run the Agent image directly on the proxy node:
@@ -119,18 +157,18 @@ Run the Agent image directly on the proxy node:
docker pull ghcr.io/rain-kl/openflare-agent:latest
docker rm -f openflare-agent 2>/dev/null || true
docker run -d --name openflare-agent --restart unless-stopped \
-p 80:80 -p 443:443 \
-v openflare-agent-data:/data \
-p 80:80 -p 443:443/tcp -p 443:443/udp \
-v openflare-agent-pages:/data/var/lib/openflare/pages \
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
ghcr.io/rain-kl/openflare-agent:latest
```
### Option B: Execute Installation Script (Local Host Deployment)
### Option B: Run the install script (local deployment)
Execute the installation script on the proxy node.
Run the install script on the proxy node.
Using the `discovery_token`:
With `discovery_token`:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -138,7 +176,7 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--discovery-token YOUR_DISCOVERY_TOKEN
```
Using the node-specific `agent_token`:
With the node-specific `agent_token`:
```bash
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
@@ -146,74 +184,58 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
--agent-token YOUR_AGENT_TOKEN
```
The script defaults to:
The script defaults:
| Item | Default Value |
| Item | Default |
| --- | --- |
| Install Directory | `/opt/openflare-agent` |
| Config File | `/opt/openflare-agent/agent.json` |
| systemd Service | `openflare-agent.service` |
| OpenResty Path | Automatically detects `openresty` if unspecified |
| Install directory | `/opt/openflare-agent` |
| Config file | `/opt/openflare-agent/agent.json` |
| systemd service | `openflare-agent.service` |
| OpenResty path | auto-finds `openresty` when unspecified |
Verify the Agent service status:
Confirm the Agent service state:
```bash
systemctl status openflare-agent
journalctl -u openflare-agent -f
```
If systemd is not available on the OS, the script outputs manual startup commands instead.
Without systemd, the script prints a manual start command.
## 4. Publish Your First Configuration
---
Perform the following operations in the management console:
## 4. Next Steps
1. Add a website configuration, filling in the website name, domain, and origin address.
2. Verify that the website configuration is enabled.
3. Check the preview or change summary before publishing.
4. Publish and activate the new version.
5. Wait for the Agent to detect and apply the version in the next heartbeat.
After starting the control panel and connecting an Agent node, you've successfully built the base runtime environment of the OpenFlare gateway. Continue with these two guides to deploy your first reverse proxy site:
The version number format is `YYYYMMDD-NNN`. Historic versions are immutable; rollbacks are accomplished by re-activating an older version.
1. **Publish your first website**:
* See [Publish First Configuration](./first-site.md). It guides you to publish your first proxy rule in the simplest way (plain HTTP) and verify the node applied it.
2. **Full reverse proxy config (HTTPS & origin management)**:
* See [Create a Reverse Proxy Config](./proxy-config.md). It guides you from certificate import/application to domain HTTPS certificate binding, origin management, and preview release.
## 5. Verify Success
---
Confirm in the management console:
## When You Hit Problems
| Position | Expected Result |
| --- | --- |
| Node List | Agent node status is online |
| Node Details | Current version matches active version |
| Apply Logs | Most recent application succeeded |
| Version Page | The new version is currently active |
Handle in this order:
Confirm on the Agent node:
1. Upgrade Server and Agent to the latest version; confirm whether the problem persists.
2. Re-publish and activate a config version, wait for the node to apply.
3. Run「Force Sync」on the target node in the node detail page to push an immediate config pull.
4. Rebuild or reinstall the Agent (re-run the install script).
5. If none of the above works, file a [GitHub Issue](https://github.com/Rain-kl/OpenFlare/issues) with the Server logs and node apply records.
```bash
journalctl -u openflare-agent -n 100 --no-pager
```
## Common Failures
| Symptom | Troubleshooting Direction |
| --- | --- |
| Management console fails to load in browser | Verify that the Server is running in `docker compose ps` and port `3000` is not bound by other processes |
| Data fails to save after logging in | Check the health of the PostgreSQL container, and verify the username, password, and database name in `DSN` |
| Agent fails to register | Verify that the Agent node can reach `--server-url`, and verify if the Token is typed correctly or expired |
| Agent is online but configuration is not applied | Verify that the website configuration is enabled and a version has been published and activated |
| OpenResty application fails | Review node application logs and `journalctl -u openflare-agent`, checking domains, certificates, upstreams, and port conflicts |
For more troubleshooting details, see [Troubleshooting](./troubleshooting.md).
More troubleshooting: [Troubleshooting](./troubleshooting.md).
---
## Advanced Deployment Guides
Once you complete the quick start and familiarize yourself with the basic operations of OpenFlare, you can read the following advanced deployment documents to put components into production:
After completing the quick start and getting familiar with OpenFlare, read these advanced deployment docs to put components into production:
* **Server Production Deployment**: Read [Launch Server](../deployment/server.md) to learn how to build the frontend from source, configure system environment variables, and run with Docker Compose.
* **Agent Production Integration**: Read [Deploy Agent](../deployment/agent.md) to learn about systemd-based service management, detailed local configuration parameters, and troubleshooting.
* **Tunnel Relay Deployment**: Read [Deploy Relay](../deployment/relay.md) to learn how to configure public relay nodes (frps) for penetration tunnels.
* **Tunnel Client Deployment**: Read [Deploy OpenFlared](../deployment/openflared.md) to learn how to run the penetration daemon client (frpc) on the intranet server side.
* **Production Deployment Topology**: Read [Deployment Guide](../deployment/deployment.md) to learn about high-availability production topologies and overall network planning.
* **System Upgrades & Maintenance**: Read [Upgrade & Maintenance](../deployment/upgrade.md) to learn how to upgrade the Server and individual node Agents smoothly.
* **Server production deployment**: read [Start the Server](../deployment/server.md) for building the frontend from source, system env vars, and Docker Compose.
* **Agent production access**: read [Access Agent](../deployment/agent.md) for systemd service management, detailed local config file fields, and troubleshooting.
* **Intranet relay deployment**: read [Deploy Relay](../deployment/relay.md) for configuring public relay nodes (frps) for tunnels.
* **Intranet client deployment**: read [Deploy OpenFlared](../deployment/openflared.md) for running the tunnel daemon client (frpc) on the intranet server.
* **Production topology reference**: read [Deployment Guide](../deployment/deployment.md) for production HA topology and overall network planning.
* **Upgrades and maintenance**: read [Upgrade & Maintenance](../deployment/upgrade.md) for smooth upgrades of the Server and Agent nodes.
+41 -62
View File
@@ -1,106 +1,85 @@
# SSO Login Configuration
You will learn: How to configure GitHub OAuth or standard OIDC login portals for OpenFlare, how to fill in callback URLs, and how third-party accounts bind to existing local users.
You will learn: how to configure an OIDC third-party login entry for OpenFlare, fill in the callback URL, and how third-party accounts bind to local users.
OpenFlare supports third-party logins configured via Authentication Sources. Currently, GitHub OAuth and standard OIDC Providers (e.g., Logto, authentik, Keycloak, Casdoor) are supported.
OpenFlare connects third-party login through OIDC auth sources. Any service providing standard OIDC Discovery (Google, Keycloak, authentik, Logto, Casdoor, etc.) can be integrated.
Once an Authentication Source is configured and enabled, it displays in the third-party login section of the login page. Users can log in using their third-party accounts or bind their third-party accounts to their current local account while logged in.
After an auth source is configured and enabled, it appears in the third-party account login area on the login page. Users can log in with a third-party account, or bind a third-party account to the current local account while logged in.
## Prerequisites
Before starting, prepare the following:
| Item | Description |
| --- | --- |
| OpenFlare URL | The actual URL accessed by user browsers, e.g., `https://openflare.example.com` |
| Auth Source Name | Unique internal identifier in OpenFlare, e.g., `github`, `company-oidc` |
| Client ID | Provided after creating an application in the third-party platform |
| Client Secret | Provided after creating an application in the third-party platform |
| OIDC Discovery URL | Required for OIDC only, e.g., `https://idp.example.com/.well-known/openid-configuration` |
| Server access URL | configured in admin「System Settings」->「System Settings」tab ->「General Settings」; must match the address users' browsers actually visit (protocol, domain, port) |
| Auth source name | unique identifier inside OpenFlare, e.g. `company-oidc` |
| Client ID | provided after creating the app on the third-party platform |
| Client Secret | provided after creating the app on the third-party platform |
| OIDC Discovery URL | e.g. `https://idp.example.com/.well-known/openid-configuration` |
**Verify that "System Settings -> General Settings -> Server Address" accurately matches your domain name.**
The Auth Source name can only contain letters, numbers, hyphens, or underscores, and must start with a letter or number. The Auth Source name will appear in the callback URL; if you modify the name after saving, you must simultaneously modify the callback URL on the third-party platform.
The auth source name may only contain letters, digits, hyphens, or underscores, and must start with a letter or digit.
## Callback URL
The Redirect URI / Callback URL in third-party platforms is formatted as:
The Redirect URI / Callback URL on the third-party platform is fixed to:
```text
<OpenFlare URL>/oauth/<Auth Source Name>
<server access URL>/login
```
Example:
For example, with a server access URL of `https://openflare.example.com`:
```text
https://openflare.example.com/oauth/github
https://openflare.example.com/oauth/company-oidc
https://openflare.example.com/login
```
When creating or editing an authentication source in the management console, the form automatically generates the callback URL based on your current browser URL and the Auth Source name you entered.
## Configure GitHub Login
1. Create an OAuth App in GitHub.
2. Fill `Homepage URL` with your OpenFlare URL.
3. Fill `Authorization callback URL` with the callback URL generated in OpenFlare, e.g., `https://openflare.example.com/oauth/github`.
4. Copy the Client ID and Client Secret provided by GitHub.
5. Log into the OpenFlare management console, go to "Settings -> System Settings -> Configure Authentication Sources".
6. Add an authentication source, choosing `GitHub` as the type.
7. Fill in the Auth Source name, display name, Client ID, and Client Secret.
8. The Scope defaults to `user:email`, which usually requires no modification.
9. Save and enable the authentication source.
Once enabled, the corresponding GitHub login button will display on the login page.
The callback URL only relates to the「server access URL」and does not include the auth source name. After the third-party platform completes authorization, it redirects here, and the OpenFlare login page uses the authorization code to complete login or binding.
## Configure OIDC Login
1. Create an application or client in your OIDC Provider.
2. Select Web / Confidential Client as the application type.
3. Fill `Redirect URI / Callback URL` with the callback URL generated in OpenFlare, e.g., `https://openflare.example.com/oauth/company-oidc`.
4. Copy the Client ID and Client Secret.
5. Retrieve the Provider's Discovery URL, which usually ends with `/.well-known/openid-configuration`.
6. Log into the OpenFlare management console, go to "Settings -> System Settings -> Configure Authentication Sources".
7. Add an authentication source, choosing `OIDC` as the type.
8. Fill in the Auth Source name, display name, Client ID, Client Secret, and OIDC Discovery URL.
9. Scope defaults to `openid profile email`. If the Provider restricts scopes, adjust to values permitted by the Provider.
10. Save and enable the authentication source.
1. Create an app or client on the OIDC Provider; choose Web / Confidential Client as the app type.
2. Set the Redirect URI / Callback URL to `<server access URL>/login`.
3. Copy the Client ID and Client Secret.
4. Get the Provider's Discovery URL, usually ending in `/.well-known/openid-configuration`.
5. Log in to the OpenFlare admin panel, go to **「System Settings」**, select the **「Security Settings」** tab, and add an auth source in the **「Auth Source Management」** section.
6. Choose type `OIDC`; fill in the auth source name, display name, Client ID, Client Secret, and OIDC Discovery URL.
7. Scope defaults to `openid profile email`. If the Provider restricts scopes, adjust according to the Provider's allowed values.
8. Save and enable the auth source.
Once enabled, the corresponding OIDC login button will display on the login page.
Once enabled, the login page shows the corresponding third-party login button.
## Login & Binding Behaviors
## Login and Binding Behavior
Once a third-party account returns to OpenFlare, it is processed according to the following rules:
When a third-party account returns to OpenFlare, it is handled as follows:
| Scenario | Behavior |
| --- | --- |
| Third-party account is already bound to a local user | Logs in directly |
| User is already logged in and initiates third-party authorization | Binds to the current local user |
| Third-party account is unbound, and registration is enabled | Automatically creates a standard user and binds |
| Third-party account is unbound, and registration is disabled | Prompts to enter an existing local username and password to complete the binding |
| Third-party account already bound to a local user | log in directly |
| User already logged in and initiates third-party authorization | bind to the current local user |
| Third-party account not bound, and registration allowed | auto-create a normal user and bind |
| Third-party account not bound, and registration disabled | require entering an existing local account password to bind |
If you want only existing users to use SSO, you can disable user registration. Unbound third-party accounts will then trigger the binding flow.
To allow only existing users to use SSO, turn off user registration. Unbound third-party accounts then enter the bind-existing-account flow.
## Modify Authentication Source
## Modifying an Auth Source
When editing an authentication source, leaving the Client Secret field blank retains the existing secret; entering a new value will overwrite the saved secret.
When editing an auth source, leaving the Client Secret input empty keeps the existing secret; entering a new value overwrites and saves it.
If you modify the Auth Source name, the callback URL changes accordingly. You must modify the Redirect URI / Callback URL on the third-party platform; otherwise, the third-party platform will deny the callback or return an error.
Changing the auth source name does not affect the callback URL, so the third-party platform config doesn't need to change.
## Common Problems
## FAQ
### Returns `invalid_scope`
This indicates that the third-party platform does not permit the configured Scope. OIDC defaults to `openid profile email`, and GitHub defaults to `user:email`. Adjust the Scope in the authentication source edit page or configure the third-party platform to permit the scope.
The third-party platform doesn't allow the currently configured scope. The OIDC default scope is `openid profile email`. Adjust the scope on the auth source edit page, or allow the scope on the third-party platform.
### Callback Address Mismatch
### Callback URL mismatch
Verify if the Redirect URI / Callback URL configured in the third-party platform matches the prompt in the OpenFlare form exactly. The protocol, domain, port, and path must match.
Check that the Redirect URI / Callback URL on the third-party platform exactly matches `<server access URL>/login`. Protocol, domain, port, and path must all match.
### Third-party Login Button Not Showing on Login Page
### Login page doesn't show the third-party login button
Verify if the authentication source is enabled and confirm that the Client ID and Client Secret are saved. OpenFlare validates these fields before enabling the source.
Check that the auth source is enabled and that the Client ID and Client Secret are saved. OpenFlare validates these fields before enabling the auth source.
### Client Secret Saved but Not Displayed in Clear Text
### Client Secret saved, but the list doesn't show the plaintext
This is expected behavior. OpenFlare does not echo the Client Secret back via API, displaying only whether the secret is configured.
This is expected. OpenFlare never echoes the Client Secret through the API; it only shows whether a secret is configured.
+110 -138
View File
@@ -1,45 +1,46 @@
# Troubleshooting
You will learn: How to troubleshoot OpenFlare Server, database, login, Agent, OpenResty, configuration publishing, and frontend build issues by symptoms.
You will learn: how to troubleshoot OpenFlare Server, database, login, Agent, OpenResty, and config release issues by symptom.
During troubleshooting, first identify which layer the issue occurs in: browser, Server, database, Agent, OpenResty, origin server, or DNS. OpenFlare configurations are not written directly to nodes online; only after the active version changes will the Agent detect and apply it in heartbeats.
First determine which layer the problem is in: browser, Server, database, Agent, OpenResty, origin, or DNS. OpenFlare configs are not written to all nodes online directly — only after the active version changes do Agents detect and apply it in their heartbeat.
## Quick Diagnostic
## Quick Locate
| Symptom | Where to check first |
| Symptom | Look Here First |
| --- | --- |
| Admin panel fails to open | Server container or process logs, port listening |
| Login anomalies | Default credentials, Session Secret, browser request payloads, Server logs |
| Data fails to save | Database connection, SQLite file permissions, PostgreSQL health |
| Agent offline | Agent logs, Token, Server URL, network connectivity |
| Node not updated after publishing | Active version, node heartbeat, application logs |
| OpenResty application failed | Application logs, Agent logs, certificates, upstream addresses, port conflicts |
| Observability analytics has no data | OpenResty container status, observability port, Agent retry logs |
| Admin panel won't open | Server container/process logs, port listening |
| Login abnormal | default account, Session Cookie, Server logs |
| Data won't save | DB connection, SQLite file permissions, PostgreSQL health |
| Agent offline | Agent logs, Token, Server address, network connectivity |
| Node not updated after release | active version, node heartbeat, apply records |
| OpenResty apply failure | apply records, Agent logs, certificates, upstream addresses, port usage |
| Access analytics empty | OpenResty container state, observability port, Agent backfill logs |
| Static assets never hit cache | global/site cache switch, policy extensions, whether config released, access log `cache_status`, origin Set-Cookie / Cache-Control |
## Server Fails to Start
## Server Won't Start
1. View logs:
1. Check the logs:
```bash
docker compose logs -n 200 openflare
```
For source-code execution, inspect terminal outputs.
For source runs, check terminal output.
2. Check port conflicts:
2. Check port usage:
```bash
lsof -i :3000
```
3. If using PostgreSQL, verify that the database is healthy:
3. If using PostgreSQL, confirm DB health:
```bash
docker compose ps postgres
docker compose logs -n 100 postgres
```
4. If using SQLite, verify that the database directory is writable:
4. If using SQLite, confirm the DB file directory is writable:
```bash
ls -ld "$(dirname /path/to/openflare.db)"
@@ -47,203 +48,174 @@ ls -ld "$(dirname /path/to/openflare.db)"
Common causes:
| Log or Symptom | Action |
| Log or Symptom | Handling |
| --- | --- |
| Database connection failed | Check `DSN` username, password, host, port, dbname, and `sslmode` |
| SQLite fails to create files | Check if the parent directory of `SQLITE_PATH` exists and is writable |
| Port is already in use | Change `PORT` or `--port`, or stop the process binding to the port |
| DB connection failed | check `DB_HOST`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD`, `DB_NAME`, `DB_SSL_MODE` consistency |
| SQLite can't create file | check the `SQLITE_PATH` directory exists and is writable |
| Port occupied | change `PORT` or `--port`, or stop the process holding the port |
## Admin Console Fails to Load or Shows Blank Page
## Admin Panel Won't Open or Is Blank
1. Verify that the Server is listening:
1. Confirm the Server is listening:
```bash
curl -I http://127.0.0.1:3000
```
2. If running from source, verify that the frontend static assets have been built:
2. Check that the browser access address matches the reverse proxy config.
## Default Account Can't Log In
The default account is `admin` / `12345678`. If the password was changed after first login, use the changed one.
Steps:
1. Confirm you're connected to the intended database — avoid `SQLITE_PATH` or `DB_HOST` / `DB_NAME` pointing at another environment.
2. Check whether the Server log uses `sqlite` or `postgres`.
3. In browser dev tools, confirm admin API requests carry the Session Cookie correctly.
4. Clear browser cache and cookies, then log in again.
### Emergency Admin Password Reset
If you forget the `admin` password, reset it with the `reset-passwd` command (supports SQLite and PostgreSQL):
```bash
cd openflare-server/web
pnpm build
go run main.go reset-passwd --user admin --password your-new-password
```
3. Verify if the browser URL matches your reverse proxy domain.
With SQLite, stop the Server process first to avoid DB file lock conflicts. Without `--password`, the command generates a random password and prints it. After resetting, log in and change the password immediately.
4. If accessing via the frontend dev server, verify the backend proxy configuration:
## Agent Can't Register or Stays Offline
```bash
cd openflare-server/web
NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev
```
## Default Credentials Fail to Log In
The default credentials are `root` / `123456`. If you have modified the password after your first login, use your new password.
Troubleshooting Steps:
1. Confirm that you are connecting to the expected database, avoiding `SQLITE_PATH` or `DSN` pointing to a different environment.
2. Check the Server log to see if it is running on `sqlite` or `postgres`.
3. If deployed in multi-replicas or behind a reverse proxy, verify that `SESSION_SECRET` is static and uniform across all instances.
4. Clear browser Cookies and try logging in again.
### Emergency Reset of Admin Password
If you forget the password for the `root` account, you can reset it back to `123456` by directly updating the password hash in the database (please change it immediately after logging in):
#### 1. If using SQLite Database
Stop the Server and open the database file using the `sqlite3` client:
```bash
sqlite3 /path/to/openflare.db
```
Execute the following SQL statement:
```sql
UPDATE users SET password_hash = '$2a$10$wN9aE3zTz83rO7R1uKlhuehJtA3c604pX4Z12B/9.5c0X337t1L4m' WHERE username = 'root';
```
Type `.exit` to exit and restart the Server.
#### 2. If using PostgreSQL Database
Connect to your PostgreSQL instance using a database tool (e.g., `psql`, `pgAdmin`, or `DBeaver`), select the corresponding `openflare` database, and execute the following SQL:
```sql
UPDATE users SET password_hash = '$2a$10$wN9aE3zTz83rO7R1uKlhuehJtA3c604pX4Z12B/9.5c0X337t1L4m' WHERE username = 'root';
```
Once executed successfully, you can log in using the default password `123456`.
## Agent Fails to Register or Stays Offline
Execute on the Agent node:
On the Agent node:
```bash
curl -I http://your-server:3000
```
Inspect Agent logs:
Check Agent logs:
```bash
journalctl -u openflare-agent -n 200 --no-pager
```
Verify configuration parameters:
Check the config file:
```bash
sed -n '1,160p' /opt/openflare-agent/agent.json
```
Key Settings:
Confirm:
| Configuration | Description |
| Config | Description |
| --- | --- |
| `server_url` | Must be the Server address reachable by the Agent node |
| `agent_token` / `discovery_token` | At least one must be provided |
| `heartbeat_interval` | Supports integer milliseconds or Go duration strings |
| `request_timeout` | Can be increased for slower network links |
| `server_url` | must be a Server address reachable by the Agent node |
| `agent_token` / `discovery_token` | at least one filled in |
| `heartbeat_interval` | supports millisecond integer or Go duration string |
| `request_timeout` | increase for slow networks |
If the log warns that the Token is invalid, retrieve a new Token in the management console, update `agent.json`, and restart the Agent:
If logs say the Token is invalid, prepare a new Token in the admin panel, update `agent.json`, then restart:
```bash
systemctl restart openflare-agent
```
## Node Fails to Apply New Version after Publishing
## Node Didn't Apply the New Version After Release
Verify in sequence:
Check in order:
1. Confirm that the target version is activated on the Versions page.
2. Verify if the node is online and if its last heartbeat time has updated.
3. Check the Application Logs for successful, warned, or failed logs for the target version.
4. Verify if the website configuration is enabled; disabled websites do not participate in rendering.
5. Inspect Agent logs for pulls, validations, reloads, or rollback events.
1. Is the target version activated in the version page?
2. Is the node online, and did the last heartbeat time update?
3. Do the apply records show success, warning, or failure for the target version?
4. Is the website config enabled? Disabled sites don't participate in release rendering.
5. Do Agent logs show pull, validation, reload, or rollback messages?
Inspect Agent logs:
View Agent logs:
```bash
journalctl -u openflare-agent -f
```
Note: If a target `version + checksum` fails to apply and triggers a rollback, the Agent blocks repeated synchronization of that failing target in its local state. You must fix the configuration issues and republish to generate a new checksum, or activate an older version to trigger a rollback.
Note: once a target `version + checksum` fails to apply and rolls back, the Agent blocks retrying that target in local state. After fixing the config, republish to generate a new checksum, or activate an older version to roll back.
If this is the Agent's first time applying configurations and no historic `nginx.conf` exists locally to roll back to, the failed version remains blocked but the Agent will attempt to enter the safe fallback runtime. At this point, the application logs and Agent logs will contain `fallback runtime started`. OpenResty will only listen to port `80`, returning a `503` with the body `OpenFlare: No Valid Configuration`, while retaining the local `/openflare/stub_status` health probe. After correcting the configurations and republishing, the Agent overrides the fallback config and restores normal reverse proxies.
If this is the Agent's first config apply with no historical `nginx.conf` to roll back to, the failed target is still blocked, but the Agent enters a safe fallback runtime. The apply records and Agent logs will contain `fallback runtime started`; OpenResty only listens on port `80` and returns `503` with `OpenFlare: No Valid Configuration` for everything, while keeping the local `stub_status` health endpoint. After fixing the config and republishing a new version, the Agent overwrites the fallback config and resumes normal proxying.
## OpenResty Application Fails
## OpenResty Apply Failure
Common Causes:
Common causes:
| Cause | Diagnostic |
| Cause | Troubleshooting |
| --- | --- |
| Domain or server block conflict | Verify if the same domain is used by multiple website configurations |
| Invalid upstream address | Confirm that all upstreams are valid `http://` or `https://` URLs |
| Mismatched multi-upstream format | Multi-upstreams must be pure `scheme://host[:port]` |
| Missing cert or invalid paths | Verify if domains are bound to certs and check if the Agent cert directory is writable |
| Port already in use | Verify ports `80` and `443` on the host |
| Domain or server block conflict | check whether the same domain is used by multiple site configs |
| Invalid upstream address | confirm all upstreams are `http://` or `https://` |
| Multi-upstream format violates constraints | multi-upstream must be plain `scheme://host[:port]` |
| Certificate missing or wrong path | check whether the domain is bound to a cert and the Agent cert dir is writable |
| Port occupied | check local `80`, `443` ports |
OpenResty Configuration Validation:
OpenResty config validation:
```bash
openresty -t -c /path/to/openflare/data/etc/nginx/nginx.conf
```
OpenResty Runtime Status:
OpenResty running state:
```bash
ps aux | grep openresty
```
The Agent determines OpenResty survival periodically using the local endpoint `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status`, completely bypassing repeated `openresty -t` calls. If a node is marked as unhealthy, confirm if this local observability port is listening. If failures only occur when applying configurations (e.g., `host not found in upstream`), the failure lies in config validation or reload, not the periodic health checks.
The Agent's periodic health check probes the local `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status` to judge OpenResty liveness — it does not repeatedly run `openresty -t`. If a node is marked unhealthy, first confirm that local observability port is listening; if `host not found in upstream` appears only during config apply, the failure comes from config validation or reload, not the periodic health probe.
Actual binary paths and main configuration paths are governed by `openresty_path` and `main_config_path` in `agent.json`.
Actual binary and main config paths follow `openresty_path` and `main_config_path` in `agent.json`.
## HTTPS Fails to Work
## HTTPS Not Taking Effect
1. Verify that the certificate has been uploaded or hosted.
2. Verify that the website configuration binds the certificate to the domain.
3. Confirm that the configuration version has been published and activated.
4. Check if the Application Logs indicate a success.
5. Check the certificate chain and status code using `curl`:
1. Confirm the certificate is uploaded or managed.
2. Confirm the site config's domain is bound to a certificate.
3. Confirm a new version was published and activated.
4. Check whether the apply records succeeded.
5. Inspect the certificate and status code with `curl`:
```bash
curl -Iv https://your-domain
```
Domains without a bound certificate will not be added to the HTTPS configuration automatically; this is expected behavior.
Domains without a bound certificate are not auto-added to the HTTPS config — that's expected.
## Traffic Analytics Has No Data
## Access Analytics Empty
1. Confirm that the node has successfully applied configurations carrying observability Lua scripts.
2. Verify that OpenResty is running.
3. Check Agent logs for observability extraction or upload errors.
4. Check if `openresty_observability_port` (default is `18081`) is bound by other processes.
5. Verify if the Server database has purged data inside the time window.
1. Confirm the node successfully applied config including the observability Lua assets.
2. Confirm OpenResty is running.
3. Check Agent logs for observability collection or backfill failures.
4. Check whether `openresty_observability_port` is occupied (default `18081`).
5. Confirm the Server's DB cleanup policy hasn't deleted the relevant time window.
## Frontend Build Fails
## Edge Cache Hit-Rate Anomalies
Execute:
Access log cache three states: **hit** (HIT/STALE/REVALIDATED/UPDATING), **origin** (MISS/EXPIRED), **not cached** (BYPASS or empty — request didn't enter a cacheable path or response wasn't stored). Design: [Edge Cache Strategy Design](../design/edge-cache-design.md).
```bash
cd openflare-server/web
corepack enable
pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
```
### Checklist
Common causes:
1. Global OpenResty cache enabled in **Performance Settings**.
2. Site **Cache** enabled and policy matches the path (「Standard static assets」covers only built-in extensions, **not HTML/JSON**; `.js.map`'s extension is `map`, in the default table).
3. Config version **published and activated**, node apply records succeeded (changing cache rules without publishing leaves nodes on old bypass logic).
4. Request method is **GET** (non-GET is never cached).
5. Origin doesn't return **`Set-Cookie`** for the target URL (if so, not written to the edge).
6. Origin doesn't declare **`Cache-Control: private` / `no-store`** (shared caches won't store).
7. Browser DevTools "Disable cache" only affects the browser; whether the edge HITs is judged by access log `cache_status`, not the Network panel.
| Symptom | Action |
### Common Misconceptions
| Symptom | Explanation |
| --- | --- |
| pnpm version mismatch | Reinstall packages after executing `corepack enable` |
| TypeScript errors | Locate detailed file bugs by running `pnpm typecheck` |
| API type mismatch | Check responses structures in `lib/api/` and `types/` |
| E2E test failures | Confirm that both the Server and frontend dev server are running |
| Everything「not cached」after login, never republished | old config bypassed session cookies; after upgrade you must republish node configs |
| `/api/foo` or `/index.html` not cached under `static` | expected (extension not in the default cacheable table) |
| HTML cross-user leakage after switching to `all` | origin didn't forbid shared caching; switch back to `static` or add `private`/`no-store` to dynamic responses |
| URLs with `?v=` have low hit rates | default cache key includes the full `$request_uri`; different query = different object |
| First MISS, second still MISS | check whether the origin sets `Set-Cookie`/`private` every time, or node disk/cache `inactive` is too short |
## Documentation Build Fails
### Expected Behavior (aligned with Cloudflare defaults)
```bash
cd docs
pnpm install
pnpm build
```
If it fails on broken links, check if new pages are added to the `docs/config.ts` sidebar, or if relative markdown links point to existing markdown files.
* A logged-in user accessing `/_app/**/*.js` static assets: **can HIT**.
* Response with `Set-Cookie` or `private`: **not stored**.
* Cacheable status codes without origin cache headers: use the default Edge TTL (e.g. ~120 min for 200).
+89 -91
View File
@@ -1,174 +1,172 @@
# Tunnel & Intranet Penetration
You will learn: The design principles of OpenFlare intranet penetration tunnels, core concepts (Relay nodes and Tunnel clients), and how to safely and stably publish your intranet development environment or private cloud services to a public domain name from scratch.
You will learn: the design principles of OpenFlare's intranet penetration tunnels, core concepts (relay nodes and tunnel clients), and how to publish an intranet dev environment or private cloud service to a public domain step by step, securely and stably.
In many practical development and operations scenarios, our origin servers are deployed in local LANs, local development machines, or heavily guarded private VPCs, having no public IP address and no port mapping (NAT) configured on border firewalls or routers.
In many real development and ops scenarios, origin services run inside a LAN, on a local dev machine, or in a private VPC — with no public IP and no way to configure port mapping on the border firewall or router.
OpenFlare provides an end-to-end solution **based on reverse relay penetration tunnels**. You only need to initiate a secure outbound connection from your intranet environment to the public relay node, without configuring any inbound ports, to smoothly route public web traffic into your intranet origin. At the same time, you benefit from automatic TLS certificate hosting and WAF security protection provided by the gateway.
OpenFlare provides a complete **reverse-relay tunnel penetration** solution. You only initiate an outbound secure connection from the intranet to a public relay node — no inbound ports need to be configured — and public web traffic is routed into the intranet origin, while enjoying the gateway's automatic TLS certificate management and WAF protection.
---
## Core Concepts
Before using the intranet penetration features, you need to familiarize yourself with the following components and core concepts:
Before using intranet penetration, get familiar with these components:
| Concept | Description | Component / Operation |
| Component | Description | Corresponding Entity |
| --- | --- | --- |
| **Relay Node (Relay)** | Traffic relay services deployed at the public edge, responsible for listening to intranet client persistent connections, acting as the transit bridge between the gateway Agent (OpenResty) and internal traffic. | Node of type `tunnel_relay` running the `openflare-relay` daemon |
| **Penetration Tunnel (Tunnel)** | Logical penetration client instances having a globally unique ID and secure authentication token, used to identify a specific intranet environment. | Globally unique ID generated by Server `tunnel_id` (format: `tun-<32hex>`) |
| **Tunnel Client (Client)** | A lightweight controller running in the intranet environment, automatically managing the underlying frpc tunnel subprocesses according to the configuration dispatched by the Server. | The `openflared` container or independent binary process deployed in the intranet |
| **Tunnel Upstream (Tunnel Upstream)** | A special upstream type in the website configuration. When this type is selected, the gateway forwards public traffic to the Vhost port of the local relay node, eventually reaching the intranet origin. | Upstream of type `tunnel` configured in the website details |
| **Relay node** | a traffic relay service deployed at the public edge; listens for the intranet client's long connections and bridges gateway Agent (OpenResty) and intranet traffic | `tunnel_relay` node guarded by `openflare-relay` |
| **Tunnel** | a logical penetration client instance with a globally unique ID and an auth token, identifying one concrete intranet environment | `tunnel_client` node created in「Node Management」, assigned a dedicated Tunnel Token |
| **Tunnel client** | a lightweight controller running in the intranet; auto-manages the underlying frpc tunnel child processes based on Server-dispatched config | `openflared` container or standalone binary deployed in the intranet |
| **Tunnel upstream** | a special reverse proxy type in route rules. With this type, the gateway forwards public traffic to the local relay's Vhost port, eventually reaching the intranet origin | reverse proxy type configured in the「Rule Management」detail page, origin mode「Intranet Tunnel」with a bound Tunnel node |
---
## Recommended Operation Sequence
## Recommended Order
To publish an intranet service to the public internet, we recommend doing so in the following order:
To publish an intranet service to the public, follow this order:
1. Register and deploy at least one public **Relay Node (Relay)** and keep it online.
2. Create a **Penetration Tunnel (Tunnel)** in the management console and copy its dedicated Token.
3. Deploy and start the **Tunnel Client (OpenFlared)** on your intranet server.
4. Confirm that the status of the tunnel in the management console shows as "Online".
5. Add a website configuration, selecting **Intranet Penetration** as the upstream type, binding it to the corresponding tunnel, and entering the intranet port (e.g., `127.0.0.1:8080`).
1. Register and deploy at least one public **Relay node** and keep it online.
2. Go to **「Node Management」**, create a node of type **Tunnel node (tunnel_client)**, and get the dedicated Token.
3. Deploy and start the **tunnel client (OpenFlared)** on the intranet server.
4. Confirm the Tunnel node's status shows「Online」in the admin panel.
5. Add or edit a rule in **「Rule Management」**; in the「Reverse Proxy」tab choose origin mode **「Intranet Tunnel」**, bind the Tunnel node, and enter the intranet service port (e.g. `127.0.0.1:8080`).
6. Publish and activate the new version.
7. Access via the public domain to verify that the intranet penetration link is established.
7. Access via the public domain to verify the tunnel link.
---
## Detailed Configuration Steps
## Detailed Steps
### Step 1: Prepare the Relay Node (Relay)
### Step 1: Prepare a Relay Node
Intranet traffic is routed through public relay nodes. Before starting, ensure you have a public relay server available.
Intranet traffic needs a public relay node to transit. Before starting, make sure you have a usable relay server on the public network.
1. Log into the management console and go to **"Node Management"**.
2. Add a new node, selecting **Relay Node (tunnel_relay)** as the **Node Type**.
3. Save and copy the node-specific `agent_token`.
4. Start the `openflare-relay` process on your public server. You can run it quickly using Docker:
1. Log in to the admin panel, go to **「Node Management」**.
2. Add a new node and set **Node Type** to **Relay node (tunnel_relay)**.
3. After saving, copy the node's dedicated `agent_token`.
4. Start `openflare-relay` on your public server. Docker quick run:
```bash
docker run -d --name openflare-relay --restart unless-stopped \
-p 7000:7000 \
-e OPENFLARE_SERVER_URL=http://<YOUR_SERVER_PUBLIC_IP>:3000 \
-e OPENFLARE_AGENT_TOKEN=<YOUR_COPIED_AGENT_TOKEN> \
-e OPENFLARE_SERVER_URL=http://<your-Server-public-IP>:3000 \
-e OPENFLARE_AGENT_TOKEN=<the-AgentToken-you-copied> \
-v openflare-relay-data:/var/lib/openflare-relay \
ghcr.io/rain-kl/openflare-relay:latest
```
> [!IMPORTANT]
> Make sure to allow port `7000` (the control port for frpc client connections) in your cloud provider's security group. If your Server and Relay are deployed on the same machine, `OPENFLARE_SERVER_URL` should point to the Server's public or internal IP.
> Open port `7000` (the frpc client connection control port, default `relay_bind_port`) in the cloud server's security group. If your Server and relay node are on the same machine, `OPENFLARE_SERVER_URL` here should point to the Server's public or intranet communication IP.
### Step 2: Create a Penetration Tunnel in the Management Console
### Step 2: Create a Tunnel Node in the Admin Panel
1. Navigate to the **"Intranet Penetration"** section in the side navigation bar.
2. Click the **"Create Tunnel"** button and enter:
* **Tunnel Name**: Describes the intranet environment, e.g., `home-lab` or `office-dev`.
* **Description**: Optional, describes the purpose of this tunnel.
3. Click save, and the system will automatically generate a globally unique ID and a dedicated `tunnel_token` (e.g., `tun-xxxx...`).
4. Copy the **Client Deployment Command** generated in the popup window, which will be used in the next step.
1. Navigate to **「Node Management」** in the admin sidebar.
2. Click **「Add Node」**; in the dialog choose node type **「Tunnel node (tunnel_client)」**.
3. Fill in the node name and description, click save.
4. Click into the Tunnel node's detail page; find the dedicated **Tunnel Token** and the one-click client deployment command.
### Step 3: Deploy the Intranet Client (OpenFlared)
Return to your intranet server and execute the copied deployment command to run the client.
Back on your intranet server, run the client with the copied deployment command.
#### Option A: Deploy with Docker (Highly Recommended)
#### Option A: Deploy with Docker (recommended)
The official `openflared` image embeds the master daemon and `frpc` runtime, working out-of-the-box with no extra dependencies:
The official `openflared` image bundles the supervisor daemon and the `frpc` runtime — out of the box, no extra dependencies:
```bash
docker run -d --name openflared --restart unless-stopped \
-e OPENFLARE_SERVER_URL=http://<YOUR_SERVER_PUBLIC_IP>:3000 \
-e OPENFLARE_TUNNEL_TOKEN=<YOUR_COPIED_TUNNEL_TOKEN> \
-e OPENFLARE_SERVER_URL=http://<your-Server-public-IP>:3000 \
-e OPENFLARE_TUNNEL_TOKEN=<the-TunnelToken-you-copied> \
-v openflared-data:/app/data \
ghcr.io/rain-kl/openflared:latest
```
#### Option B: Host Binary Manual Execution
#### Option B: Run the host binary manually
If you cannot use Docker, you can download or compile the `flared` binary:
If Docker isn't convenient, download or build the `flared` binary yourself:
1. Create a `flared.json` configuration file in the same directory as the executable on your intranet machine:
1. Create a `flared.json` config file next to the program on the intranet machine:
```json
{
"server_url": "http://<YOUR_SERVER_PUBLIC_IP>:3000",
"tunnel_token": "<YOUR_COPIED_TUNNEL_TOKEN>",
"server_url": "http://<your-Server-public-IP>:3000",
"tunnel_token": "<the-TunnelToken-you-copied>",
"frpc_path": "/usr/local/bin/frpc",
"data_dir": "./data"
}
```
2. Execute the startup command:
2. Start it:
```bash
./flared -config ./flared.json
```
#### Verify Online Status
#### Status Confirmation
Once started successfully, the intranet client will send heartbeats through outbound networks to synchronize configurations. At this point:
1. Refresh the **"Intranet Penetration"** list in the management console; the tunnel status indicator should turn green and show **"Online"**.
2. Click tunnel details to view which public Relays the intranet client is currently connected to.
After starting, the intranet client sends heartbeat syncs to the control plane over outbound connections:
1. Refresh the **「Node Management」** list; the Tunnel node's status light should turn green **「Online」**.
2. Click into the node detail page to see which public Relay nodes the intranet client is connected to.
### Step 4: Create a Website and Bind the Tunnel Upstream
### Step 4: Configure the Route and Bind the Tunnel Upstream
Now you can configure public reverse proxy and domain routing for your intranet service.
Now configure public reverse proxying and domain access for your intranet service:
1. Go to the **"Website Configuration"** page and click **"Create Website"**.
2. Enter the **Domain Name** required to access the service publicly, e.g., `nas.example.com`.
3. Critical Configuration: In the **"Upstream Configuration"** section, switch the **Upstream Type** from "Direct" to **"Intranet Penetration"**.
4. In the dropdown list, select your newly deployed **Intranet Tunnel** (e.g., `home-lab`).
5. Enter the **Intranet Target Address** (the local address and port reachable by the intranet client, e.g., `127.0.0.1:8080`) and select the **Intranet Protocol** (usually `http`).
6. Configure other standard website settings (such as TLS certificates) and click save.
1. First go to **「Website Management」->「Domain List」** and register the domain you want to expose.
2. Go to **「Rule Management」**, click **「New Rule」** or edit an existing rule.
3. In the **「Reverse Proxy」** tab, switch the **origin mode** to **「Intranet Tunnel」**.
4. Select the online **Tunnel node** from the dropdown.
5. Fill in the **intranet target address** (a local address/port reachable by the intranet client, e.g. `127.0.0.1:8080`) and **intranet protocol** (usually `http`).
6. Configure other regular site options and save.
### Step 5: Publish & Activate
### Step 5: Publish and Apply
To allow the gateway's OpenResty instance to match and route domain traffic correctly, we need to publish a new configuration version.
To let the gateway's OpenResty match and route domain traffic, publish a new config version:
1. Click **"Preview Config"** in the top right corner of the navigation bar to verify the generated configurations.
2. In the popup window, click **"Publish & Activate"**.
3. Now, the public edge Agent pulls the latest routing, forwarding requests for `nas.example.com` to the loopback virtual host port of `openflare-relay (frps)`.
4. The intranet client `openflared (frpc)` receives the relayed packets, securely hands them over to the local `127.0.0.1:8080` service, and returns responses back through the tunnel.
5. Access `nas.example.com` in your browser to confirm that the intranet service displays successfully!
1. Click **「Preview and Publish」** in the top-right nav; confirm the generated site config is correct.
2. In the dialog, click **「Confirm Publish」**.
3. The public-edge Agent now pulls the latest route: it forwards requests to the same-host `openflare-relay (frps)` vhost port.
4. The intranet client `openflared (frpc)` receives the relayed packets and safely forwards them to the intranet `127.0.0.1:8080` service, returning the response along the same path.
5. Visit the domain in a public browser to confirm the intranet service displays.
---
## Advanced Application Scenarios
## Advanced Scenarios
### 1. Single-Tunnel Multi-Service Multiplexing (Multi-Port Mapping)
### 1. One Tunnel, Multiple Services (multi-port mapping)
You do not need to deploy an `openflared` container for every single internal service.
You don't need a separate `openflared` container for every intranet service.
If you want to map multiple different services in the same intranet environment (e.g., `127.0.0.1:80` for a blog, `127.0.0.1:8080` for an API, and `192.168.1.120:9000` for a local network drive):
1. Keep this single `openflared` client online.
2. Create three independent website configurations in the management console (binding their respective public domains).
3. Set the **Upstream Type** to **the same intranet tunnel** for all three website configurations.
4. Fill in their respective "Intranet Target Addresses" (e.g., `127.0.0.1:80`, `127.0.0.1:8080`, and `192.168.1.120:9000`).
5. Publish and activate the new version to achieve single-tunnel multi-service multiplexing.
To map multiple services in one intranet environment (e.g. `127.0.0.1:80` blog, `127.0.0.1:8080` API, `192.168.1.120:9000` intranet drive):
1. Keep this one `openflared` client online.
2. Create three separate website configs in the admin panel (each bound to its own public domain).
3. Select **the same tunnel** as the origin mode for all three.
4. Fill in the corresponding different ports or LAN IPs in each intranet target address (e.g. `127.0.0.1:80`, `127.0.0.1:8080`, `192.168.1.120:9000`).
5. Publish and activate — one tunnel, many uses.
### 2. Seamless Integration with Gateway Security Features
### 2. Gateway Security Features Stack Seamlessly
Since all public traffic enters the public Agent node first, completing the HTTPS/TLS handshake and WAF filtering before traveling through the secure tunnel:
Because all public traffic first enters the public Agent node — HTTPS/TLS handshake and WAF engine interception happen there — then travels through the secure tunnel to the intranet:
Your intranet services **naturally benefit from the following advanced features without any code changes**:
* **One-Click HTTPS**: Select or issue SSL certificates directly in the management console, encrypting transmission end-to-end.
* **Global/Custom WAF Protections**: Enables SQL injection blocking, XSS prevention, and regional IP filtering.
* **Human-Machine Challenge (PoW CC)**: Instantly blocks brute-force CC API attacks targeting your intranet services.
Your intranet service needs **zero modification** to enjoy:
* **One-click HTTPS**: select or apply an SSL certificate for the domain directly in the admin panel; data is encrypted end-to-end.
* **Global/custom WAF protection**: enable SQL injection blocking, XSS injection defense, and malicious geo-IP blocking.
* **Human challenge (CC PoW)**: one-click defense against malicious CC requests to intranet APIs.
---
## Common Troubleshooting
## Troubleshooting
### 1. Tunnel Shows as "Offline" in the Management Console
### 1. Tunnel shows「Offline」in the admin panel
* **Check the Token**: Check if the `tunnel_token` configured in `flared` logs or environment variables matches the one generated in the management console.
* **Check Outbound Connectivity**: The intranet server must be able to make outbound requests to the Server address. Ensure the control plane firewall is not blocking HTTP requests from the client.
* **Relay Firewall Port Closed**: Check if port `7000` (or your custom bindPort) on the public Relay node has been allowed in the public security groups.
* **Check the Token**: verify the `tunnel_token` in `flared` logs or env vars matches the one generated in the admin panel.
* **Check network connectivity**: the intranet server must be able to reach the Server address over outbound connections.
* **Relay firewall not open**: check that the relay node's public `7000` port (or custom `relay_bind_port`) is opened to the public in the security group.
### 2. Accessing the Public Domain Returns 502 Bad Gateway / 504 Gateway Timeout
### 2. Public domain returns 502 Bad Gateway / 504 Gateway Timeout
* **Intranet Service Not Running**: Verify that the service corresponding to the intranet target address is running and listening on the intranet server.
* **Target Address Unreachable**: If the intranet address is set to `127.0.0.1:8080`, ensure the service is running on the exact same host as `openflared`; if set to a LAN IP `192.168.x.x`, test connectivity to that IP inside the `openflared` container.
* **Check Client Application Logs**: View the "Apply Logs" in the management console or inspect local `flared` logs for any `LastError`. When frpc fails to connect to the intranet port, it reports the failure details to the Server.
* **Intranet service not running**: confirm the service at the intranet target address is started and listening on the intranet server.
* **Target address unreachable**: if the intranet address is `127.0.0.1:8080`, ensure the service runs on the same host as `openflared`; if it's a LAN IP `192.168.x.x`, test LAN reachability from inside the `openflared` container.
* **Check node state and logs**: view the Tunnel node detail and「Apply Records」in the admin panel; frpc process errors are logged in detail in the `flared` logs on the intranet host.
### 3. Multiple Relays Network Instability or Retry Failures
### 3. Multi-relay network flapping or retry failures
* When the control plane associates multiple Relay nodes, `openflared` spawns independent frpc daemon processes for each Relay and pulls topology states periodically at `sync_interval` (default 30s) configured in `flared.json`.
* If a Relay drops frequently due to network jitter, the system triggers the backoff retry mechanism automatically. You can see `frpc process missing, starting` logs on the host, which is a normal process self-healing action and will recover within 5-10 seconds after network recovery.
* When the control plane is associated with multiple Relay nodes, `openflared` spawns a separate frpc supervisor per Relay and periodically pulls topology state from the control plane within the `sync_interval` configured in `flared.json` (default 30s).
* If a relay node frequently drops due to network jitter, the system auto-triggers exponential backoff retries (initial 1s, cap 60s). You may see `frpc process missing, starting` in the host logs — that's normal process self-healing; it reconnects automatically after the network recovers.
+95 -68
View File
@@ -1,100 +1,113 @@
# WAF Auto IP Group Expressions
# WAF Auto IP Group Rule Syntax
Automatic IP groups are used to aggregate metrics from request logs on a per-client-IP basis, using Expr expressions to determine if an IP should be added to the group. Automatic IP groups can be referenced by IP blacklists or whitelists in WAF rule groups; during publication, the Server only writes the referenced IP group ID to `waf_config.json`, while IP group members are synchronized independently by the Agent into the local runtime files.
Auto IP groups aggregate metrics per client IP from request logs, then use Expr expressions to decide whether to add an IP to the group list. Auto IP groups can be referenced by a WAF rule group's IP blocklist or allowlist; on config release, the Server only writes the IP group reference IDs into `waf_config.json` — IP group members are synced independently by the Agent to the local runtime file.
## Configuration Structure
## Config Structure
The configuration of an automatic IP group is a JSON object:
An auto IP group config is a JSON object:
```json
{
"lookback_minutes": 60,
"lookback": "1h",
"rules": [
{
"name": "Single IP High-Frequency 404 Scanning",
"expr": "request_count > 100 && status_404_ratio >= 0.8"
"name": "Single-IP high-frequency 404 scanning",
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
}
]
}
```
Field Descriptions:
Field reference:
| Field | Type | Role |
| Field | Type | Purpose |
| --- | --- | --- |
| `lookback_minutes` | number | How many minutes of request logs to look back during execution. Defaults to 60 minutes if blank, minimum 5 minutes, maximum 43200 minutes. |
| `rules` | array | List of automatic rules. If any rule matches, the IP is added to the automatic IP group list. |
| `rules[].name` | string | Rule name, used only for UI display and error messages. |
| `rules[].expr` | string | Expr expression, must return a boolean value. |
| `lookback` | string | lookback window duration in Go Duration syntax, e.g. `30m`, `1h`, `90m`. Defaults to `1h`, max 30 days. Compatible with the legacy `lookback_minutes` (integer minutes). |
| `rules` | array | auto-rule list. If any rule matches, the IP enters the auto IP group list. |
| `rules[].name` | string | rule name, only for UI display and error messages. |
| `rules[].expr` | string | Expr expression, must return a boolean. |
## Evaluation Mechanics
## Execution Semantics
Automatic rules do not evaluate logs request-by-request, but instead aggregate them by client IP first:
Auto IP groups first aggregate metrics per client IP, then run rule expressions against each IP:
1. The Server reads request logs from the past `lookback_minutes` minutes.
2. Groups them by normalized IP (`remote_addr`).
3. Computes metrics like request count, 404 count, and direct IP host count for each IP.
4. Evaluates `rules[].expr` for each IP.
5. If an IP matches any rule, it is written to the automatic IP group's IP member list.
1. The Server reads request logs from the last `lookback` window.
2. Groups by normalized `remote_addr` IP.
3. Computes per-IP metrics: request count, 404 count, direct-IP Host count, etc.
4. Runs `rules[].expr` per IP.
5. If an IP matches any rule, it is written into the auto IP group's `IP / IP segment` list.
Whether a request is "accessing via IP directly" is determined by the `Host` field in the request logs. If the Host header is an IPv4 or IPv6 literal (e.g., `203.0.113.10`, `[2001:db8::10]`, `203.0.113.10:443`), it is counted in `ip_host_count`.
Whether a Host is "accessed via IP" is judged by the `Host` field in request logs: if the Host is an IPv4 or IPv6 literal, e.g. `203.0.113.10`, `[2001:db8::10]`, `203.0.113.10:443`, it counts toward `ip_host_count`.
## Available Metrics
## Available Keywords
The following metrics are directly available in Expr expressions:
The expression can use these fields directly:
| Keyword | Type | Role |
| Keyword | Type | Purpose |
| --- | --- | --- |
| `ip` | string | The client IP currently being evaluated. |
| `request_count` | number | Total request count of the IP in the lookback window. |
| `status_404_count` | number | Number of 404 responses returned to the IP in the lookback window. |
| `status_404_ratio` | number | 404 request ratio, calculated as `status_404_count / request_count`. |
| `ip_host_count` | number | Number of requests from the IP using an IP address directly as the Host header. |
| `ip_host_ratio` | number | Ratio of direct IP address accesses, calculated as `ip_host_count / request_count`. |
| `client_error_count` | number | Number of requests returning 4xx status codes. |
| `server_error_count` | number | Number of requests returning 5xx status codes. |
| `last_seen_unix` | number | Unix timestamp (in seconds) of the last request from the IP in the lookback window. |
| `ip` | string | the client IP currently being judged. |
| `request_count` | number | the current IP's total requests in the lookback window. |
| `status_404_count` | number | the current IP's requests returning 404 in the window. |
| `status_404_ratio` | number | 404 ratio, computed as `status_404_count / request_count`. |
| `ip_host_count` | number | requests where the current IP accessed via an IP-literal Host. |
| `ip_host_ratio` | number | ratio of IP-address access, computed as `ip_host_count / request_count`. |
| `client_error_count` | number | the current IP's requests returning 4xx. |
| `server_error_count` | number | the current IP's requests returning 5xx. |
| `last_seen_unix` | number | the current IP's last request Unix timestamp (seconds) in the window. |
All ratio fields are decimals between `0` and `1`. An 80% ratio should be written as `0.8`, and 50% as `0.5`.
Ratio fields are decimals between `0` and `1`. 80% is written `0.8`, 50% is `0.5`.
## Common Expr Syntax
### Custom Status Code Matching
Automatic IP groups use the Expr syntax. The expression must return a boolean value.
If the built-in `status_404_count` / `status_404_ratio` don't fit, use these built-in methods to match arbitrary status codes:
Common Operators:
* **`StatusCount(code)`**: request count of the current IP returning the given status code (or class) in the window.
* exact code: `StatusCount(403) > 10`
* status class (`1xx`–`5xx`, case-insensitive): `StatusCount("4xx") > 50`
* **`StatusRatio(code)`**: the ratio of the above count to the IP's total requests.
* exact code: `StatusRatio(502) >= 0.5`
* status class: `StatusRatio("4xx") >= 0.8`, `StatusRatio("5xx") >= 0.3`
| Operator | Role | Example |
A status class aggregates all codes in that hundred range, e.g. `"4xx"` covers 400–499, `"2xx"` covers 200–299.
## Common Expr Patterns
Auto IP groups use Expr syntax; the expression must return a boolean.
Common operators:
| Pattern | Purpose | Example |
| --- | --- | --- |
| `>`, `>=`, `<`, `<=` | Numeric comparison | `request_count > 100` |
| `==`, `!=` | Equality / Inequality | `ip != "127.0.0.1"` |
| `&&` | Logical AND | `request_count > 100 && status_404_ratio >= 0.8` |
| `||` | Logical OR | `status_404_ratio >= 0.8 || server_error_count > 20` |
| `!` | Logical NOT | `!(ip == "127.0.0.1")` |
| `in` | Value is in list | `ip in ["203.0.113.10", "198.51.100.20"]` |
| `not in` | Value is not in list | `ip not in ["127.0.0.1"]` |
| `()` | Grouping controls operator priority | `(request_count > 100 && status_404_ratio >= 0.8) || server_error_count > 50` |
| `>`、`>=`、`<`、`<=` | numeric comparison | `request_count > 100` |
| `==`、`!=` | equal / not equal | `ip != "127.0.0.1"` |
| `&&` | and | `request_count > 100 && StatusRatio(404) >= 0.8` |
| `||` | or | `StatusRatio(404) >= 0.8 || server_error_count > 20` |
| `!` | negation | `!(ip == "127.0.0.1")` |
| `in` | value in list | `ip in ["203.0.113.10", "198.51.100.20"]` |
| `not in` | value not in list | `ip not in ["127.0.0.1"]` |
| `()` | grouping precedence | `(request_count > 100 && StatusRatio(404) >= 0.8) || server_error_count > 50` |
## Built-in Presets
The management console provides two built-in preset rules that can be added directly and adjusted as needed:
The admin panel ships two preset rules that can be added and then adjusted:
```json
{
"name": "Single IP High-Frequency 404 Scanning",
"expr": "request_count > 100 && status_404_ratio >= 0.8"
"name": "Single-IP high-frequency 404 scanning",
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
}
```
Meaning: A single IP requests more than 100 times in the lookback window, and the 404 status code ratio is at least 80%.
Meaning: a single IP has over 100 requests in the window and a 404 ratio of at least 80%.
```json
{
"name": "Single IP Direct IP Access Mismatch",
"name": "Single-IP direct-access anomaly",
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
}
```
Meaning: A single IP accesses the server directly using an IP address as the Host header more than 50 times, and this type of access represents more than 50% of its total requests.
Meaning: a single IP accessed via IP-literal Host over 50 times, and that access ratio exceeds 50%.
## Examples
@@ -102,60 +115,74 @@ High-frequency 404 scanning:
```json
{
"lookback_minutes": 60,
"lookback": "1h",
"rules": [
{
"name": "High-Frequency 404 Scanning",
"expr": "request_count > 100 && status_404_ratio >= 0.8"
"name": "High-frequency 404 scanning",
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
}
]
}
```
Direct IP access mismatch:
IP direct-access anomaly:
```json
{
"lookback_minutes": 30,
"lookback": "30m",
"rules": [
{
"name": "Direct IP Access Mismatch",
"name": "IP direct-access anomaly",
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
}
]
}
```
Capture both high 4xx and 5xx errors:
Catching both high 4xx and high 5xx:
```json
{
"lookback_minutes": 120,
"lookback": "2h",
"rules": [
{
"name": "Abnormal Error Rates",
"name": "Abnormal error rate",
"expr": "(client_error_count > 80 && request_count > 100) || server_error_count > 30"
}
]
}
```
Exclude trusted IPs:
Using status-class syntax (equivalent thinking to `client_error_count` / `server_error_count`):
```json
{
"lookback_minutes": 60,
"lookback": "2h",
"rules": [
{
"name": "404 Scanning Excluding Trusted IPs",
"expr": "ip not in [\"203.0.113.10\", \"198.51.100.20\"] && request_count > 100 && status_404_ratio >= 0.8"
"name": "High 4xx or 5xx ratio",
"expr": "request_count > 100 && (StatusRatio(\"4xx\") >= 0.8 || StatusRatio(\"5xx\") >= 0.3)"
}
]
}
```
## Usage Recommendations
Excluding trusted IPs:
Start with a shorter lookback window and higher thresholds to monitor matches, then adjust thresholds gradually. The IP Groups page in the management console allows you to click **"Test Rule"** before saving to view matching IPs in the current window immediately. Once an automatic IP group runs, it overwrites the list of IPs. If you want to permanently whitelist or blacklist certain IPs, add them to a manual IP group instead, and reference both manual and automatic groups in your WAF rule groups.
```json
{
"lookback": "1h",
"rules": [
{
"name": "404 scanning excluding trusted IPs",
"expr": "ip not in [\"203.0.113.10\", \"198.51.100.20\"] && request_count > 100 && StatusRatio(404) >= 0.8"
}
]
}
```
Updating automatic IP groups does not require publishing configuration versions. Online Agents receive changes via WebSocket and update the local `waf_ip_groups.json` instantly. If WebSocket is unavailable, the Agent reports its local checksum in heartbeats, and the Server syncs only the mismatched IP groups.
## Usage Tips
Start with a shorter lookback and higher thresholds to observe hits, then tune gradually. The admin IP group page supports clicking **Test Rule** before saving to directly view the IPs hit in the current window; when the auto IP group actually runs, it overwrites the group's IP list. To keep certain addresses long-term, put them in a manual IP group and reference both the manual and auto groups in the WAF rule group.
Auto IP group updates don't require republishing a config version. Online Agents receive the changed IP groups via WebSocket and update the local `waf_ip_groups.json`; when the WebSocket is unavailable, the Agent reports the local IP group checksum in the next heartbeat and the Server only returns the groups whose checksums differ.
+18 -149
View File
@@ -1,162 +1,31 @@
# WAF Security Protection
You will learn: How the OpenFlare edge Web Application Firewall (WAF) works, its protection dimensions, how to manage and reference the three types of IP groups (Manual, Subscription, and Expr-based Automatic IP groups), configure CC protection challenges (PoW human-machine verification) and regional filtering, and achieve sub-second hot updates of IP group members without Nginx reloads.
OpenFlare WAF orchestrates rules with a visual directed acyclic graph. When creating a rule you only enter a name; the system creates the default "start → pass" graph and enters the editor.
---
## Nodes and Connections
## Core Concepts
- **Start**: unique per rule; enters the graph along `next`.
- **Pass**: ends the current rule; if the route has more rules, execution continues.
- **Block**: immediately terminates the request with the configured status code and HTML response.
- **IP match**: configure an IP, CIDR, or IP group; connect `true` / `false` respectively.
- **Geo match**: branch by country or ISO 3166-2 first-level division code; the country list shows both localized names and codes; divisions are searchable by country name, division name, or code. Country and City MMDB are provided by disk files (Docker images COPY them to the default path; bare binaries download from the configured URL on first startup) and update on the configured cycle. When City MMDB is unavailable, it is treated as no match.
- **PoW**: takes over the request when the challenge is incomplete; after verification passes, continues along `next`.
Before configuring security policies, you need to understand the core components of the WAF:
The server rejects cycles, dangling outlets, unreachable nodes, duplicate port connections, and invalid configs. Save carries the `revision` obtained at page load; on a 409 conflict, reload to avoid overwriting others' changes.
| Concept | Description | Scope & Activation Method |
| --- | --- | --- |
| **WAF Rule Group (Rule Group)** | A logical collection of security rules, including: IP whitelists/blacklists (direct input or IP group references), country/region limits, CC protection (PoW), and custom block responses. | Supports global enablement or binding to single/multiple websites. **Modifying rule group definitions requires publishing and activating a configuration version**. |
| **IP Group (IP Group)** | A list container storing individual IPs or CIDR blocks. Divided into **Manual**, **Subscription**, and **Automatic** types. WAF rule groups reference IP groups by ID. | Belongs to dynamic resources. **IP group member updates support sub-second WebSocket hot-syncing, completely bypassing Nginx process reloads**. |
| **PoW Challenge (CC PoW)** | A human-machine verification challenge based on Proof of Work. By prompting browsers to solve hash collisions of a specified difficulty, it silently blocks malicious brute-force scripts and bots while keeping legitimate user experience smooth. | A configuration Tab in the rule group. **Modifying PoW parameters requires publishing and activating a configuration version**. |
Select a normal node or edge, then click the delete button at the canvas top-right, or press Delete/Backspace. Deleting a node also deletes associated edges; the single "start" and "pass" nodes cannot be deleted. Dragging a node only records the final coordinates on release; the canvas state is not rebuilt repeatedly during movement.
---
The right node property panel is hidden by default and shows after clicking a node; it auto-collapses when clicking an edge or blank canvas.
## Recommended Configuration Sequence
The orchestration area defaults to a compact height and smaller initial zoom; you can still zoom freely with the wheel or canvas Controls.
When configuring security protections for your websites, we recommend doing so in the following order:
## Binding and Effect
1. Navigate to IP Groups, creating the required **Manual IP Groups** (e.g., developer whitelist) or **Automatic IP Groups** (e.g., auto-blocked IPs based on 404 scans).
2. Create or edit a **WAF Rule Group**:
* Bind the IP groups you want to reference or block.
* Configure regional whitelists/blacklists for countries or provinces.
* (Optional) Configure human-machine challenge parameters in the `PoW` Tab.
* Set custom status codes (e.g., 403, 418) and HTML block pages in the `Block Response` Tab.
3. Associate the rule group with the corresponding **Website Configuration**.
4. Publish and activate the configuration version to let the edge node (Agent) apply the WAF rules to filter traffic.
Enabled global rules always execute first; route-bound custom rules execute strictly in list order. After adjusting the order, you must publish a config version for the rule topology to take effect with the OpenResty reload.
---
IP group members are dynamic resources. The Agent checks the checksum every 5 seconds and updates in-memory snapshots across workers on change — no rule republish or reload needed. Manual, subscription, and auto IP groups can all be referenced by IP-match nodes. A single full IP group runtime snapshot is capped at 20 MiB; exceeding the cap makes publish or sync return an error and keeps using the previous valid snapshot.
## Detailed Step Guide
> [!IMPORTANT]
> When upgrading from the legacy fixed allow/block-list, geo, or PoW forms, rule graphs reset to "start → pass" and old policy fields are not migrated. Re-orchestrate and verify each rule before publishing a new version.
### Step 1: Manage and Configure IP Groups
IP groups are the foundations of large-scale IP filtering. OpenFlare provides three highly resilient types of IP groups:
#### 1. Manual IP Groups (Manual)
* **Purpose**: Statically maintain a list of verified trusted IPs or long-term blocked IPs/CIDR blocks.
* **Configuration**: Click "Create IP Group" -> select type "Manual" -> enter IPs or CIDRs line-by-line (e.g., `192.168.1.100` or `10.0.0.0/24`).
#### 2. Subscription IP Groups (Subscription)
* **Purpose**: Integrate third-party threat intelligence databases or IP ranges published by cloud providers.
* **Configuration**: Select type "Subscription" -> enter fetch URL (supports line-separated plain text or standard JSON formats). A background cron job on the Server periodically pulls the subscription source and updates the group members automatically.
#### 3. Automatic IP Groups (Automatic)
* **Purpose**: **The most aggressive automated defense channel against scans and brute-force attacks**.
* **Configuration**: Select type "Automatic" -> write Expr log aggregation logic. You can directly select built-in presets:
* **Single IP High-Frequency 404 Scanning**: `request_count > 100 && status_404_ratio >= 0.8` (A single IP requesting over 100 times in the past hour with a 404 response ratio of at least 80%).
* **Single IP Direct IP Access Mismatch**: `ip_host_count > 50 && ip_host_ratio > 0.5` (Bypassing domains to hit the server directly using IP address host headers).
* **Test & Run**: Click **"Test Rule"** before saving to preview IPs matching the current log window. Click **"Execute Now"** after saving to aggregate logs immediately and generate the block list.
> [!TIP]
> For the detailed syntax and available metrics of automatic IP groups, see [WAF Auto IP Group Expressions](./waf-ip-group-expr.md).
---
### Step 2: Create and Configure a WAF Rule Group
1. Navigate to the **"WAF"** section in the side menu, and click **"Create Rule Group"**.
2. Enter the rule group name (e.g., `production-api-shield`), and select if it is a "Global Rule Group".
3. Enter rule group details, and configure the tabs sequentially below:
#### 1. Whitelist / Blacklist Configuration (Allow / Block Lists)
* **Direct IPs**: Enter individual IPs or CIDR blocks line-by-line that need temporary whitelisting or blacklisting directly in the text area.
* **IP Group Reference**: Click "Bind IP Groups", selecting the manual, automatic, or subscription IP groups you configured in Step 1. Whitelists permit traffic instantly, whereas blacklists block it.
#### 2. Regional Restriction (GeoIP)
* **Description**: OpenFlare integrates GeoIP geolocation resolution.
* **Configuration**: Toggle the regional restriction switch, selecting "Allow Only" or "Block".
* * For example, if your service is only intended for domestic users, set the mode to "Allow Only" and check `China` in the country list.
* * Supports refining to specific provinces/regions, enabling you to block malicious traffic originating from targeted geographic zones with one click.
#### 3. Human-Machine Challenge Configuration (PoW CC Protection)
* **Description**: Enable CC protection human-machine challenges. When a request triggers the CC protection threshold, the browser renders a silent challenge page, solving a mathematical challenge (hash collision) within several hundred milliseconds. Upon passing, it sets a Cookie and allows subsequent visits. This is seamless to actual users but blocks brute-force scripts and CC tools that do not support JS execution or mathematical computations.
* **Core Parameters**:
* **Status**: Enable / Disable.
* **Hash Difficulty**: Controls the computation difficulty (recommending `4` or `5`).
* **Cookie Expiration**: How long the verification remains valid after passing (e.g., `3600` seconds).
* **Custom Challenge HTML**: Customize the Loading page style of the challenge to match your business design.
#### 4. Block Response (Block Response)
* **Description**: Define the behavior of the WAF when blocking malicious requests.
* **Configuration**:
* **Block Status Code**: Customize the HTTP status code returned, e.g., the standard `403` or a fun `418 (I'm a teapot)`.
* **Block Response Body**: Input custom HTML content shown to blocked attackers (e.g., "WAF Interception: Your request has been logged").
---
### Step 3: Associate the Rule Group with Websites
Once configured, the rule group does not automatically take effect; you need to bind it to specific website configurations.
* **Option A (Recommended)**: In the **"Bind Websites"** Tab of the rule group details, select the websites you wish to apply this rule group to and save.
* **Option B**: Return to **"Website Configuration"**, edit a specific website, and check and bind the rule group in the "Security Protection" section.
> [!NOTE]
> If a rule group is marked as **"Global Rule Group (is_global)"**, it applies to **all websites** hosted on the gateway automatically, requiring no manual binding.
---
### Step 4: Publish & Activate Configurations
1. If you modify **rule group definitions**, **GeoIP scopes**, **PoW CC difficulties**, or **website-to-rule-group bindings**:
* Click **"Preview Config"** -> **"Publish & Activate"** in the top right corner.
* Once the Agent pulls and validates the new version, it rewrites local core OpenResty config files (`waf_config.json`, etc.) and gracefully reloads the processes to apply the policies.
2. If you only update **IP group members** (e.g., adding/deleting an IP in a manual IP group, or an automatic IP group aggregates a new set of blocked IPs periodically):
* **No publication or activation is required!**
* The Server calculates the new MD5 Checksum of the IP group immediately after updating the database.
* The control plane **broadcasts the modified IP group members in real-time to all online Agents via WebSocket**. The Agent overwrites the runtime local disk file `waf_ip_groups.json` incrementally.
* The OpenResty Lua engine calculates the file hash in microseconds when processing new requests. If it detects a Checksum change, it reloads it into the memory dictionary (`ngx.shared`) in real-time. **This entire process requires absolutely no Nginx service reloads, having zero impact on online high-concurrency operations**.
* Even if the WebSocket connection drops, the Agent reports its local Checksum in every heartbeat cycle, and the Server syncs the differential updates to guarantee synchronization.
---
## WAF Evaluation Flow (Filtering Funnel)
When an external request reaches the OpenResty data plane, the WAF runtime evaluates it in the `access` phase according to the funnel decision chain below. Once a match is made, evaluation terminates:
```text
Request enters access phase
│
v
Get all active rule groups bound to this site (Global + Bound Custom groups)
│
v
1. Matches IP whitelist / Whitelist IP group? ──────(Yes)─────► [ Allow (ALLOW) ]
│ (No)
v
2. Matches country / province whitelist? ────────(Yes)─────► [ Allow (ALLOW) ]
│ (No)
v
3. Matches IP blacklist / Blacklist IP group? ──────(Yes)─────► [ Block (BLOCK) ] ──► Return status & HTML block page
│ (No)
v
4. Matches country / province blacklist? ────────(Yes)─────► [ Block (BLOCK) ] ──► Return status & HTML block page
│ (No)
v
5. Is PoW CC protection enabled for this site?
├───(Yes)───► [ Validate PoW Cookie ] ──(Passed)──► [ Allow (ALLOW) ]
│ │
│ (Not Passed)
│ v
│ [ Render PoW Challenge ] ──(Solved)──► Set Cookie & Allow
v
6. No rules triggered, legitimate traffic ─────────────────────► [ Allow (ALLOW) ]
```
---
## Best Practices & Tuning Recommendations
* **Whitelist Precedence & Protection**: Before deploying strict blacklists or regional blocks, we strongly recommend creating a "Trusted IP Group" containing your team's office egress IPs, local development IPs, and third-party callback server IPs (e.g., WeChat or Alipay payment callback addresses), and prioritizing it in the rule group's **whitelist**. This effectively prevents accidental blockages.
* **Reasonably Fine-tune PoW Difficulty**: Human-machine CC challenge hash difficulty (`challenge_difficulty`) is a double-edged sword:
* Difficulty `3`: Computes almost instantly, providing low protection.
* Difficulty `4`: Normal phones/low-end browsers solve it in 100-300ms, providing good protection.
* Difficulty `5`: Requires 500ms-2s, providing strong protection but low-end client browsers might perceive slight loading delays.
* Difficulty `6` and above: Computes exponentially slower, easily freezing client browser CPUs. **We strongly recommend choosing `4` or `5` in production**.
* **Utilize "Test Rule"**: For automatic IP groups, always click **"Test Rule"** before saving. By inspecting the list of matching IPs in the current window, verify if your Expr expressions thresholds (such as request counts, 404 ratios, etc.) are too broad or too strict, preventing accidental blockages of legitimate users.
* **Isolate Static & Dynamic Blacklists**: Never enter static malicious IPs that require permanent blocks directly into automatic IP groups (since the aggregated list will be overwritten in the next cron cycle). You should add permanent malicious IPs into a dedicated "Manual Blacklist IP Group" and reference both the manual and automatic groups in your rule groups.
Architecture, graph validation, and failure rollback details: [WAF Orchestration Rule Design](../design/waf-orchestration-design.md).