mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 07:06:36 +08:00
docs(i18n): 恢复并补齐英文版 vitepress,README 默认改为英文
- README 默认英文:README.en.md → README.md(英文为默认),中文移至 README.zh-CN.md,语言切换链接同步 - 恢复被删除的 docs/en/ 英文文档(git 历史 cc5e53c5^),删除 4 篇已废弃文件 - 英文导航 config.ts 对齐中文结构(新增 Deployment/Changelog 侧栏,同步 Guide/Design 条目) - 翻译 15 篇中文新增文档:guide 5 篇(certificates/pages-usage/proxy-config/uptime-kuma/zone-domain-migration)+ design 10 篇(zone-design/cloudflare-pointing/waf-orchestration/origin-error-page/edge-cache-design/pages-design/logstore/kuma-design/login-captcha/observability 三篇) - en 首页更新(新增 Pages 特性、tagline 同步);changelog 英文入口指向中文版 - vitepress 构建验证:43 个英文页面全部渲染 注意:29 篇旧英文文档为恢复版,部分内容(如 deployment/server、reference/configuration)可能落后于中文,需后续逐篇同步
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# TLS Certificates and Auto-Renewal
|
||||
|
||||
This guide explains how to manage TLS certificates in OpenFlare. To secure traffic with HTTPS, you need to configure the corresponding certificate. OpenFlare supports **manually importing existing certificates** and **automatic issuance and managed renewal via ACME**.
|
||||
|
||||
---
|
||||
|
||||
## Method 1: Manually Import an Existing Certificate
|
||||
|
||||
If you have obtained a free or paid certificate from a third-party provider (such as Tencent Cloud, Alibaba Cloud, etc.), or generated a self-signed certificate locally:
|
||||
|
||||
1. Log in to the admin panel, go to **「Website Management」->「TLS Certificates」** in the left navigation.
|
||||
2. Click **「Import Certificate」** in the top-right corner.
|
||||
3. Fill in the configuration:
|
||||
* **Certificate Name**: Enter an easily recognizable alias (e.g. `my-domain-cert`).
|
||||
* **Certificate Content (PEM)**: Paste the PEM-format certificate public key (usually starts with `-----BEGIN CERTIFICATE-----`).
|
||||
* **Private Key (KEY)**: Paste the certificate private key (usually starts with `-----BEGIN PRIVATE KEY-----` or `-----BEGIN RSA PRIVATE KEY-----`).
|
||||
4. Click **「Save」**. After a successful import, the certificate can be directly bound when configuring domains.
|
||||
|
||||
---
|
||||
|
||||
## Method 2: Automatic Issuance and Auto-Renewal (ACME)
|
||||
|
||||
OpenFlare has a built-in ACME client integrated with the **Asynq async task queue**. With the DNS API of your cloud DNS provider, the system can automatically complete DNS-01 challenge validation, apply for wildcard/single-domain certificates from a CA (Let's Encrypt by default), and **automatically trigger renewal 7 days before expiry**.
|
||||
|
||||
### Step 1: Create a DNS API Token in Cloudflare
|
||||
|
||||
To let OpenFlare automatically add TXT records under your domain for DNS validation, you need a Cloudflare API Token with specific permissions.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> For security, **it is strongly recommended to use a permission-scoped API Token** rather than the Global API Key.
|
||||
|
||||
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/).
|
||||
2. Click the user avatar in the top-right corner and select **「My Profile」**.
|
||||
3. In the left menu select **「API Tokens」**, then click **「Create Token」**.
|
||||
4. Find the **「Edit Zone DNS」** template and click **「Use template」**.
|
||||
5. Configure the token permissions and scope (keep defaults or restrict as needed):
|
||||
* **Permissions**:
|
||||
* `Zone` - `DNS` - `Edit` (required, for ACME to write TXT records)
|
||||
* `Zone` - `Zone` - `Read` (required, to list and retrieve zone IDs)
|
||||
* **Zone Resources**:
|
||||
* Select **「Include」** -> **「All zones」**, or select **「Specific zone」** and point to the specific domain you manage.
|
||||
6. Click **「Continue to summary」**, confirm, then click **「Create Token」**.
|
||||
7. Copy the generated **API Token** string. It is only shown once, so save it carefully.
|
||||
|
||||
### Step 2: Add a DNS Account in the Control Plane
|
||||
|
||||
1. Log in to the OpenFlare admin panel, go to **「Website Management」->「DNS Accounts」**.
|
||||
2. Click **「Add Account」**.
|
||||
3. Fill in the configuration:
|
||||
* **Account Name**: e.g. `cloudflare-main`.
|
||||
* **DNS Provider**: Select `Cloudflare`.
|
||||
* **API Token**: Paste the API token copied from Cloudflare (stored encrypted automatically).
|
||||
4. Click **「Save」**.
|
||||
|
||||
### Step 3: Submit a Certificate Application Task
|
||||
|
||||
1. Go to **「Website Management」->「TLS Certificates」**, click **「Apply for Certificate」** in the top-right corner.
|
||||
2. Fill in the application form:
|
||||
* **Certificate Name**: Custom name (e.g. `wildcard-example-cert`).
|
||||
* **Primary Domain**: The domain to apply for (wildcards supported, e.g. `example.com` or `*.example.com`).
|
||||
* **Associated Domains**: Append more domains if any (wildcards supported, comma-separated).
|
||||
* **DNS Account**: Select the DNS account just added from the dropdown (e.g. `cloudflare-main`).
|
||||
3. Click **「Save and Apply」**.
|
||||
|
||||
### Step 4: Track Application Progress and Renewal Status
|
||||
|
||||
- **Real-time progress**: After saving, the system delivers a certificate renewal/application task (`of_ssl_single_renew`) to the Asynq queue. You can view detailed step-by-step logs (adding TXT records, DNS record global propagation probing, ACME validation, certificate issuance, etc.) in the admin task or node log pages.
|
||||
- **Automatic renewal**: All certificates issued via ACME are automatically managed by the system. The background Scheduler scans certificate validity daily and automatically triggers renewal via async tasks 7 days before expiry — no manual maintenance needed.
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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.
|
||||
|
||||
---
|
||||
|
||||
### 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/)
|
||||
|
||||
### 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)
|
||||
|
||||
---
|
||||
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
### 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).
|
||||
@@ -0,0 +1,100 @@
|
||||
# Publishing Your First Site
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Pre-publish Checks
|
||||
|
||||
Verify that the following conditions are met:
|
||||
|
||||
| Item | Expectation |
|
||||
| --- | --- |
|
||||
| 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 |
|
||||
|
||||
## Create Website Configuration
|
||||
|
||||
A new website configuration requires at least:
|
||||
|
||||
| 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 |
|
||||
|
||||
Example:
|
||||
|
||||
| Field | Example |
|
||||
| --- | --- |
|
||||
| Website Name | `app` |
|
||||
| Domain | `app.example.com` |
|
||||
| Origin Address | `http://10.0.0.20:8080` |
|
||||
|
||||
A single domain can belong to only one website configuration. Rate limiting, reverse proxy, and caching parameters are shared site-wide.
|
||||
|
||||
## Bind Certificate
|
||||
|
||||
HTTPS certificates are bound by domain. Domains without a bound certificate will not be placed into `443 ssl` server blocks automatically.
|
||||
|
||||
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.
|
||||
|
||||
## Publish & Activate
|
||||
|
||||
Standard Pipeline:
|
||||
|
||||
```text
|
||||
Modify rules -> Preview / Diff -> Publish -> Generate complete version -> Activate version -> Agent pulls -> Local application -> Report result
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Guide Overview
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Recommended Reading Path
|
||||
|
||||
If you are new to OpenFlare, read the documents in the following 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.
|
||||
|
||||
## Role-Based Entrypoints
|
||||
|
||||
| What do you want to do? | Recommended Entrance |
|
||||
| --- | --- |
|
||||
| Run the console in under 5 minutes | [Quick Start](./quick-start.md) |
|
||||
| Publish your first reverse proxy configuration | [Publish First Configuration](./first-site.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) |
|
||||
| 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) |
|
||||
| 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) |
|
||||
|
||||
## Documentation Partitions
|
||||
|
||||
`guide/` is oriented toward users and deployers, providing actionable steps from installation to daily operations.
|
||||
|
||||
`reference/` collects stable facts such as configuration fields, commands, API response structures, and repository layout.
|
||||
|
||||
`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.
|
||||
@@ -0,0 +1,116 @@
|
||||
# Pages Static Hosting Usage
|
||||
|
||||
You will learn: how to deploy pre-built static sites via local upload, Remote URL, or public GitHub Release assets; configure SPA Fallback and API reverse proxy; and safely check for updates, auto-publish, and roll back.
|
||||
|
||||
---
|
||||
|
||||
## Core Mechanics and Page Structure
|
||||
|
||||
OpenFlare Pages is inspired by Cloudflare Pages' Direct Upload and deployment history interaction, but currently handles **pre-built artifacts** rather than building from repository source. The project detail is organized as "current production deployment → deployment source → deployment history": source configuration can change, while created deployments stay immutable.
|
||||
|
||||
```text
|
||||
Local upload ─> unified validation / upload.Ingest ─> new candidate ─> admin explicit activation ─┐
|
||||
Remote URL ── Server restricted download ─────────────┐ │
|
||||
GitHub Release asset ─ Server resolves ───────────────┴─> create/load deployment ────────────────┤
|
||||
└─> source sync atomic activation ────────┘
|
||||
|
|
||||
v
|
||||
Agent pulls per-project latest
|
||||
|
|
||||
v
|
||||
OpenResty local static serving
|
||||
```
|
||||
|
||||
External URLs, GitHub metadata, and auto-checks are handled only by the Server. The Agent only pulls the currently active deployment package from the control plane; it does not receive external source credentials, nor does it run `git clone`, dependency installation, or build commands.
|
||||
|
||||
## Step 1: Create a Project
|
||||
|
||||
1. Log in to the admin panel, go to **「Pages」**, click **「Create Project」**.
|
||||
2. Fill in the project name and a unique Slug.
|
||||
3. Configure the content entry:
|
||||
* **Entry file name**: default `index.html`.
|
||||
* **Static asset root path (RootDir)**: fill in the relative path when artifacts are in a subdirectory like `dist/`; leave empty when artifacts are at the archive root.
|
||||
4. Set SPA Fallback and API proxy as needed. RootDir and entry file are project-level configs applied uniformly to all sources.
|
||||
|
||||
## Step 2: Choose a Deployment Source
|
||||
|
||||
### 1. Manual Upload
|
||||
|
||||
Without a persistent source configured, the project stays in manual mode. Click **「Upload Deployment Package」** to select a pre-built archive; a successful upload creates a candidate deployment, which you then explicitly activate from the deployment history. Re-uploading does not modify existing deployments.
|
||||
|
||||
Supported formats: `zip`, `tar.gz` / `tgz`, `tar.xz` / `txz`, `tar.bz2` / `tbz2`, `tar`, and `7z`.
|
||||
|
||||
### 2. Remote URL
|
||||
|
||||
In the deployment source card select **Remote URL**, fill in the HTTP(S) address and choose a network policy:
|
||||
|
||||
* **public**: default policy; rejects loopback, private network, link-local addresses, DNS rebinding, self-signed TLS, and redirects to non-public targets.
|
||||
* **trusted_internal**: only for explicitly trusted intranet or self-signed services; requires a second risk confirmation before saving.
|
||||
|
||||
After saving, the address is only displayed masked. You don't need to re-enter it when editing other configs; only submit a new URL when choosing to change the address. Remote sources only offer **「Sync and Publish」**: the Server downloads, validates, and atomically activates each time — no "check for updates", scheduled checks, or auto-updates.
|
||||
|
||||
### 3. GitHub Release
|
||||
|
||||
GitHub sources only support public `github.com` repositories. Fill in:
|
||||
|
||||
* A repository address in `https://github.com/{owner}/{repo}` format;
|
||||
* **Latest Release** or a **fixed Tag**;
|
||||
* An exact, case-sensitive Release Asset filename, default `dist.zip`.
|
||||
|
||||
Both options support manual **「Check for Updates」** and **「Sync and Publish」**. Differences:
|
||||
|
||||
* **latest**: supports a check interval of 5–1440 minutes, default 1440 minutes (24 hours); auto-update is off by default. When enabled, the scanner asynchronously syncs and publishes only when a new revision is found.
|
||||
* **tag**: only supports manual admin checks and sync; does not participate in the scheduled scanner.
|
||||
|
||||
"Check for updates" only resolves the Release/asset and advances the version cursor without downloading the deployment package; "Sync and publish" downloads, validates, creates or reuses a deployment, and activates it. If the asset under the same Release is replaced, the source enters **「Needs Confirmation」** — you must confirm the exact revision shown before publishing, to avoid silent overwrites.
|
||||
|
||||
GitHub Release sources only import pre-built artifacts; they do not build from repository source.
|
||||
|
||||
### 4. Switch or Delete a Source
|
||||
|
||||
You can switch between Manual, Remote, and GitHub Release. Modifying or deleting a source does not delete the current production deployment or historical deployments; switching back to manual mode lets you continue uploading and explicitly activating.
|
||||
|
||||
## Deployment Package Security Limits
|
||||
|
||||
Deployment packages must satisfy these constraints:
|
||||
|
||||
* Archive size is controlled by the system config `pages_max_package_size_mb`, default 100 MiB, configurable 1–2048 MiB.
|
||||
* Expanded single-file and total size limits are "package size limit × 4", with a floor of 100 MiB; at most 1,000 regular files.
|
||||
* The control plane streams regular file bodies, checking declared size against actual bytes, and validates the project entry file.
|
||||
* Absolute paths, `..` path traversal, symlinks, hard links, and special files in archives are all rejected.
|
||||
|
||||
The Agent also verifies SHA-256, real response byte limits, and post-extraction file count and total size on download; failures do not switch the existing `current`.
|
||||
|
||||
## Step 3: Configure Advanced Routing Rules
|
||||
|
||||
### 1. SPA Fallback
|
||||
|
||||
When using front-end routing like React Router or Vue Router, enable **「SPA Fallback」** and set the entry path (usually `/index.html`). When a visitor accesses a physical path that doesn't exist, OpenResty falls back to the entry file for the front-end router to handle.
|
||||
|
||||
### 2. API Reverse Proxy
|
||||
|
||||
Pages can forward a specified prefix to a backend API under the same domain:
|
||||
|
||||
* **APIProxyPath**: match prefix, e.g. `/api`.
|
||||
* **APIProxyPass**: backend address, e.g. `http://10.0.0.5:8080`.
|
||||
* **APIProxyRewrite**: optional path rewrite rule.
|
||||
|
||||
Requests matching the API prefix go through the reverse proxy; other requests continue to be served by the static site.
|
||||
|
||||
## Step 4: Bind a Route and First Publish
|
||||
|
||||
1. Create or edit a proxy rule.
|
||||
2. Set the origin type to **Pages** and select the Pages **project**.
|
||||
3. Preview the config, then publish and activate.
|
||||
|
||||
The route binds to a stable project ID, not a specific deployment. The first publish gives the Agent the project anchor; afterwards, local uploads, source syncs, auto-updates, or manual rollbacks only change the project's active deployment — the Agent converges via the latest hash reconciliation without needing to republish the main config.
|
||||
|
||||
## Operations, Status, and Rollback
|
||||
|
||||
* The source card shows the last check/sync time, found vs. applied revision, next check time, and security errors. While a check or sync task runs, the page polls the task status; when latest is idle, it refreshes at low frequency only near the check time.
|
||||
* A failed auto-update does not replace the old active deployment; a single source failure does not block the scanner from processing other projects.
|
||||
* Activating another deployment in the history is a manual rollback. The system fences in-flight source tasks and disables that source's auto-update to avoid the next latest round overwriting your manual choice; re-activating the current version is a no-op.
|
||||
* The Agent downloads to a temp file, verifies SHA-256, extracts safely, then atomically switches `current`. Any failure keeps the old content; with multi-project reconciliation, a single project failure does not affect others.
|
||||
|
||||
> [!TIP]
|
||||
> For the source state machine, auto scanner, upload compensation, immutable deployments, and Agent atomic switching, see [Pages Static Hosting Design](../design/pages-design.md).
|
||||
@@ -0,0 +1,117 @@
|
||||
# Create a Reverse Proxy Config
|
||||
|
||||
You will learn: how to create and publish a reverse proxy website configuration from scratch, step by step, in OpenFlare. This guide walks you through certificate import and application, origin definition, route rule configuration, version release, and connectivity verification.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Workflow
|
||||
|
||||
In the gateway control plane, follow these steps to add a new reverse proxy rule:
|
||||
|
||||
```text
|
||||
[ Step 1. Certificate Management ] ──► [ Step 2. Origin Definition (optional) ] ──► [ Step 3. Add Website Config ]
|
||||
│
|
||||
[ Step 5. Verify Access ] ◄── [ Step 4. Publish & Activate Version ] ◄───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Prepare Certificates
|
||||
|
||||
Before using HTTPS-secured traffic, you need to prepare the corresponding TLS certificate (supports manually importing an existing certificate, or automatically applying from a CA via DNS validation with managed renewal).
|
||||
|
||||
To keep this guide concise, the certificate details (including creating a dedicated DNS API Token in Cloudflare) have been split into a dedicated guide. First go to **[TLS Certificates & Auto-Renewal](./certificates.md)** to prepare the certificate, then come back to continue.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Prepare the Upstream Origin (optional)
|
||||
|
||||
An Origin represents the backend real service address being proxied. Although you can fill in an IP directly when creating a website, it is recommended to register origins in the origin library first for reuse and maintenance:
|
||||
|
||||
1. Go to **「Website Management」->「Origin Addresses」** in the left navigation, click **「Add Origin」**.
|
||||
2. Fill in the origin name (e.g. `production-api`).
|
||||
3. Enter a valid upstream address (e.g. `http://10.0.0.10:8080`) and save.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Create the Website Config
|
||||
|
||||
Once the certificate and origin are ready, create the core website proxy route:
|
||||
|
||||
1. Go to **「Website Management」->「Domain List」**, click **「Add Zone」**:
|
||||
* **Domain**: Enter the domain bound to this site.
|
||||
* **Bind Certificate**: Select the certificate prepared or applied for in Step 1.
|
||||
2. Configure the request route rule: go to the **「Rule Management」** page, click **「New Rule」** or edit an existing rule:
|
||||
* **Rule Name**: Enter a unique simple identifier (e.g. `app-portal-route`).
|
||||
* **Domain Match**: Enter the corresponding domain (wildcards or exact domains supported; must match the registered domain above).
|
||||
* In the **「Reverse Proxy」** tab below, select the origin mode as「Direct Upstream」.
|
||||
* **Origin Selection**: Select the origin created in Step 2 from the dropdown; or choose manual input and fill in `http://10.0.0.20:9000`.
|
||||
3. Click save to create the config.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Publish and Apply the Config
|
||||
|
||||
Website configs added in the admin panel are only saved in the Server database — **they do not take effect immediately**. You must generate a config version snapshot and distribute it to the Agent edge nodes:
|
||||
|
||||
1. Click the **「Preview and Publish」** button in the top-right corner of the control panel.
|
||||
2. Review the config file diff, confirming the newly added `server` block and certificate binding rules are correct.
|
||||
3. Click **「Confirm Publish」**.
|
||||
4. **Agent application mechanism**:
|
||||
* The Agent node on the data plane detects the active version Checksum change in its heartbeat, and automatically pulls the full OpenResty config files and certificate bundle locally.
|
||||
* It automatically runs a local config validation (similar to `openresty -t`); after confirming no syntax errors, it performs a smooth reload.
|
||||
* If reload or validation fails, the Agent safely blocks and rolls back to the previous stable version to keep the node highly available.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Connectivity and Rollback Verification
|
||||
|
||||
### 1. Verify Access
|
||||
You can verify the new config takes effect as follows:
|
||||
* **Browser access**: Open `https://your-domain.com` directly in a browser and check whether it proxies the backend successfully.
|
||||
* **CLI verification** (recommended): probe with `curl`:
|
||||
```bash
|
||||
curl -I https://your-domain.com
|
||||
```
|
||||
* **Bypass DNS validation**: if your domain is not yet resolvable, temporarily send a `Host` header request to the Agent node's physical IP:
|
||||
```bash
|
||||
curl -I -H "Host: your-domain.com" https://AGENT_NODE_IP --insecure
|
||||
```
|
||||
|
||||
### 2. One-Click Second-Level Rollback
|
||||
If the released config causes an online business issue:
|
||||
1. Navigate to the **「Version Release」** menu on the left.
|
||||
2. Find the previous stable version before the release in the history list.
|
||||
3. Click **「Activate」**.
|
||||
4. All online Agent nodes will automatically reload the historical config within seconds for second-level risk avoidance.
|
||||
|
||||
---
|
||||
|
||||
## Edge Cache (optional)
|
||||
|
||||
The **「Cache」** page in the site details can enable edge `proxy_cache` (requires **Performance Settings → Global OpenResty Cache** to be enabled at the same time). Behavior mirrors the Cloudflare default model; see [Edge Cache Strategy Design](../design/edge-cache-design.md).
|
||||
|
||||
### Recommended Settings
|
||||
|
||||
| Item | Suggestion |
|
||||
| --- | --- |
|
||||
| Strategy | **Standard static assets** (recommended default): only css/js/map/images/fonts, **not HTML/JSON** |
|
||||
| Login Cookie | Is **not** separately skipped from caching; users with sessions can still hit static assets |
|
||||
| Origin | Static assets: `Cache-Control: public, max-age=…`; dynamic/personalized must be `private` or `no-store` |
|
||||
| Response Set-Cookie | Is not written to the edge cache |
|
||||
| No origin cache headers | Uses default Edge TTL by status code (e.g. ~120 min for 200) |
|
||||
|
||||
### Advanced Strategy「All Cacheable GET」
|
||||
|
||||
Similar to Cloudflare Cache Everything: the path is no longer limited by extension. If the origin does not declare `private`/`no-store` for HTML, **personalized pages may be cached and served across users**. Use only when origin cache headers are correct or content is globally consistent.
|
||||
|
||||
### How It Takes Effect
|
||||
|
||||
The cache switch and strategy are written into the config snapshot. After saving the site, you must **publish and activate the config version** for the Agent to apply it. Changing the UI only without publishing leaves nodes on the old rules.
|
||||
|
||||
### Quick Self-Check
|
||||
|
||||
1. Global cache on, site cache on, strategy「Standard static assets」.
|
||||
2. Publish the config and confirm nodes applied successfully.
|
||||
3. Request the same `/assets/app.js` (or a hashed immutable path) twice with a login cookie; the `cache_status` in access logs should be **HIT** on the second request.
|
||||
4. If still「not cached」: check whether the strategy matches the path extension, whether it is a non-GET request, whether the origin returns `Set-Cookie` / `private`, and whether the node applied the new version. More in [Troubleshooting · Edge Cache](./troubleshooting.md#edge-cache-hit-rate-anomalies).
|
||||
@@ -0,0 +1,219 @@
|
||||
# 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.
|
||||
|
||||
The minimum running unit of OpenFlare 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. |
|
||||
|
||||
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.
|
||||
|
||||
## 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**: `20.10.0+`
|
||||
* **Docker Compose**: `2.0.0+`
|
||||
|
||||
## 1. Start the Server
|
||||
|
||||
Create a `docker-compose.yml` file in an empty directory:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: openflare
|
||||
POSTGRES_USER: openflare
|
||||
POSTGRES_PASSWORD: replace-with-strong-password
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U openflare -d openflare"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
openflare:
|
||||
image: ghcr.io/rain-kl/openflare:latest
|
||||
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
|
||||
volumes:
|
||||
- openflare-data:/data
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
openflare-data:
|
||||
```
|
||||
|
||||
Start the services:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Verify that 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:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
Default credentials:
|
||||
|
||||
| Username | Password |
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
Please change the default password immediately after your first login.
|
||||
|
||||
## 2. Prepare Agent Token
|
||||
|
||||
The Agent can be connected using one of two types of credentials:
|
||||
|
||||
| Credential | Applicable Scenario |
|
||||
| --- | --- |
|
||||
| `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 |
|
||||
|
||||
After preparing one of these credentials in the management console, proceed to the next step.
|
||||
|
||||
* **`discovery_token`** path: "System Settings" -> "Auto Registration"
|
||||
* **`agent_token`** path: "Node Management" -> "Add Node"
|
||||
|
||||
## 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.
|
||||
|
||||
### Option A: Run Agent in Docker (Recommended)
|
||||
|
||||
Run the Agent image directly on the proxy node:
|
||||
|
||||
```bash
|
||||
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 \
|
||||
-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)
|
||||
|
||||
Execute the installation script on the proxy node.
|
||||
|
||||
Using the `discovery_token`:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
|
||||
--server-url http://your-server:3000 \
|
||||
--discovery-token YOUR_DISCOVERY_TOKEN
|
||||
```
|
||||
|
||||
Using the node-specific `agent_token`:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash -s -- \
|
||||
--server-url http://your-server:3000 \
|
||||
--agent-token YOUR_AGENT_TOKEN
|
||||
```
|
||||
|
||||
The script defaults to:
|
||||
|
||||
| Item | Default Value |
|
||||
| --- | --- |
|
||||
| Install Directory | `/opt/openflare-agent` |
|
||||
| Config File | `/opt/openflare-agent/agent.json` |
|
||||
| systemd Service | `openflare-agent.service` |
|
||||
| OpenResty Path | Automatically detects `openresty` if unspecified |
|
||||
|
||||
Verify the Agent service status:
|
||||
|
||||
```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.
|
||||
|
||||
## 4. Publish Your First Configuration
|
||||
|
||||
Perform the following operations in the management console:
|
||||
|
||||
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.
|
||||
|
||||
The version number format is `YYYYMMDD-NNN`. Historic versions are immutable; rollbacks are accomplished by re-activating an older version.
|
||||
|
||||
## 5. Verify Success
|
||||
|
||||
Confirm in the management console:
|
||||
|
||||
| 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 |
|
||||
|
||||
Confirm on the Agent node:
|
||||
|
||||
```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).
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||
* **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.
|
||||
@@ -0,0 +1,106 @@
|
||||
# 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.
|
||||
|
||||
OpenFlare supports third-party logins configured via Authentication Sources. Currently, GitHub OAuth and standard OIDC Providers (e.g., Logto, authentik, Keycloak, Casdoor) are supported.
|
||||
|
||||
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.
|
||||
|
||||
## 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` |
|
||||
|
||||
**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.
|
||||
|
||||
## Callback URL
|
||||
|
||||
The Redirect URI / Callback URL in third-party platforms is formatted as:
|
||||
|
||||
```text
|
||||
<OpenFlare URL>/oauth/<Auth Source Name>
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
https://openflare.example.com/oauth/github
|
||||
https://openflare.example.com/oauth/company-oidc
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
Once enabled, the corresponding OIDC login button will display on the login page.
|
||||
|
||||
## Login & Binding Behaviors
|
||||
|
||||
Once a third-party account returns to OpenFlare, it is processed according to the following rules:
|
||||
|
||||
| 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 |
|
||||
|
||||
If you want only existing users to use SSO, you can disable user registration. Unbound third-party accounts will then trigger the binding flow.
|
||||
|
||||
## Modify Authentication 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.
|
||||
|
||||
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.
|
||||
|
||||
## Common Problems
|
||||
|
||||
### 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.
|
||||
|
||||
### Callback Address 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.
|
||||
|
||||
### Third-party Login Button Not Showing on Login Page
|
||||
|
||||
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.
|
||||
|
||||
### Client Secret Saved but Not Displayed in Clear Text
|
||||
|
||||
This is expected behavior. OpenFlare does not echo the Client Secret back via API, displaying only whether the secret is configured.
|
||||
@@ -0,0 +1,249 @@
|
||||
# Troubleshooting
|
||||
|
||||
You will learn: How to troubleshoot OpenFlare Server, database, login, Agent, OpenResty, configuration publishing, and frontend build issues by symptoms.
|
||||
|
||||
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.
|
||||
|
||||
## Quick Diagnostic
|
||||
|
||||
| Symptom | Where to check 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 |
|
||||
|
||||
## Server Fails to Start
|
||||
|
||||
1. View logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -n 200 openflare
|
||||
```
|
||||
|
||||
For source-code execution, inspect terminal outputs.
|
||||
|
||||
2. Check port conflicts:
|
||||
|
||||
```bash
|
||||
lsof -i :3000
|
||||
```
|
||||
|
||||
3. If using PostgreSQL, verify that the database is healthy:
|
||||
|
||||
```bash
|
||||
docker compose ps postgres
|
||||
docker compose logs -n 100 postgres
|
||||
```
|
||||
|
||||
4. If using SQLite, verify that the database directory is writable:
|
||||
|
||||
```bash
|
||||
ls -ld "$(dirname /path/to/openflare.db)"
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
| Log or Symptom | Action |
|
||||
| --- | --- |
|
||||
| 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 |
|
||||
|
||||
## Admin Console Fails to Load or Shows Blank Page
|
||||
|
||||
1. Verify that 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:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
3. Verify if the browser URL matches your reverse proxy domain.
|
||||
|
||||
4. If accessing via the frontend dev server, verify the backend proxy configuration:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
curl -I http://your-server:3000
|
||||
```
|
||||
|
||||
Inspect Agent logs:
|
||||
|
||||
```bash
|
||||
journalctl -u openflare-agent -n 200 --no-pager
|
||||
```
|
||||
|
||||
Verify configuration parameters:
|
||||
|
||||
```bash
|
||||
sed -n '1,160p' /opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
Key Settings:
|
||||
|
||||
| Configuration | 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 |
|
||||
|
||||
If the log warns that the Token is invalid, retrieve a new Token in the management console, update `agent.json`, and restart the Agent:
|
||||
|
||||
```bash
|
||||
systemctl restart openflare-agent
|
||||
```
|
||||
|
||||
## Node Fails to Apply New Version after Publishing
|
||||
|
||||
Verify in sequence:
|
||||
|
||||
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.
|
||||
|
||||
Inspect 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.
|
||||
|
||||
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.
|
||||
|
||||
## OpenResty Application Fails
|
||||
|
||||
Common Causes:
|
||||
|
||||
| Cause | Diagnostic |
|
||||
| --- | --- |
|
||||
| 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 |
|
||||
|
||||
OpenResty Configuration Validation:
|
||||
|
||||
```bash
|
||||
openresty -t -c /path/to/openflare/data/etc/nginx/nginx.conf
|
||||
```
|
||||
|
||||
OpenResty Runtime Status:
|
||||
|
||||
```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.
|
||||
|
||||
Actual binary paths and main configuration paths are governed by `openresty_path` and `main_config_path` in `agent.json`.
|
||||
|
||||
## HTTPS Fails to Work
|
||||
|
||||
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`:
|
||||
|
||||
```bash
|
||||
curl -Iv https://your-domain
|
||||
```
|
||||
|
||||
Domains without a bound certificate will not be added to the HTTPS configuration automatically; this is expected behavior.
|
||||
|
||||
## Traffic Analytics Has No Data
|
||||
|
||||
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.
|
||||
|
||||
## Frontend Build Fails
|
||||
|
||||
Execute:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
| Symptom | Action |
|
||||
| --- | --- |
|
||||
| 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 |
|
||||
|
||||
## Documentation Build Fails
|
||||
|
||||
```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.
|
||||
@@ -0,0 +1,174 @@
|
||||
# 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Before using the intranet penetration features, you need to familiarize yourself with the following components and core concepts:
|
||||
|
||||
| Concept | Description | Component / Operation |
|
||||
| --- | --- | --- |
|
||||
| **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 |
|
||||
|
||||
---
|
||||
|
||||
## Recommended Operation Sequence
|
||||
|
||||
To publish an intranet service to the public internet, we recommend doing so in the following 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`).
|
||||
6. Publish and activate the new version.
|
||||
7. Access via the public domain to verify that the intranet penetration link is established.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Configuration Steps
|
||||
|
||||
### Step 1: Prepare the Relay Node (Relay)
|
||||
|
||||
Intranet traffic is routed through public relay nodes. Before starting, ensure you have a public relay server available.
|
||||
|
||||
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:
|
||||
|
||||
```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> \
|
||||
-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.
|
||||
|
||||
### Step 2: Create a Penetration Tunnel in the Management Console
|
||||
|
||||
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.
|
||||
|
||||
### Step 3: Deploy the Intranet Client (OpenFlared)
|
||||
|
||||
Return to your intranet server and execute the copied deployment command to run the client.
|
||||
|
||||
#### Option A: Deploy with Docker (Highly Recommended)
|
||||
|
||||
The official `openflared` image embeds the master daemon and `frpc` runtime, working out-of-the-box with 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> \
|
||||
-v openflared-data:/app/data \
|
||||
ghcr.io/rain-kl/openflared:latest
|
||||
```
|
||||
|
||||
#### Option B: Host Binary Manual Execution
|
||||
|
||||
If you cannot use Docker, you can download or compile the `flared` binary:
|
||||
|
||||
1. Create a `flared.json` configuration file in the same directory as the executable on your intranet machine:
|
||||
```json
|
||||
{
|
||||
"server_url": "http://<YOUR_SERVER_PUBLIC_IP>:3000",
|
||||
"tunnel_token": "<YOUR_COPIED_TUNNEL_TOKEN>",
|
||||
"frpc_path": "/usr/local/bin/frpc",
|
||||
"data_dir": "./data"
|
||||
}
|
||||
```
|
||||
2. Execute the startup command:
|
||||
```bash
|
||||
./flared -config ./flared.json
|
||||
```
|
||||
|
||||
#### Verify Online Status
|
||||
|
||||
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.
|
||||
|
||||
### Step 4: Create a Website and Bind the Tunnel Upstream
|
||||
|
||||
Now you can configure public reverse proxy and domain routing 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.
|
||||
|
||||
### Step 5: Publish & Activate
|
||||
|
||||
To allow the gateway's OpenResty instance to match and route domain traffic correctly, we need to publish a new configuration 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!
|
||||
|
||||
---
|
||||
|
||||
## Advanced Application Scenarios
|
||||
|
||||
### 1. Single-Tunnel Multi-Service Multiplexing (Multi-Port Mapping)
|
||||
|
||||
You do not need to deploy an `openflared` container for every single internal 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.
|
||||
|
||||
### 2. Seamless Integration with Gateway Security Features
|
||||
|
||||
Since all public traffic enters the public Agent node first, completing the HTTPS/TLS handshake and WAF filtering before traveling through the secure tunnel:
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
### 1. Tunnel Shows as "Offline" in the Management Console
|
||||
|
||||
* **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.
|
||||
|
||||
### 2. Accessing the 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.
|
||||
|
||||
### 3. Multiple Relays Network Instability 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.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Uptime Kuma Monitoring Sync
|
||||
|
||||
You will learn: how to enable and configure the Uptime Kuma auto-sync integration, control the sync scope and heartbeat probe parameters for monitored sites, and the underlying principles of differential synchronization between OpenFlare and Uptime Kuma.
|
||||
|
||||
---
|
||||
|
||||
## Feature Overview
|
||||
|
||||
In edge multi-node operations, knowing the availability of each proxied site in time is critical. To avoid manually re-entering site information into a monitoring system, OpenFlare provides deep integration with the open-source monitoring service **Uptime Kuma**.
|
||||
|
||||
Once enabled, OpenFlare starts a background sync scheduler that automatically syncs the proxy sites configured in the admin panel as HTTP monitor tasks in Uptime Kuma. It supports scope filtering, differential attribute updates, and automatic cleanup of decommissioned sites.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Configure the Integration in System Settings
|
||||
|
||||
1. Log in to the admin panel, go to **「System Settings」** in the left navigation, select the **「OpenFlare」** tab, and configure the **「Uptime Kuma Integration」** section.
|
||||
2. Configure the following core connection parameters:
|
||||
* **Enabled**: Turn on the integration switch.
|
||||
* **Instance URL**: Your Uptime Kuma service address, e.g. `http://192.168.1.100:3001` or `https://kuma.example.com` (the protocol prefix `http://` or `https://` is required).
|
||||
* **Username** and **Password**: Credentials for a Uptime Kuma account with admin privileges, used for API authentication.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Control Monitor Scope and Heartbeat Parameters
|
||||
|
||||
In the integration panel you can finely control the monitor scope and probe behavior:
|
||||
|
||||
### 1. Monitor Scope
|
||||
* **All sites**: Default option. OpenFlare automatically syncs all **enabled** proxy route sites. When a new site is created and enabled, or an old site is disabled, the monitor list is updated automatically.
|
||||
* **Selected sites**: Only monitor specified sites. After selecting this mode, click the **「Select Monitored Sites」** dialog. Inside the dialog you can filter sites by search and check the ones you want. Sites that are unchecked or not checked will not be synced (and will be automatically cleaned up if they already exist).
|
||||
|
||||
### 2. Probe Frequency and Heartbeat Settings
|
||||
You can specify uniform probe parameters for auto-generated monitors:
|
||||
* **Sync Interval**: Frequency (minutes) of automatic differential sync, default `5` minutes. The control plane compares state with Uptime Kuma every 5 minutes.
|
||||
* **Heartbeat Interval**: Frequency (seconds) at which Uptime Kuma probes sites, default `60` seconds.
|
||||
* **Retry**: Maximum number of retries before a failed probe is judged Down, default `0`.
|
||||
* **Retry Interval**: Seconds to wait between retries, default `60` seconds.
|
||||
* **Request Timeout**: Seconds after which a probe request is judged timed out, default `48` seconds.
|
||||
|
||||
---
|
||||
|
||||
## Sync and Cleanup Mechanism
|
||||
|
||||
* **Dedicated tag isolation**: All auto-created monitors are bound with the `OpenFlare`-specific tag (purple-blue). The sync and cleanup routines only operate on monitors with this tag, and will not interfere with or damage other monitors you created manually in Uptime Kuma.
|
||||
* **Differential incremental sync**: The sync routine periodically compares monitor metadata. When a domain or heartbeat configuration change is detected, only a differential update is performed to avoid interrupting historical statistics; when a site is disabled or moved out of scope, it is automatically taken offline and cleaned up.
|
||||
|
||||
> [!TIP]
|
||||
> For details on the Socket.IO control flow, anti-pollution tag model, and differential comparison algorithm of Uptime Kuma monitoring sync, see [Uptime Kuma Sync Design](../design/kuma-design.md).
|
||||
@@ -0,0 +1,161 @@
|
||||
# WAF Auto IP Group Expressions
|
||||
|
||||
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.
|
||||
|
||||
## Configuration Structure
|
||||
|
||||
The configuration of an automatic IP group is a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "Single IP High-Frequency 404 Scanning",
|
||||
"expr": "request_count > 100 && status_404_ratio >= 0.8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Field Descriptions:
|
||||
|
||||
| Field | Type | Role |
|
||||
| --- | --- | --- |
|
||||
| `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. |
|
||||
|
||||
## Evaluation Mechanics
|
||||
|
||||
Automatic rules do not evaluate logs request-by-request, but instead aggregate them by client IP first:
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
## Available Metrics
|
||||
|
||||
The following metrics are directly available in Expr expressions:
|
||||
|
||||
| Keyword | Type | Role |
|
||||
| --- | --- | --- |
|
||||
| `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. |
|
||||
|
||||
All ratio fields are decimals between `0` and `1`. An 80% ratio should be written as `0.8`, and 50% as `0.5`.
|
||||
|
||||
## Common Expr Syntax
|
||||
|
||||
Automatic IP groups use the Expr syntax. The expression must return a boolean value.
|
||||
|
||||
Common Operators:
|
||||
|
||||
| Operator | Role | 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` |
|
||||
|
||||
## Built-in Presets
|
||||
|
||||
The management console provides two built-in preset rules that can be added directly and adjusted as needed:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Single IP High-Frequency 404 Scanning",
|
||||
"expr": "request_count > 100 && status_404_ratio >= 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%.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Single IP Direct IP Access Mismatch",
|
||||
"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.
|
||||
|
||||
## Examples
|
||||
|
||||
High-frequency 404 scanning:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "High-Frequency 404 Scanning",
|
||||
"expr": "request_count > 100 && status_404_ratio >= 0.8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Direct IP access mismatch:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 30,
|
||||
"rules": [
|
||||
{
|
||||
"name": "Direct IP Access Mismatch",
|
||||
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Capture both high 4xx and 5xx errors:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 120,
|
||||
"rules": [
|
||||
{
|
||||
"name": "Abnormal Error Rates",
|
||||
"expr": "(client_error_count > 80 && request_count > 100) || server_error_count > 30"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Exclude trusted IPs:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Recommendations
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,162 @@
|
||||
# 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.
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Before configuring security policies, you need to understand the core components of the WAF:
|
||||
|
||||
| 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**. |
|
||||
|
||||
---
|
||||
|
||||
## Recommended Configuration Sequence
|
||||
|
||||
When configuring security protections for your websites, we recommend doing so in the following order:
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Step Guide
|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Zone Domain Migration and Release Acceptance
|
||||
|
||||
When migrating from the legacy `managed_domains` / inline domain columns of reverse proxy routes to the Zone + Zone Domain model, data import and table structure upgrades are both completed by the **automatic goose migration at Server startup** — no separate import command is needed.
|
||||
|
||||
## What Happens During Upgrade
|
||||
|
||||
When starting (or rolling-upgrading) a Server version that includes the Zone rework, **no manual command is required**; `migrator.Migrate()` automatically:
|
||||
|
||||
1. Applies goose SQL: creates `of_zones` / `of_zone_domains` (if they do not yet exist).
|
||||
2. **Automatically imports** the legacy route domain columns (and `of_managed_domains` when routes have no domains) as Zone / Zone Domains, binding `proxy_route_id` / `cert_id` (registering root domains via public suffix list parsing).
|
||||
3. Continues goose SQL: drops the redundant domain/certificate columns from `of_managed_domains` and `of_proxy_routes`.
|
||||
|
||||
The import is idempotent: existing domains are skipped or have their route binding back-filled.
|
||||
|
||||
**If historical data cannot be parsed (conflicting domains, invalid root domains, missing certificates, etc.), startup fails.** Fix the data or restore a backup and start again to retry.
|
||||
|
||||
## Recommended Actions
|
||||
|
||||
### 1. Back Up Before Upgrading
|
||||
|
||||
```bash
|
||||
# PostgreSQL example
|
||||
pg_dump "$DATABASE_URL" > openflare-pre-zone-$(date +%Y%m%d).sql
|
||||
|
||||
# Or copy the backup volume / snapshot; for SQLite, copy the database file in the data directory
|
||||
```
|
||||
|
||||
Optional: note down the current **active config version number** and checksum in the admin panel for config rollback comparison.
|
||||
|
||||
### 2. Upgrade and Start the Server
|
||||
|
||||
Deploy the new version and start it. Watch the goose success messages in the startup log; if "Zone migration failed (N conflicts)" appears, fix the source data according to the conflicts listed in the log and restart.
|
||||
|
||||
### 3. Post-Upgrade Checks
|
||||
|
||||
1. Admin panel **Websites** `/websites`: check whether Zone root domains and domain counts are reasonable.
|
||||
2. Zone details: domains, certificates, associated route IDs.
|
||||
3. **Reverse proxy routes**: domain bindings come from Zone Domains, not legacy hand-written fields.
|
||||
|
||||
### 4. Config Preview and Release
|
||||
|
||||
1. Review the config diff / preview in the admin panel.
|
||||
2. Verify **per route**: `server_name` set, certificate paths, WAF Route ID, Pages references.
|
||||
3. **Allow** the redundant `domain` / `domains` / `cert_ids` on routes in old snapshot JSON to disappear.
|
||||
4. **Do not allow** data-plane semantic changes.
|
||||
5. After the preview passes, release it; if needed, activate the pre-upgrade version in config versions for config rollback. For database rollback, use the pre-upgrade backup (down migrations do not backfill business domain data).
|
||||
|
||||
## Related Docs
|
||||
|
||||
* [Zone & Domain Resource Design](../design/zone-design.md)
|
||||
* [Create a Reverse Proxy Config](./proxy-config.md)
|
||||
* [Publish First Configuration](./first-site.md)
|
||||
Reference in New Issue
Block a user